目录
1 Linux文件系统概述
1.1 Linux文件系统特性
1.2 Linux与windows文件系统的区别
1.3 Linux文件系统架构
1.4 虚拟文件系统(VFS)
2 Linux文件的IO操作
2.1 Linux系统调用
2.2 文件描述符
2.3 Linux文件IO函数说明
3 Linux标准I/O库
3.1 fopen函数
3.2 setbuf和setvbuf函数
3.3 fdopen函数
4 Linux文件定位
4.1 lseek函数
4.2 pread函数
4.3 pwrite函数
5 Linux文件共享
5.1 进程共享文件
5.2 线程共享文件
5.3 进程间文件描述符的传递
6 目录操作
6.1 ls -l功能分析
6.2 获取当前工作路径函数
6.3 Dir结构体
6.4 打开目录函数
6.5 关闭目录函数
6.6 Dirent结构体
6.7读取目录文件函数
6.8 创建目录函数
6.9 删除目录函数
6.10 改变当前工作目录函数
6.11 设置目录读取位置函数
6.12 获取目录读取位置函数
6.13 读取特定目录数据函数
7 文件属性管理
7.1 读取文件属性函数
7.2 设置用户ID位和设置组ID位
7.3 文件长度
7.4 文件截断函数
7.5 根据用户ID获取用户属性
7.6 根据组ID获取属性
7.7 设备特殊文件
8 Linux文件权限管理
8.1 access函数
8.2 chmod、fchmod函数
8.3 更改文件所有者函数
8.4 Link函数
8.5 unlink和remove函数
8.6 更名文件和目录函数
8.7 utime函数
8.8 mkdir函数
8.9 rmdir函数
1 Linux文件系统概述
1.1 Linux文件系统特性
- “一切皆文件”是Linux的基本哲学之一。
- 普通文件、目录、字符设备、块设备、套接字等在Linux中都是文件。
- 类型不同的文件都是通过相同的API对其进行操作。
1.2 Linux与windows文件系统的区别
1.3 Linux文件系统架构
1.4 虚拟文件系统(VFS)
是Linux内核中的软件层,对内实现文件系统的抽象,允许不同的文件系统共存,对外向应用程序提供统一的文件系统接口。
为了支持不同的文件系统,VFS定义了所有文件系统都支持的基本的、抽象的接口和数据结构。
1.4.1 VFS中的数据结构
- 超级块是对一个文件系统的描述
- 索引节点是对一个文件物理属性的描述
- 目录项是对一个文件逻辑属性的描述
- 一个进程所处的位置是由fs_struct来描述的,而一个进程(或用户)打开的文件是由files_struct来描述的,而整个系统所打开的文件是由file结构来描述
2 Linux文件的IO操作
2.1 Linux系统调用
系统调用:操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。
- 系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。
- 在实际使用中程序员调用的通常是用户编程接口——API。
- Linux中的系统调用包含在Linux的libc库中,通过标准的C函数调用方法可以调用。
- 系统命令相对API更高了一层,它实际上是一个可执行程序,它的内部调用了用户编程接口(API)来实现相应的功能。
2.2 文件描述符
文件描述符:文件描述符是一个非负的整数,它是一个索引值,并指向在内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。一个进程启动时,通常会打开3个文件: —–标准输入 描述符为0—–标准输出 描述符为1—–标准出错处理 描述符为2
2.3 Linux文件IO函数说明
2.3.1 open函数
2.3.2 creat函数
2.3.3 close函数
2.3.4 read函数
2.3.5 write函数
2.3.6 ioctl函数(ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径 )
3 Linux标准I/O库
为什么要设计标准I/O库?
- 直接使用API进行文件访问时,需要考虑许多细节问题,例如:read、write时,缓冲区的大小该如何确定,才能使效率最优。
- read和write等底层系统调用函数进行输入输出时,在用户态和内核态之间来回切换,每次读出或写入的数据量较少,导致频繁的I/O操作,增加了系统开销。
它如何减少直接读盘次数的?
- 读取前查看是否已存在页缓存中,如果已经存放在了页缓存中,数据立即返回给应用程序。
- 写数据前先写到页缓存中,如果用户采用的是同步写机制( synchronous writes ), 那么数据会立即被写回到磁盘上,应用程序会一直等到数据被写完为止;如果用户采用的是延迟写机制( deferred writes ),那么应用程序就完全不需要等到数据全部被写回到磁盘,数据只要被写到页缓存中去就可以了。
3.1 fopen函数
3.2 setbuf和setvbuf函数
3.3 fdopen函数
4 Linux文件定位
4.1 lseek函数
- lseek常用于找到文件的开头、找到文件的末端,判定文件描述符的当前位置。
- lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作,然后该偏移量用于下一次读、写操作。
- 文件偏移量可以大于文件的当前长度,但并不改变相应的i节点信息。在这种情况下的下一次写将延长该文件,并在文件中构成一个空洞,但文件大小并不是文件的最大偏移量。对空洞位置的读操作将返回0。
Lseek实现空洞
4.2 pread函数
4.3 pwrite函数
pread/pwrite与read/write的区别
- 调用更容易使用,特别是在进行需要技巧的操作时
- 完成工作后不会改变文件指针
- 避免使用lseek时可能造成的竞争条件(如果有多个线程共享文件描述符,当地一个线程调用lseek之后,在它进行读取或写入操作之前,同一个程序中的另一个线程可能会改变文件的位置)
5 Linux文件共享
Linux支持不同进程间共享文件。内核使用的三种表(文件描述符表、文件表、索引结点表)之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
- 每个进程在进程表中有一个文件描述符表,每个描述符表项指向一个文件表。
- 内核为每一个被打开的文件维护一张文件表,文件表项包含:文件的状态标志(读、写、同步、非阻塞)、文件当前位置 、指向该文件索引节点表的指针
- 每个文件(或设备)都有一个索引节点,它包含了文件类型属性及文件数据。
5.1 进程共享文件
◼ 如果两个进程分别打开同一个的文件(物理文件),则它们有不同的文件表,因此每个进程有自己的文件当前位置,因此其读写操作互不影响。◼ 也存在不同进程共享同一个文件表(父子进程),或同一进程共享同一个文件表(dup操作)。此时,两个进程对该文件的读写操作将基于同一个文件当前位置。 不同进程共享相同文件(一)不同进程共享相同文件(二)同一进程共享相同文件(一)同一进程共享相同文件(二)
5.2 线程共享文件
◼ 线程的定义:有时称轻量级进程,是进程中的一个执行线路或线索,是一个相对独立的、可独立调度和指派的执行单元◼ 线程的创建:应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程。
- clone()调用do_fork()创建线程, do_fork()参数为:(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
- CLONE_VM:do_fork()需要调用copy_mm()来设置task_struct中的mm和active_mm项,这两个mm_struct数据与进程所关联的内存空间相对应。如果do_fork()时指定了CLONE_VM开关,copy_mm()将把新的task_struct中的mm和active_mm设置成与current的相同,同时提高该mm_struct的使用者数目(mm_struct::mm_users)。也就是说,轻量级进程与父进程共享内存地址空间。
- CLONE_FS:task_struct中利用fs(struct fs_struct *)记录了进程所在文件系统的根目录和当前目录信息,do_fork()时调用copy_fs()复制了这个结构;而对于轻量级进程则仅增加fs->count计数,与父进程共享相同的fs_struct。也就是说,轻量级进程没有独立的文件系统相关的信息,进程中任何一个线程改变当前目录、根目录等信息都将直接影响到其他线程。
- CLONE_FILES:一个进程可能打开了一些文件,在进程结构task_struct中利用files(struct files_struct *)来保存进程打开的文件结构(struct file)信息,do_fork()中调用了copy_files()来处理这个进程属性;轻量级进程与父进程是共享该结构的, copy_files()时仅增加files->count计数。这一共享使得任何线程都能访问进程所维护的打开文件,对它们的操作会直接反映到进程中的其他线程。
- CLONE_SIGHAND:每一个Linux进程都可以自行定义对信号的处理方式,在task_struct中的sig(struct signal_struct)中使用一个struct k_sigaction结构的数组来保存这个配置信息,do_fork()中的copy_sighand()负责复制该信息;轻量级进程不进行复制,而仅仅增加signal_struct::count计数,与父进程共享该结构。也就是说,子进程与父进程的信号处理方式完全相同,而且可以相互更改。
- 总结:线程间所有文件结构都为共享资源,不但“文件表项”(file对象)是共享的,就连“文件描述符表”(files_struct结构)也是共享的。线程的创建仅仅增加的是files和fs的引用计数,“文件打开计数”(file对象的引用计数)并没有增加,所以任何一个线程对打开的文件执行close操作,文件都将关闭(文件打开计数为1的情况)。但是如果线程不进行打开文件的关闭,则文件直到进程结束时才会关闭,这就是使用多线程实现tcp服务器时,服务线程必须要显示调用close的原因,否则永远不会发送FIN终止链接(因为主线程一直处于监听不会结束)。
5.3 进程间文件描述符的传递
◼ 传递描述符的函数的参数是fd,fd是打开文件指针在数组中的下标◼ 将一个文件描述符传递给另一个进程后,文件的“访问计数”会增加◼ 进程间传递文件描述符可以看做跨进程的dup调用,也就是同一个file对象在不同进程间的映射◼ 对于网络接口返回的描述符 ,只能采取传递文件描述符的方法。◼ UNIX系统中两个方法:BSD sendmsg,recvmsg方法;SYSV ioctl方法◼ 进程间传递文件描述符时,发送进程和接收进程共享同一文件表项◼ 进程间文件描述符的传递,只是通过内核将接收文件的一个新file指针指向和发送进程的同一个file对象,并使这个file对象的引用计数增加
6 目录操作
6.1 ls -l功能分析
6.2 获取当前工作路径函数
6.3 Dir结构体
6.4 打开目录函数
6.5 关闭目录函数
6.6 Dirent结构体
6.7读取目录文件函数
6.8 创建目录函数
6.9 删除目录函数
6.10 改变当前工作目录函数
函数说明:
- 每个进程都具有一个当前工作目录。在解析相对目录引用时,该目录是搜索路径的开始之处。
- 如果调用进程更改了目录,则它只对该进程有效,而不能影响调用它的那个进程。
- 在退出程序时,shell还会返回开始时的那个工作目录。
- 内核解析参数中的路径名,并确保这个路径名有效。内核定位该目录的索引节点,并检查它的文件类型和权限位,确保目标文件是目录以及进程的所有者可以访问该目录。
- 内核用新目标目录的路径名和索引节点替换u区中当前目录路径名和索引节点号。
6.11 设置目录读取位置函数
6.12 获取目录读取位置函数
6.13 读取特定目录数据函数
7 文件属性管理
Linux系统中常见的文件类型:
- 普通文件:包含了某种形式的数据
- 目录文件:包含了其他文件的名字以及指向与这些文件有关信息的指针
- 字符特殊文件:提供对设备不带缓冲的访问
- 块特殊文件:提供对设备带缓冲的访问
- FIFO文件:用于进程间的通信,命名管道
- 套接字文件:用于网络通信
- 符号链接:使文件指向另一个文件
7.1 读取文件属性函数
7.2 设置用户ID位和设置组ID位
7.3 文件长度
- stat结构的成员st_size包含了以字节为单位的该文件的长度。此字段只对普通文件、目录文件和符号连接有意义
- 对于普通文件,其文件长度可以是0,在读这种文件时,将得到文件结束指示
- 对于目录,文件长度通常是一个数,例如16或512的整倍数
- 对于符号连接,文件长度是在文件名中的实际字节数。例如:lrwxrwxrwx 1 root 7 Sep 25 07:14 lib -> usr/lib 其中,文件长度7就是路径名usr/lib的长度
7.4 文件截断函数
常见问题: truncate和ftruncate函数并未实质性的向磁盘写入数据,只是分配了一定的空间供当前文件使用。当fd<length时,此时如果使用十六进制编辑工具打开该文件,会发现文件末尾多了很多00,这就是执行这个函数后的效果。如果发生系统复位或者装置掉电以后,该函数所产生的作用将被文件系统忽略,也就是说它所分配的空间将不能被识别,文件的大小将会是最后一次写入操作的区域大小,而非ftruncate分配的空间大小,也就是说,文件大小有可能会被改变。解决方案: 可以在执行完ftruncate之后,在新空间的末尾写入一个或以上字节的数据(不为0x00),这样新空间则不为空,文件系统会把这部分空间当成这个文件的私有空间处理,而不会出现文件大小改变的错误。
7.5 根据用户ID获取用户属性
7.6 根据组ID获取属性
7.7 设备特殊文件
8 Linux文件权限管理
- Linux系统通过进程的有效用户ID和有效用户组ID来决定进程对系统资源的访问权限
- 与一个进程相关联的用户ID和用户组ID有如下几种
- 通常情况下,有效用户ID等于实际用户ID,有效组ID等于实际组ID;
- 可执行文件的权限中有一个特殊标志,定义为“当执行此文件时,将进程的有效用户ID设置为文件的所有者”,与此类似,组ID也有类似的情况。这两个标志位称为:“设置用户 ID” 和 “ 设 置 组 ID” , 这 两 位 都 包 含 在 stat 信 息 中 的 st_mode 中 , 可 用 S_ISUID,S_ISGID测试。
8.1 access函数
8.2 chmod、fchmod函数
文件存取许可权
chmod函数出错信息
fchmod函数出错信息