Linux专栏主要系统介绍了在Linux的开发和应用过程中所需要的各种基础知识和相关命令,共分为七部分。
1. Linux | 系统状态查看 / 文本文件处理命令_菜鸟的人工智能之路的博客-CSDN博客
2. Linux | 正则表达式和相关概念_菜鸟的人工智能之路的博客-CSDN博客
3. Linux | 文件比较 / vi编辑与使用 / 文件通配符_菜鸟的人工智能之路的博客-CSDN博客
4. Linux | 文件管理 / 目录管理_菜鸟的人工智能之路的博客-CSDN博客
5. Linux | 命令风格 / 文件系统_菜鸟的人工智能之路的博客-CSDN博客
文章目录
1 命令获取信息的方法
Linux系统命令和用户程序
- 从操作系统看,在地位上相同,都属于用户态程序;
- 运行时需要获取的信息包括配置信息、处理方式(选项参数)、被处理的对象。
1.1 运行时获取信息的常见方式
易变性从小到大为
- 配置文件
- 环境变量
- 命令行参数
- 交互式键盘输入
运行时获取信息的常见方式
- 一般为较复杂的程序会提供配置文件以存储配置信息或者偏好配置信息。
- 分为系统级偏好设置和用户级偏好设置,例如 bash 的/etc/profile 和 ~/.bash_profile。
(1)配置文件
- 配置文件提供了灵活性(同一个程序文件因用户不同,读取的配置文件不同而表现不同),变更这些信息不太方便;
- 一般不需要变化的配置信息或选项信息存入配置文件,持久化存储。
(2)环境变量
- 命令 env 可以打印出当前的环境变量;
- 一般是一些与“环境”相关的配置或选项信息,信息量不大。
- 选择在一段时间内反复使用同一个命令或者不同命令时该环境变量保持不变。
- 环境变量:LANG(语言选择),HOME(主目录),TERM(终端类型),PATH(可执行文件的查找路径),CLASSPATH(类库查找路径),CVSROOT。
- 虽然运行的程序(可执行文件)是完全相同的一个文件,程序通过获得环境变量感知环境的不同,控制自己的行为。
- 环境变量值得获取与设置:C语言有库函数getenv(),用户设置环境变量的方法也很简单。
- 性能问题:比读取配置文件需要的系统开支要小。
(3)命令行参数
- 程序启动之前指定:通过命令行参数,操作员输入命令时提供处理选项和操作对象。
- 每个命令都不同,命令运行完之后,对后续命令无影响。
(4)交互式键盘输入
- 该方式在Linux命令中极少使用;
- 计算机启动后通过计算机与操作员之间的人机交互获取信息,C语言的scanf(),fget()函数。
1.2 命令行参数的三种风格
(1)类似 dd 命令的风格
特点:命令行参数采用 param=value 的风格
- dd if=sysdisk.img of=/dev/sdb
用dd命令将硬盘映像拷贝到硬盘:if指定输入文件,of指定输出文件。
- dd if=/dev/urandom of=test.dat bs=1024 count=512
用dd命令,生成512KB测试数据文件test.dat。
命令行参数中:if, of, bs, count分别指定输入文件,输出文件,块大小,以及块计数。
(2)类似find和gcc的风格
特点:以减号开头的一个由多个字符构成的单词用作选项。
例如:find src -name '*.c' -type f -exec dos2unix --keepdate {} \; # 将带有扩展名.c的普通文件由windows文本格式转为linux格式。
(3)类似ls和grep的风格:目前流行的格式
特点:长选项与短选项,有的选项同时有两种格式,也有的选项仅有长格式或仅有短格式。
例如:ls(其中 -w 选项指定一个整数参数值告知 ls 排版时屏幕的列宽度)
- ls --classify --all --size --human-readable --width=80 /home/li # 长选项
- ls -Fashw80 /home/li # 多个选项挤在一起
- ls -F -a -s -h -w 80 /home/li # 多个选项分开
- ls -F -w80 /home/li -has # 把多个选项放在后面
2 文件系统
2.1 文件系统的创建于安装
根文件系统(root filesystem):是整个文件系统的基础,不能“脱卸(umount)”。
子文件系统:包括硬盘,软盘,USB盘等。以根文件系统中某一子目录的身份出现。
mksf和mount 文件系统的创建于安装
创建文件系统
- mkfs /dev/sdb # 块设备文件/dev/sdb上创建文件系统。
- mount /dev/sdb /mnt # /mnt可以是一个事先建好的空目录名,允许处于根文件系统的任何目录中,此后,操作子目录/mnt就是对子系统的访问。对于应用程序来说,从所操作的文件或目录名,看不出和其它根文件系统的对象有什么区别。/dev/sdb为磁盘文件。
- 不带参数的mount命令,列出当前已安装的所有的子文件系统。
umount 文件系统的卸载
- umount命令,功能与mount命令相反,卸载一个已安装的子文件系统。例如:umount /dev/sdb
df 文件系统空闲空间
2.2 文件系统的存储结构
(1)文件系统的结构
把整个逻辑设备以块(扇区)为单位为划分,编号为0,1,2 ......(每块521字节或更大的2^n字节大小)
引导块 | 专用块 | i 节点区 | 文件存储区 |
引导块(0号块):用于启动系统,只有根文件系统的引导块有效。
专用块(1号块):也叫管理块,或者超级块、
- 存放文件系统的管理信息。如:文件系统的大小,i 节点区的大小,空闲空间大小,空闲块链表的头等等。
- mkfs 命令时初始化,df 命令读取部分信息,df -i 和 df
i 节点区:i节点(index node)
- i节点区由若干块构成,在mkfs命令创建文件系统时确定。
- 每个文件都对应一个i节点,i节点中的信息包括:指向文件存储区数据块的一些索引(index)
- 指针(组成文件的逻辑块与硬盘的物理块之间的映射)
文件存储区
- 用于存放文件数据的区域,包括目录表。
(2)目录的存储结构
Linux目录结构是树形带交叉勾连的目录结构。
目录表
- 每个目录表也作为一个文件来管理,存于“文件存储区”中。有其自己的i节点和数据存储块。
- 目录表由若干个“目录项”构成,目录项只含两部分信息:文件名、i节点号。
- 用ls命令列出的目录大小时目录表文件本身的长度。
目录表和i节点两级结构
2.3 硬链接
- 目录表由目录项构成,目录项时一个“文件名-i节点号”对应关系。
- 根据文件系统的存储结构,可以在同一目录或者不同目录中的两个目录项,有相同的i节点号。
- 每个目录项指定的“文件名-i节点号”映射关系,叫做1个硬链接。
- 硬链接数目(link数):同一i节点被目录项引用的次数。
ln chapt0 intro # 给chapt0建立硬链接,也就意味着一个文件具有两个名字。chapt0和intro同时存在时,地位是平等的。删除其中的一个文件时,仅仅是Link数将减一,不会影响其他。
不允许对目录用ln命令建立硬链接。
2.4 符号链接
符号链接也叫软连接
- 用符号文件“符号链接文件”来实现;
- 文件中仅包括了一个路径名(包含一个字符串);
(1)符号链接示例
命令 ln -s 和 ls -l
- ln -s users_on sym.link
类型为 l,大小为8字节(就是users_on这8个字符,文件中仅仅存储该字符串),一旦建立了符号链接,删除操作删除的是符号链接文件,其他操作都将访问符号链接所引用的文件。
说明:users_on是一个普通文件,它有自己的i节点,通过i节点的所有信息可以找到磁盘上面的数据文件信息。在其他地方建立了sym.link,该文件和users_on之间没有任何联系,但是在它的i节点里面标识了文件的类型、说明了是符号链接文件,因此通过i节点的存储信息也可以找到磁盘块的信息users_on。
(2)符号链接中的相对路径
- 若符号链接文件包含绝对路径名,引用绝对路径名;
- 若符号链接文件包含相对路径名,是相对符号链接文件的位置(不是相对于调用进程的当前工作目录)
设当前目录(bash进程的当前目录)为 d
ln -s d1/dll d1/dx
在 d1下新建文件dx,访问d1/dx实际是访问d1/d1/dx。(包含相对路径名,相对于符号链接文件,在d1文件下,相对于d1的d1/dx路径)
(3)硬链接与符号链接比较
硬链接
- 在数据结构层次实现
- 只适用于文件,不适用与目录
- 不同文件系统之间也不行
- 硬链接能够完成的功能软连接可以做到
符号链接
- 在算法软件上实现
- 硬链接能够完成的功能软连接都可以做到
- 适用于目录,也适用于不同的文件系统
- 同硬链接相比要占用操作系统内核的一部分开销(唯一的区别就是浪费点开销,可能会循环)
- 循环式符号链接,以及处理方法(解析路径时设置符号链接解析计数器)
上图为一个硬连接例子,chapt0和intro进行了链接,两个的id号是一样的,修改其中一个,另一个也会发生变化;若是删除其中一个文件,则它的Link数会变为1,就会只剩一个文件。
下图为系统中的一些符号链接文件,例如第一行,当访问from时,实际访问的是 /etc/ alternatives/ from 文件。
2.5 系统调用 system call
系统调用以C语言函数调用的方式提供
操作系统内核提供的编程界面
- 应用程序(ap)和操作系统(kernel)进行交互的唯一手段。
- 例如:文件操作的open, read,write,close
种类
- 早期UNIX有50多个,后来扩充到120个,Linux有300多个左右。
(1)系统调用与库函数在执行方式上的区别
例如:获取进程ID的getpid()与字符串拷贝函数strcpy()。
- 系统调用工作在所谓的特权状态,需要CPU的INT指令(软中断)产生中断以后,在中断服务程序才能进入到特权状态,进入到操作系统内核,完成相关操作,完成之后在返回。
- 字符串拷贝函数在汇编中看就是CALL指令(子周期指令),转到执行该子程序的代码就可以。
- 总结:前者需要利用软中断在CPU内核里面完成;后者利用CALL指令在当前进程的堆栈里面完成。
(2)库函数对系统调用的封装(API)
- 目的:执行效率更高或者调用界面更方便。
- 例如:库函数printf是对系统调用write的封装;库函数malloc/free是对系统调用sbrk的封装。
(3)系统调用函数的返回值与错误信息
返回值
一般返回一个整数值
- 返回值大于或等于零:成功
- 返回值为-1:失败
整形变量errno
- 标准库为errno保留存储空间,系统调用失败后填写错误代码,记录失败愿意。
- #include<errno.h>之后,就可以直接使用变量errno。
- errno.h头文件定义了许多有E前缀的宏。例如:EACCESS(访问出错),EIO(调用IO出错),ENOMEM(没有内存),EINTR(系统调用被打断)。
- 在man命令给出的手册页中有ERRORS一节介绍出错原因,如man recv。
有了errno变量只可以就可以方便的知道系统调用出错的原因,但是整形变脸代表的出错原因只是方便计算机识别,对于程序员来说更希望是用字符串来表示出错原因。
strerror
- char *strerror(int errno); //将errno整数变为字符串
- errno是个整数,便于程序员识别错误原因,不便于操作员理解识别原因。
- 库函数strerror将数字形式的错误代码转换成一个可阅读的字符串。
printf的%m
- 是printf类函数格式字符串中的%m会被替换成上次系统调用失败的错误代码对应的message。
#include <errno.h>
int main(char argc, char *argv[]) {
int fd;
fd = open(argv[1], O_WRONLY);
if (fd == -1) { //系统调用失败
//这里的errno不需要进行定义,只要前面有系统调用,它就会存在
printf("ERROR %d: %m\n", errno);
printf("ERROR [%s]\n", srrerror(errno));
}
}
//下面为其中一种错误的输出信息
ERROR 2: No such file or directory
ERROR [No such file or directory]
2.6 访问 i 节点和目录
(1)系统调用stat/fstat
从i节点获得文件的状态信息
- fstat得到已打开文件的i节点。
- stat得到指定路径名的文件的i节点。
stat和fstat将数据放入调用者提供的stat结构中
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf); //fd为已打开文件的文件号
st_dev 存储该文件的块设备的设备号,包括主设备号和次设备号
- 例如:stat 命令显示文件Device:821h/2081d。十六进制0821,主设备号8(高字节),次设备号33(低字节),就是/dev/sdc1(文件的存储位置)。
st_mode 访问权限及文件类型,16比特
- 文件的基本存取权限(自己、同组、其他共计9bit)和SUID/SGID权限(共计11比特),剩余的5bit表示文件的类型。
文件类型判断 st_mode & S_IFMT
- S_IFREG 普通磁盘文件
- S_IFDIR 目录文件
- S_IFCHR 字符设备文件
- S_IFIFO 管道文件
- S_IFLNK 符号链接文件
st_size与st_blocks
- 程序可以通过st_size获取文件大小,一般情况:st_size<=st_block*512
st_atim, st_ctim, st_mtim
- Linux中存储这三个时间的精度为纳秒,“a”访问时间,“c”改变时间,“m”修改时间。
- “a访问”:读,执行(有些系统为了效率做懒惰处理,不进行更新,但不早于m时间)。
- “m修改”:文件内容修改。比如写文件。
- “c改变”:i节点信息变化。比如写文件,修改权限/link数/文件主等。(m改变时c也改变)
(2)目录访问
- 早期的UNIX像普通磁盘文件那样open()打开目录read()读取。
- 现在的系统 不再这样操作,而是直接使用封装好的库函数。
目录访问的一组库函数
- opendir 打开目录得到句柄(NULL表示失败)。
- readdir 获取一个目录项。
- dirent结构体:记录i节点号和文件名(d_ino和d_name成员),返回指针指向的dirent结构体(返回NULL表示已经读到目录尾)。
- 访问结束:用closedir关闭不再使用的目录句柄。
#include <dirent.h>
DIR *opendir(char *dirname);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);