Linux基础知识

Linux

Linux系统目录结构

  在Linux或者Unix系统中,所有文件和目录都被组织成以一个根节点开始的树状结构,文件系统的最顶层是由根目录开始\,根目录下既可以是文件也可以是目录。如果一个目录或者文件是以.开头的,则表示这个文件或目录是隐藏的。系统目录结构如下所示:

目录具体含义:

  • /bin  Binaries(二进制文件)的缩写,存放着最经常使用的命令
  • /boot  用于存放启动Linux时所需要的一些核心文件,包括一些链接文件以及镜像文件
  • /dev  Devices的缩写,用于存放Linux的外部设备,在Linux中设备也是以文件的形式进行表示的,访问设备的方式与访问文件的方式类似。
  • /etc  Etcetera(等等)的缩写,用来存放所有系统管理所需要的配置文件和子目录
  • /home  用户的主目录,在Linux中,每个用户都有一个自己的目录,一般是以用户的账号进行命名的,如上途中的alice,bob等
  • /lib  Library(库)的缩写,这个目录存放着系统最基本的动态链接共享库,几乎所有的应用程序都会用到这些共享库
  • /lost+found  这个目录一般是空的,当系统非法关闭后,会暂时保存一些文件
  • /media  Linux系统会自动识别一些设备(u盘,光驱),识别到后会将这些设备自动挂载此目录下
  • /mnt  系统提供该目录是为了让用户临时挂载别的文件系统,例如光驱或者u盘
  • /opt  存放其他需要额外安装的软件
  • /proc  Process(进程)的缩写,/proc是一种伪文件系统(即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟目录,可以通过直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上,而是在内存里面。可以通过直接修改里面的某些文件,来实现对系统的设置,例如,可以向特定的文件写入内容,来实现屏蔽主机的ping命令:
    echo 1 /proc/sys/net/ipv4/icmp_echo_ignore_all,向这个文件写入1,就可实现屏蔽ping命令,使得其他主机无法ping到当前主机。
  • /root  为系统管理员的用户目录
  • /selinux  这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,这个目录存放selinux相关的文件。
  • /sys 这是Linux2.6内核的一个重大变化,该目录下安装了2.6内核中新出现的一个文件系统sysfs,sysfs文件系统集成了以下三种文件系统的信息:
    1. 针对进程信息的proc文件系统
    2. 针对设备的devfs文件系统
    3. 针对伪终端的devpts文件系统
  • /tmp  Temporary的缩写,用来存放一些临时文件,系统重启后,里面的内容可能被删除,用户也可配置不被删除
  • /usr  Unix Shared Resources, 共享资源目录,用户的很多应用程序和文件都存放在这个目录下,相当于Windows的Program Files目录
  • /usr/bin  系统用户使用的应用程序
  • /usr/src  内核源代码默认的放置目录
  • /var  Variable缩写,这个目录存放着不断扩充的东西,通常会将经常被修改的目录或者文件存在这里,例如日至文件
  • /run  是一个临时文件系统,存储系统启动以来的信息,当系统重启的时候,这个目录下的文件应该被删掉或清除。

软链接与硬链接

  Linux文件系统中,数据分为元数据和用户数据,用户数据,即文件数据块(data block)是记录文件真实内容的地方。元数据是文件的附加属性,记录了如文件大小,创建时间,属主,inode(索引节点,为一个结构体)等信息。在Linux文件系统中,文件名并不是文件的唯一标识,元数据中的inode号才是文件的唯一标识。为解决文件的共享使用问题,Linux引入了硬链接和软链接的概念,通过链接,隐藏了源文件的路径,提升了权限安全以及节省了内存。
  文件名只是为了方便区分和记忆,在Linux文件系统中,系统和程序是通过inode号来寻找正确的文件数据块,通过文件名获取文件内容的过程如下:

   filename-->inode-->data block

  在Linux中,可通过ls -i filename来查看文件的inode号,可以通过stat filename查看文件的inode信息

硬链接

  若一个inode号对应多个文件名,则称这些文件为硬链接,硬链接是指一个文件使用了多个别名,但是这些文件都具有相同的inode号。

特点:

  • 所有文件具有相同的inode号以及data block
  • 只能对已经存在的文件创建硬链接
  • 不能对目录创建硬链接
  • 不能交叉文件系统创建硬链接(因为inode号只在各文件系统下是唯一的,当Linux系统下挂载了多个文件系统后,可能出现inode号重复)
  • 删除一个硬链接并不影响其他的硬链接

  可以使用命令df -i --print-type来查看当前系统中挂载的文件系统类型,各文件系统使用情况以及文件系统挂载点等信息。

软链接

若文件的用户数据快中存放的是另一个文件的路径指向,则该文件就称为软链接,因此软链接本质上就是一个文件,只是文件的内容比较特殊,软链接有自己的inode号以及用户数据,因此软链接的创建和使用限制较少。

特点:

  • 软链接有自己的文件权限及属性
  • 可对不存在的文件或者目录创建软链接
  • 软链接可交叉文件系统进行创建
  • 删除软链接并不影响被指向的文件。若被指向的文件被删除,则相关的软链接称为死链接(dangling link),若被指向的文件被重建,则死链接可恢复为正常的链接
链接的创建
ln -s filename softlink      # 建立软链接
ln filename hadrlink         # 建立硬链接

cp -s filename softlink      # 建立软链接
cp -l filename hardlink      # 建立硬链接

文件存储-关于inode补充
  文件存储在硬盘上,硬盘的最小存储单位叫做扇区(sector),每个扇区存储512个字节,操作系统在读取硬盘的时候,不会一个扇区一个扇区的读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个块(Block),这种由多个扇区组成的块,是文件存取的最小单位,块的大小,最常见的是4kB,即8个连续的扇区组成一个快。文件数据都存储在块中,那么,还必须找到一个地方存储文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
  inode包含文件的元信息,具体来说有如下内容:

  1. 文件的字节数
  2. 文件拥有者的user id
  3. 文件的group id
  4. 文件的 r w x 权限
  5. 文件的时间戳,ctime表示inode上一次变动的时间,mtime表示文件内容上一次的变动时间,atime表示文件上一次打开的时间
  6. 文件数据Block的位置

  inode也会占用硬盘的空间,因此在硬盘格式化的时候,操作系统会自动将硬盘分为两个区域,一个是数据区,用于存放所有的文件数据;另一个是inode区(inode table),用于存放inode所包含的信息。每个inode的大小为128或者256个字节,inode节点的总数,在格式化的时候就已经给定,一般是1kB或者2kB就设置一个inode。假定在一块1G的硬盘中,每个inode节点的大小为128字节,每1KB设置一个inode,那么inode table的大小就会达到128M,占据整块硬盘容量的12.8%,可以使用df -i查看每个硬盘下的inode总数以及使用情况(可以看到每个硬盘下的inode总数,以使用的inode数量,未使用的inode数量等信息)

每个文件都必须有一个inode,因此可能会发生inode已经用光,但是硬盘还未满的情况,此时也是无法进行文件创建的

目录文件:

在Linux系统中,目录也是一种文件,打开目录,实际上就是打开目录文件,目录文件的结构较为简单,其内容是由一些列目录项构成的目录列表,每个目录项由两部分构成,该目录下文件的文件名,以及文件名所对应的inode号。由ls -i dir可查看特定目录下的所有目录项。

目录文件的链接数:

创建文件的时候,默认生成两个目录项...,.相当于当前目录文件的硬链接,而..相当于当前目录父目录的硬链接,因此,任何一个目录的硬链接总数,等于2加上它的子目录总数,这里的2是父目录对其的“硬链接”和当前目录下.对其的硬链接

Linux文件类型

Linux常见的文件类型有七种:

表示符号 文件类型 补充
- 常规文件 即file
d 目录文件 目录
b 块设备文件 Block Device 文件,如硬盘,支持以Block为单位进行随机访问
c 字符设备文件 Character Device 文件,如键盘支持以字符为单位进行线性访问
l 符号链接文件 Symbol Link 软链接文件
p 管道文件 pipe文件
s 套接字文件 socket文件

对于普通文件,ls -l会显示文件的大小,但是在设备类文件中,会显示两个用逗号隔开的数字,这两个数字不表示文件的大小。第一个数字表示主设备号,第二个数字表示次设备号,主设备号用于区分设备类型,用于决定加载设备的驱动程序。次设备号用于区分同一类型的不同设备,如硬盘1,硬盘2

文件基本属性

Linux 环境变量配置方法记录

Linux读取配置的环境变量的方法:

export    # 显示当前系统定义的所有环境变量

echo $PATH     # 输出当前的PATH环境变量值

Linux下环境变量可分为用户自定义的环境变量以及系统级别的环境变量:

  • 用户自定义环境变量: 所在文件 ~/.bashrc 或者~/.profile,部分系统~/.bash_profile
  • 系统级别的环境变量: 所在文件/etc/bashrc 或者 /etc/profile 或者 /etc/bash_profile,部分系统为/etc/environment

环境变量配置方法:

  1. 直接使用export命令在终端修改
   export /home/mysql/bin:$PATH
   export $PATH:/home/mysql/bin

   生效时间 :立即生效
   生效期限 : 仅在当前终端内有效,关闭当前终端后自动失效
   生效范围 : 仅限于当前用户有效

  1. 修改文件 ~/.bashrc 或者~/.profile,部分系统~/.bash_profile
   vim ~/.bashrc
   # 设置环境变量
   export /home/mysql/bin:$PATH

   生效时间 :在相同用户下打开新终端的时候生效,或者执行source ~/.bashrc时生效
   生效期限 : 永久生效
   生效范围 : 仅限于当前用户有效

  1. 修改文件/etc/bashrc 或者 /etc/profile 或者 /etc/bash_profile,部分系统为/etc/environment
   vim /etc/bashrc
    # 设置环境变量
   export /home/mysql/bin:$PATH

   生效时间 :打开新终端的时候生效,或者执行source /etc/bashrc时生效
   生效期限 : 永久生效
   生效范围 : 对所有用户有效

Tips
可在在自己的项目中自定义需要的环境变量文件,例如myProject.profile,在此文件中可以使用export定义一系列环境变量,然后在 ~/.profile文件中加入source myProject.profile,即可将项目中自定义的环境变量引入当前用户终端下使用。

还可以通过alias定义一些命令的别名,例如alias cdpro="cd /home/test/project"

Linux用户权限与用户组管理

  Linux是多用户,多任务操作系统,可支持多个用户在同一时间内同时登陆执行不同的任务,且互不影响。如果需要使用Linux的资源,就必须向管理员申请一个用户,通过这个用户进入系统。系统管理员可以赋予每个用户不同的权限,实现合理的控制与利用系统资源,提升系统的安全性。

用户组: 用户组是具有相同权限的用户的集合,利用用户组,可方便的实现对用户的批量管理

1. 用户和用户组的常用命令

useradd命令用于创建用户账号和用户目录,需要使用超级用户(uid=0)进行创建

Linux下用户的分类:

  • 超级管理员  uid为0,拥有极大的权限
  • 系统用户   uid为1-499,一般不会被登入
  • 普通用户   uid为500-65534,权限会受到基本的限制,也会受到超级管理员的限制

uid为65534的特殊账号,nobody,这个账号的权限会受到进一步的限制,一般作为来宾账户使用

1.1 Linux uid 和 gid

  Linux中,每个用户的id可以分为两种,分别是uid (user id) 和 gid (group id),Linux中用户名只是用于方便记忆而已,实际系统识别的是用户的id号,在Linux下的/etc/passwd文件中,存放着所有用户和其id的对应关系。

在Linux中,每个文件的拥有者(属主)拥有两个属性,可通过如下命令进行查看:

ls -i test.txt

可查看到文件的拥有者的用户名,以及文件拥有者所属的组名;但是对于Linux来说并不认识这些用户名和组名,而只能识别 uid 和 gid,Linux会通过/etc/passwd文件和/etc/group文件,查找到uid 和 gid 对应的用户名和组名,并将其显示出来。

可通过如下的指令查看Linux下用户和组的信息,以用户root和组root

cat /etc/passwd | grep root     # 用户信息
cat /etc/group  | grep root     # 组信息
1.2 useradd命令

参数含义:

  • -c  用于指定用户的描述信息
  • -u  创建新用户账号时,指定用户的uid(不要小于500)
  • -d  指定用户的主目录,需要绝对路径,且需要设置目录权限
  • -g  指定用户的初始组
  • -s  指定用户的登陆shell,默认为bin/bash
  • -e  指定用户的失效日期,格式为YYYY-MM-DD,也就是/etc/shadow文件的第八个字段。
  • -r  创建系统用户,也即uid在1~49之间的用户,系统用户是供系统程序使用的用户,系统用户主要用于运行系统所需服务的权限配置,因此不会为其创建用户主目录。

Linux下创建一个用户时,实际会进行以下操作:

  1. /etc/passwd文件中创建一行与用户名相关的数据
  2. /etc/shadow文件中创建一行与用户密码相关的数据
  3. /etc/group文件中创建一行与用户名一样的群组
  4. /etc/gshadow文件中创建一行与新增群组相关的密码信息
  5. /etc/skel目录中的配置文件复制到新用户的主目录中

上述文件的作用:

  1. /etc/passwd文件
    此文件是用户配置文件,存储了系统中所有用户的基本信息,并且所有用户均可对此文件进行操作。每个用户在文件中都有一条记录,包含了用户的一些基本属性。文件内容如下:

可以看到文件的每一行内容都是固定且有规律可循的,每一行文本代表一个用户的相关信息,可以看到在文件中,大部分都是系统用户信息,这些系统用户是系统和服务正常运行所必须的用户,系统用户也称为伪用户,因为这些用户不能用来登录,但是也不能删除,如果删除,依赖这些用户的服务或者应用就无法正常运行,导致系统问题。上述每行的用户信息,分为7个字段,以":"进行分隔,每个字段的含义如下:

字段 含义
字段1 用户名
字段2 密码 x表示用户设有密码,但不是真正的密码
真正的密码保存在/etc/shadow文文件中且只有管理员能够访问
字段3 UID 可通过命令id -u获取当前用户的uid
字段4 GID 用户的初始组id
初始组: 在创建用户的时候,会创建和用户名同名的一个用户组,并将这个用户组作为创建的用户的初始组
附加组: 除过用户组之外,用户还可以加入其他的组,且拥有这些组的权限,这些组称为用户的附加组
字段5 描述性信息 对用户进行描述的信息,此外无其他作用
字段6 用户主目录 指用户登录后有操作权限的目录,例如root用户的主目录为/root
普通用户的目录为 /home/username
字段7 默认shell shell相当于Linux命令的解释器,使用户和系统内核进行沟通的桥梁,将用户输入的指令转换为系统可以识别的机器语言, 默认为解释器为bash,其他的还有sh,csh等
  1. /etc/shadow

  Linux中的shadow文件用于存储用户的密码信息,又被称为影子文件、因为Linux下的/etc/passwd文件可以被所有用户访问,因此为了避免用户的密码安全,Linux单独将用户密码从/etc/passwd文件中分离出来,存储在/etc/shadow 文件中,/etc/shadow 文件只有root用户具备访问权限。

使用命令cat /etc/shadow命令查看影子文件内容:

img

文件中的每一行代表一条用户密码信息,通过":"进行分割,可分为9个字段:

字段 含义
字段1 用户名
字段2 加密密码 目前采用的SHA512散列解密算法
字段3 最后一次修改密码的时间,以1970年1月1日起开始算天数
字段4 最小修改间隔 表示从最后一次修改密码的时间算起,多长时间之内不能修改密码
字段5 密码有效期
字段6 密码需要变更前的警告天数,提示用户密码还有多长时间就要过期了
字段7 密码过期后的宽限时间,宽限时间内,用户还可以正常登录系统,如果超期,则用户被禁用
字段8 账号失效时间
字段9 保留字段
  1. /etc/group

  表示用户组配置文件,系统中所有用户组的信息都保存在此文件中。GID和组名的对应关系就记录在此文件中。可使用如下的命令查看文件的内容:

cat /etc/group

可以看到文件中每行的内容,可分为四个字段,如下表所示:

字段 含义
字段1 组名
字段2 组密码
字段3 GID
字段4 组内用户列表

组密码字段的x仅仅是密码标识,真正加密后的密码默认保存在/etc/gshadow文件中。用户密码用于验证用户身份,而用户组密码主要用于指定组管理员。除了root用户之外,每个组可以拥有组管理员,可以替代root用户向组中添加或者删除用户。

  1. /etc/gshadow

  用户存储Linux系统中组密码信息,可以使用如下命令查看:

cat /etc/gshadow

文件中每一行有4个字段,分别为:

字段 含义
字段1 组名
字段2 加密密码
字段3 组管理员
字段4 组附加用户列表
1.3 passwd命令

  Linux中passwd命令用于修改用户的密码,在使用useradd命令创建新用户的时候,并没有设定用户密码,因此还无法用来登录系统。必须要通过passwd命令来设置密码。

参数含义:

  • -d  username  删除用户密码
  • -l  username  停止账号使用
  • -S  username  查看用户密码状态
  • -u  username  启用已被停止的账户
  • -i  username  设置用户密码失效日期
passwd  # 设置当前用户密码

passwd username  # 设置用户username的密码
1.4 usermod命令

  Linux中usermod命令用于修改用户信息,也可用于修改用户账号的各项设定:usermod option username

参数含义:

  • -c   修改用户账号的备注文字
  • -d   修改用户主目录
  • -e   修改账号的有效期限
  • -g   修改用户的初始去群组
  • -G   修改用户的附加群组
  • -l   修改用户账号名称
  • -s   修改用户登入后使用的shell
  • -u   修改用户id
  • -L   锁定用户密码,使用户密码无效
  • -U   接触密码锁定

使用实例:

# 修改用户主目录
usermod -d /home/dir/ username

# 修改用户名
usermod -l newname username
1.5 chage命令

chage命令用于用户密码时效管理

参数含义:

  • -l   列出用户的详细密码状态
  • -d YYYY-MM-DD   修改/etc/shadow文件中第3个字段,也即最后一次修改密码的日期
  • -m n   修改/etc/shadow文件中第4个字段,也即密码最短的保留天数
  • -M n   修改/etc/shadow文件中第5个字段,也即密码的有效期
  • -W n   修改/etc/shadow文件中第6个字段,也即密码到期前的警告天数
  • -i n   修改/etc/shadow文件中第7个字段,也即密码过期后的宽限天数
  • -E   修改/etc/shadow文件中第8个字段,也即账号失效日期

使用实例(查看用户的密码时效信息):

chmod -l username
1.6 userdel命令

userdel命令用于删除用户userdel [options] username

参数含义:

  • -r  删除用户的同时,删除用户目录等相关目录
userdel username      # 删除用户,默认不删除用户目录
userdel -r username   # 删除用户,同时删除用户目录
1.7 id指令

id指令用于查看用户的uid, gid, 以及附加组信息: id [options] username

参数含义:

  • -a   显示用户的所有组
  • -g   显示用户所属初始群组id
  • -G   显示用户所属附加组id
  • -u   显示用户id
1.9 groupadd指令

增加一个新的用户组 group add 选项 用户组

参数含义:

  • -g  指定新用户组的标识号(GID)
  • -o  一般与-g参数配合使用,表示新建组的GID可以与已有的GID相同

groupadd grp1     # 新增用户组grp1, 其GID在现有GID基础上加1
groupadd -g 1001 grp2 # 新增用户组grp2, 指定GID为1001
2.0 groupdel指令

删除指定的用户组:groupdel grp

2.1 groupmod指令

groupmod指令用于修改用户组的属性

参数含义:

  • -g  为用户组指定新的标识号
  • -o  为用户组指定新的标识号且可以与已有标识号相同
  • -n  为用户组指定新的组名
groupmod -g 102 grp1  # 将组grp1的GID修改为102

groupmod -n grp2 grp1  # 将用户组grp1的组名修改为grp2

tips:

如果用户属于多个用户组,则用户可以在不同用户组之间进行切换,以便获得其他组的权限:

newgrp root

将当前用户切换到root用户,前提是当前用户属于root用户组。

2.2 su指令

su指令用于切换用户

whoami 命令用于显示当前的用户信息,相当于id -un

Linux磁盘管理

Linux下常用的磁盘管理工具有df, du, fdisk

  • df   列出文件系统的整体磁盘使用量(disk free)
  • du   检查磁盘空间使用量(disk used)
  • fdisk   用于磁盘分区
1. df命令 (disk free)

 列出文件系统的整体磁盘使用量,该命令可以获取到磁盘被占用了多少,目前还有多少可用空间。

常用参数:

  • -a  列出所有的文件系统,包括特殊的文件系统/proc
  • -k  以KBytes为单位显示文件系统的使用情况
  • -m  以MBytes为单位显示文件系统的使用情况
  • -h  会自动进行换算,以较为容易的单位显示文件系统的使用情况(KBytes,MBytes,GBytes)
  • -i  以inodes的数量为单位显示文件系统的使用情况(df -i --print-type)
  • -T  显示文件系统的类型
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           389M  1.9M  388M   1% /run
/dev/sda5        34G   15G   18G  46% /
tmpfs           1.9G   25M  1.9G   2% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/loop0       56M   56M     0 100% /snap/core18/2679
/dev/loop1      128K  128K     0 100% /snap/bare/5
/dev/loop2       56M   56M     0 100% /snap/core18/2697
2. du命令 (disk used)

 du命令主要是对文件和目录磁盘的使用空间进行查看,而df命令主要是对文件系统磁盘使用情况进行查看。

参数含义:

  • -a  列出所有的文件与目录容量,因为默认只统计目录底下的文件
  • -k  以KBytes为单位显示文件和目录容量
  • -m  以MBytes为单位显示文件和目录容量
  • -h  会自动进行换算,以较为容易的单位显示容量的使用情况(KBytes,MBytes,GBytes)
  • -s  仅仅列出总量,不列出目录和文件所占的容量
  • -S  仅仅列出目录所占容量,不列出文件
 du -ah ./*       # 当前目录下的所有目录和问价所占的容量
 du -sh ./        # 当前目录所占的容量
 du -Sh ./*       # 当前目录下所有目录所占的容量
3. fdisk命令

 fdisk时Linux下磁盘分区表操作命令

Linux中磁盘分区的概念:

参数含义:

  • -l : 列出系统中特定装置或者所有装置的分区内容
 fdisk -l              # 列出所有装置的分区内容
 fdisk -l /dev/sda     # 列出sda装置的分区内容

img

Linux下通过GDB扩展定义宏,查看QString类型数据的值

  在Linux下调试代码的时候,GDB的print指令只能查看基本的数据类型的值,对于Qt代码中的QString类型,则无法查看到其值;GDB为用户提供了扩展功能,可以使用户自定义宏命令,来查看负责的数据类型.

需要在目录/etc/gdb/或者etc/下找到gdbinit文件,可能会存在一个gdbinit.d的目录。一般看到的写法有两种:

  • 将需要定义的宏命令代码直接写到gdbinit文件中
  • 将需要定义的宏命令单独定义文件,写入代码后放到gdbinit.d目录下,然后在gdbinit中写入脚本代码,遍历gdbinit.d目录下的文件,执行gdb.execute(source filename);

两种方式均可,如果GDB已经启动,可在GDB中直接source宏命令的脚本文件,定义的宏命令即可生效:
(gdb) source printqstr

脚本内容:

define printqstring
    printf "(QString)0x%x (length=%i): \"",&$arg0,$arg0.d->size
    set $i=0
    while $i < $arg0.d->size
        set $c=$arg0.d->data[$i++]
        if $c < 32 || $c > 127
                printf "\\u0x%04x", $c
        else
                printf "%c", (char)$c
        end
    end
    printf "\"\n"
end

如果出现"cannot resolve overloaded method `xxx': no arguments supplied"的错误,就需要打开具体的数据类型定义看一下,例如QString的定义,与上述的宏命令定义脚本进行对比,看是否各种成员调用正确

这里提供QString的定义,与上述脚本对比:

class Q_CORE_EXPORT QString
{
public:
    typedef QStringData Data;
    // ..... TODO
    Data *d;
    // ..... TODO
};

typedef QTypedArrayData<ushort> QStringData;

template <class T>
struct QTypedArrayData
    : QArrayData
{
    // ..... TODO
    T *data() { return static_cast<T *>(QArrayData::data()); }
    const T *data() const { return static_cast<const T *>(QArrayData::data()); }
    // ..... TODO
};

struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;

    qptrdiff offset; // in bytes from beginning of header

    void *data()
    {
        Q_ASSERT(size == 0
            || offset < 0 || size_t(offset) >= sizeof(QArrayData));
        return reinterpret_cast<char *>(this) + offset;
    }

    const void *data() const
    {
        Q_ASSERT(size == 0
            || offset < 0 || size_t(offset) >= sizeof(QArrayData));
        return reinterpret_cast<const char *>(this) + offset;
    }
    // ..... TODO
};

此外,可以在gdb中使用正常的print + toStdString来实现查看QString的值,但是输出的内容不是很友好。

可以参看gdb对c++容器的支持,通过自定义宏命令查看容器的内容: c++标准容器宏命令定义

例子:写一个简单的GDB宏命令,在调试的时候打印自定义数据结构的内容:

定义如下简单的c++代码main.cpp:

#include <stdio.h>
#include <unistd.h>

// 定义结构体
struct myDataStruct
{
   const int size = 10;
   char *data;
   float para;
};

void printSt(struct myDataStruct* pSt);

int main(int argc, char* argv[])
{
   struct myDataStruct st;

   st.data = "Nxdoen";
   st.para = 12.5f;

   printSt(&st);

   return 0;
}

void printSt(struct myDataStruct* pSt)
{
   if (!pSt)
       return;

   float pa = pSt->para;
   char* content = pSt->data;

   printf("Para is %f\n", pa);
   printf("content is %s\n", content);
} 

定义GDB宏命令,在调试过程中,对结构体myDataStruct中的内容进行查看,并打印出来,可在etc/gdb/gdbinit文件中添加如下定义:

# System-wide GDB initialization file.

#
# print struct myDataStruct
#

define printSt
    if $argc == 0
        help printSt
    else
        printf "(myDataStruct)0x%x:\n", &$arg0
        printf "size = %d.\n", $arg0.size
        printf "para = %f.\n", $arg0.para
        printf "content is: " 
        p($arg0.data)   
        printf "\n"
    end
end

document printSt
    The command is self defined and used to output the content of the struct.
    Note: this command can only accept one para
    Example:
            printSt variableName
end

编译上述的main.cpp,进入debug模式,运行结果如下:

img

可参考文章

Linux Vim使用技巧

1.显示行号

临时显示行号 set number 或者set nu
取消显示行号 set nonu

永久显示行号,在/etc/vimrc文件中做如下修改:

set nu
2.文本查找

待补充

Linux coredump文件生成相关设置

 Core Dump称为核心转储,它是进程在运行过程中奔溃那一刻的一个内存快照,操作系统会在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程在此刻的内存,寄存器状态,堆栈信息的内容转储在一个文件中,即生成的coredump文件。该文件是一个二进制文件,可以使用GDB,objdump等工具分析里面的内容;

1. 产生的可能原因:
  • 内存访问越界
  • 使用线程不安全的函数
  • 多线程读写数据未加锁
  • 堆栈溢出
2. coredump文件大小查看与设置

查看和设置coredump文件的大小:

ulimit -c  # 返回0(不生成coredump文件), unlimited,或者固定大小

ulimit -a # 查看所有资源限制

 # 设置coredump文件的大小
ulimit -c filesize  #(unlimited表示大小不受限制)

img

如果需要永久生效,则需要在~/.bashrc文件中添加:

ulimit -c unlimited

/etc/profile文件中也可修改生效

3. coredump文件生成路径

查看和设置coredump文件的生成路径:

 # 查询文件生成路径
cat /proc/sys/kernel/core_pattern

Ubantu上默认的生成路径是如下:

|/usr/share/apport/apport %p %s %c %P

“|”表示执行后面的命令,apport是Ubantu的bug反馈工具,因此,在ubantu下,默认如果发生段错误,处理机制是将其作为一个bug,进行bug检查,如果是bug就进行上报。在这种情况下,是无法使用GDB来进行调试的。

所以需要修改core_pattern的值:

 # 设置coredump文件的路径 
sysctl -w kernel.core_pattern=/tmp/coredump/core-%e.%p

上述是指定core文件的路径以及命名的方式,还可以通过以下的方式添加文件命名的字段:

  • %% 单个%字符
  • %p 所dump进程的进程ID
  • %u 所dump进程的实际用户ID
  • %g 所dump进程的实际组ID
  • %s 导致本次core dump的信号
  • %t core dump的时间 (由1970年1月1日计起的秒数)
  • %h 主机名
  • %e 程序文件名

kernel.core_pattern属于运行时的内核参数,用户可通过sysctl命令来查看和修改系统内核参数,这些内核参数位于/proc/sys目录下.

sysctl -a   # 显示所有的内核参数

sysctl -p file  # 从指定文件加载系统参数,默认从/etc/sysctl.conf中加载参数

sysctl -w name=var  # 修改系统参数的值,但是这种只能是临时修改,如果系统重启,设置值会丢失

 # 如果需要永久生效,需要修改/etc/sysctl.conf文件

在修改/etc/sysctl.conf文件后,如果重启系统,通过sysctl kernal.core_attern查看设置路径,发现又回到了之前的默认值,并不是/etc/sysctl.conf文件中设置的值。这与某些Linux的core生成机制有关,例如Ubantu系统下的apport.service服务,是官方为了自动收集错误而设置的。这个服务会导致core_pattern的设置不能一直有效,只要这个服务存在,系统重新启动后就会把core_pattern改为一个特定的值|/usr/share/apport/apport %p %s %c %P,直接导致在etc/sysctl.conf文件中设置的coredump文件路径无效,总是加载不到内核。

解决办法:经用apport服务,在/etc/default/apport文件中,将enabled的值设置为0即可。

# set this to 0 to disable apport, or to 1 to enable it
# you can temporarily override this with
# sudo service apport start force_start=1
enabled=0

修改完之后,如果不想重启,可直接执行sysctl -p即可,系统直接从/etc/sysctl.conf加载对应的内核参数设置值。

tips:
Linxu下的/tmp目录在重启后,默认会将用户新建的文件以及文件夹全部删除。因此下通常会将一些使用的临时文件放到这个目录,即使重启删除也不会影响 (并非所有版本的Linux都会自动清除/tmp目录,视具体系统而定)。

如果需要修改此默认行为,需要修改/usr/lib/tmpfiles.d/tmp.conf文件:

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# See tmpfiles.d(5) for details

# Clear tmp directories separately, to make them easier to override
v /tmp 1777 root root 10d           #   清理/tmp下10天前的目录和文件
v /var/tmp 1777 root root 30d       #   清理/var/tmp下30天前的目录和文件

# Exclude namespace mountpoints created with PrivateTmp=yes
x /tmp/systemd-private-%b-*
X /tmp/systemd-private-%b-*/tmp
x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp

Linux机器如何修改IP地址

开源许可证

如果不明白各种开源许可证具体包含哪些含义,可参考下图

Linux下USB相关设备

1. Linux下如何挂载U盘

  Windows下插入U盘,系统会自动为U盘指定一个盘符,此时就可以直接操作U盘中的文件了;Linux下没有盘符的概念,因此,在Linux下,需要将U盘手动挂载在某个特定的目录下才能进行操作;

Linux下挂载的概念:

Linux系统中一切皆文件,对于外部设备(如USB设备),Linux系统也将其视作文件,因此在U盘设备插入后,系统会将其视作一个虚拟的目录,用户需要手动将这个虚拟的目录映射到Linux系统中实际的目录(挂载点)上,才能通过挂载点去访问USB设备;这个步骤就叫做挂载

外部设备必须挂载在根目录/mnt下目录上才能进行访问,为了方便区分,一般会在目录/mnt下新建不同的目录,用来挂载不同的USB设备。

# 第一步 查看设备
sudo fdisk -l      // 查看当前系统下的设备,一般需要root权限
sudo lsblk -l      // 也可进行查看usb设备
# Linux下需要U盘是FAT32模式

# 新建挂载目录
mkdir -p /mnt/myusb

# 挂载U盘
sudo mount /dev/sad1 /mnt/myusb

# 取消挂载 相当于windwos的弹出设备
sudo umount /mnt/myusb

# 删除挂载目录
rmdir -p /mnt/myusb

取消挂载的时候如果提示(target is busy)还有进程占在访问挂载点,可查看访问的进程,将进程杀掉:

sudo lsof /mnt/myusb
kill -9 pid
2. Linux下如何查看USB设备以及配置设备权限

  udev是Linux下的设备管理工具,在udev之前,Linux还采用了其他的工具(例如devfs)来管理设备;udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件;与之前的devfs不同,udev试运行在用户空间(user space),而不是运行在内核空间(kernel space);

udev的工作流程

udev下的udev.conf文件:

配置文件含义:

  • udev_log: 日志等级,可配置为err(3), info(6), debug(7)
  • children_max: 表示允许同时执行的最大设备数量
  • exec_delay: 表示延迟多少秒之后再执行规则文件中的run指令
  • event_timeout: 等待设备事件(uevent)完成的超时秒数,超时后,设备事件将被终止,默认180s
  • timeout_signal: 这个参数表示到达event_timeout超时的时候,发出的信号,可配置为timeout_signal=SIGKILL
  • resolve_names: 设置systemd-udevd 在何时解析用户与组的名称。默认值 early 表示在规则的解析阶段;显示late时表示在每个设备事件发生的时候;显示never时表示不解析(所有设备都归 root 用户拥有)。
  • udev_rules=/etc/udev/rules.d,指定规则文件的目录

规则文件:

  规则文件位于目录/etc/udev/rules.d目录下,以.rules作为后缀名,目录下文件如下所示:

img

规则文件前面的数字表示优先级,数字越小,表示优先级越高

规则文件的内容如下所示(网络图片):

img

  • SUBSYSTEM 表示的是系统,这里需要实现硬盘的自动挂载,硬盘属于块设备,因此SUBSYSTEM设置为block;
  • ACTION表示uevent事件的行为,add表示添加,remove表示移除,change表示更改
  • ENV表示环境变量,最多可配置五条(干啥的?)
  • RUN表示所执行的内容,可以为脚本,或者命令行

整个文件的意思表示,当一个块设备(硬盘)插入,拔掉,或者变更的时候,都会去执行对应的/etc/udev/scripts/mount.sh脚本;可在这个脚本中,对插入的块设备的类型进行相应的判断,进行相应的处理流程;

例如规则文件还可以配置为:

SUBSYSTEMS=="usb",ATTRS{idVendor}=="096e",ATTRS{idProduct}=="0209",MODE="0666"
SUBSYSTEMS=="usb",ATTRS{idVendor}=="096e",ATTRS{idProduct}=="020a",MODE="0666"

这个是配置usb设备的规则文件;

其中idVendor和idProduct的值,可通过命令sudo lsusb命令来进行查看:

img

  • OWNER: 设置设备文件所属的用户
  • GROUP: 设置设备文件所属的用户组
  • MODE: 设置设备文件的权限:

规则文件还可以配置为:

KERNEL=="sda", NAME="my_root_disk", MODE="0660"

表示:如果一个设备的内核名称为sda,则这条规则文件生效,在/dev/目录下生成一个名字为NAME的设备文件,且将设备文件的权限设定为MODE所指定的文件

udev规则文件的所有操作符:

"==" :比较键、值,若等于,则该条件满足;
"!=" :比较键、值,若不等于,则该条件满足;
"=":  对一个键赋值;
"+=": 为一个表示多个条目的键赋值;
":=": 对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值。

例如上述的KERNEL=="sda"

udev规则匹配键包含一下:

ACTION:事件(uevent)的行为,例如:add(添加设备)、remove(删除设备), change(更新);
KERNEL:内核设备名称,例如:sda,cdrom;
DEVPATH:设备的 devpath 路径;
SUBSYSTEM:设备的子系统名称,例如:sda 的系统为 block;
BUS:设备在 devpath 里的总线名称,例如:usb;
DRIVER:设备在 devpath 的设备驱动名称,例如:ide-cdrom;
ID:设备在 devpath 里的识别号;
SYSFS{filename}:设备的 devpath 路径下,设备的属性文件 "filename" 里的内容;
ENV{key}:环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键; PROGRAM:调用外部命令; 
RESULT:外部命令 PROGRAM 的返回结果。

例如U盘自动挂载的规则编写:

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"

usb块设备插入的时候,执行RUN中的命令,实现自动挂载

在编写规则文件的时候,也可以引用下面的替换操作符

$kernel, %k:        设备的内核设备名称,例如:sda、cdrom。
$number, %n:         设备的内核号码,例如:sda3 的内核号码是 3。
$devpath, %p:        设备的 devpath路径。
$id, %b:          设备在 devpath里的 ID 号。
$sysfs{file}, %s{file}:  设备的 sysfs里 file 的内容。其实就是设备的属性值。
$env{key}, %E{key}:     一个环境变量的值。
$major, %M:         设备的 major 号。
$minor %m:          设备的 minor 号。
$result, %c:        PROGRAM 返回的结果。
$parent, %P:          父设备的设备文件名。
$root, %r:          udev_root的值,默认是 /dev/。
$tempnode, %N:        临时设备名。
%%:              符号 % 本身。
$$:             符号 $ 本身。

可通过命令udevadm info --attribute-walk --name=/dev/bus/usb/001/025来查看设备的信息

在修改完规则文件后,如果不想重启电脑,可通过如下的命令使其生效:

sudo udevadm control --reload
posted @ 2023-01-09 14:19  Alpha205  阅读(166)  评论(0编辑  收藏  举报