
APUE 第四章 文件和目录
第四章 文件和目录
4.2 stat fstat fstatat lstat 函数
1 |
|
stat
函数返回路径名代表文件的信息结构;
fstat
返回由文件描述符描述的文件的信息结构;
lstat
作用和 stat
差不多,只是如果文件是链接符号,lstat
会返回链接符号本身的信息结构而不是它所链接的文件的结构;
fstatat
用来处理相对路径,其中 flag
参数可以是 AT_SYMLINK_NOFOLLOW
(当文件是链接符号时,返回符号的信息) 或者 AT_FDCWD
(返回符号链接所指向的实际文件的信息)
buf
指向一个结构体
1 | struct stat { |
实际上在使用 ls -l
命令的时候就用到了 stat
函数
4.3 文件类型
- 普通文件 regular file
- 目录文件 directory file
- 块特殊文件 block special file
- 字符特殊文件 character special file
- FIFO 用于进程间通信
- 套接字 socket ,用于进程间的网络通信
- 符号链接 symbolic link , 指向另一个文件
文件类型由 stat
结构体中的 st_mode
成员表示,可用以下宏确定文件类型

4.4 设置用户 ID 和 组 ID
每个进程有6个或者更多的关联ID,如下图

通常,有效用户ID effective user ID = 实际用户ID real user ID
有效组ID effective group ID= 实际组ID real group ID
4.5 文件访问权限
访问权限保存在 st_mode
中,每个文件有9个访问权限位:

chmod
命令可以用来改变这9个位
4.6 新文件和目录的所有权
新文件的用户ID设置为进程的有效用户ID
新问价的组ID有以下两种可能:
- 进程的有效组ID
- 所在目录的组ID
4.7 access 和 faccessat 函数
使用 open
函数打开一个文件时,内核以进程的有效用户ID和有效组ID执行访问权限测试。但有时内核也想使用实际用户ID和实际组ID执行访问权限测试
本节标题中的两个函数就是使用实际用户ID和实际组ID执行访问权限测试
1 |
|
4.8 umask 函数
umask 函数相当于为 进程 创建了一个模板,进程以后创建的文件的访问权限都根据这个模板来设置
1 |
|
cmask
里置1的位所代表的访问权限会被关闭,比如
cmask = 000 111 111
(为了清晰,这里写的是比特位)
那么进程以后创建的所有文件的 group 和 other 的所有访问权限都会被关闭
4.9 chmod 、 fchmod 和 fchmodat 函数
1 |
|
mode
可选下表中列出的值,或者它们的按位或

chmod
函数在下列情况下自动清除两个权限位
- Solaris 等系统对普通文件的粘着位赋予了特殊含义。如果我们在这些系统上以非 root 用户设置普通文件的粘着位,粘着位会自动关闭
- 新创建文件的组ID可能不是调用进程所属的组。这种情况下,新文件的设置组ID位会被自动关闭
4.11 chown 、 fchown 、fchownat 和 lchown 函数
以上四个函数用来修改文件的用户ID和组ID
1 |
|
这四个函数的异同点和 stat
fstat
fstatat
lstat
这四个函数的异同点相同
当 _POSIX_CHOWN_RESTRICTED
有效时,不能更改其他用户文件的用户ID。可以更改自己文件的组ID,但是只能改到自己所属的组
4.12 文件长度
stat
结构成员 st_size
表示以字节为单位的文件的长度。这个字段只对普通文件、目录文件和符号链接有意义
文件中的空洞
du
命令可以查看空洞文件实际占据的磁盘块数
4.13 文件截断
文件截断可以用来缩短文件。
如果想要截断文件,打开的时候要使用 O_TRUNC
标志
1 |
|
4.14 文件系统
磁盘、分区和文件系统的关系:

将上图中的 i-nodes 和 data blocks 放大来看:

图中,有两个目录项指向同一个 i-node 。每个 i-node 有一个链接计数,表示指向它的目录项的个数,只有当链接计数为 0 时才能够删除该文件(和智能指针管理动态内存的方法类似)。
stat
结构中, 链接计数保存在st_nlink
成员中。这种链接类型称为硬链接另外一种链接是符号链接(symbolic link)。符号链接内只保存了它链接的文件的名字
i-node 包含了文件有关的所有信息:文件类型、文件访问权限位、文件长度和指向文件数据块的指针等。
目录项中存放 i-node 编号和文件名
介绍完了文件的链接计数,那么目录是如何进行链接计数的呢? 假设我们运行下面的命令创建了一个新的文件夹 testdir
1 | mkdir testdir |
关于它的链接计数形式请看下图

4.15 link 、 linkat 、unlink 、unlinkat 和 remove 函数
创建一个指向现有文件的链接的方法是使用 link 或 linkat 函数:
1 |
|
为了删除一个现有的目录项,可以使用 unlink 函数:
1 |
|
remove函数可以解除对一个文件或目录的链接
1 |
|
4.16 rename 和 renameat 函数
从命名可以看出,这俩函数是用来重命名的
1 |
|
4.17 符号链接
主要要区分符号链接和硬链接的区别
符号链接是对一个文件的间接指针
硬链接直接指向文件的 i-node
使用符号链接的目的是为了避开硬链接的一些限制
- 硬链接通常要求链接和文件位于同一文件系统中
- 只有 root 用户才能创建指向目录的硬链接
符号链接则没有以上两种限制
在使用函数的时候要注意其是否处理符号链接。如果函数不处理符号链接,那么它将处理链接本身而不是链接指向的文件
下图展示了一些函数对符号链接的支持情况

ftw
函数可以遍历文件结构,打印遇到的每个路径名
4.18 创建和读取符号链接
使用以下俩函数创建符号链接:
1 |
|
我们知道 open
函数会跟随符号链接打开链接指向的文件,那么怎么打开链接自身呢?
使用下面这俩函数:
1 |
|
4.19 文件的时间
UNIX系统为每个文件维护三个时间字段,如下图:

为什么有了 st_mtim
文件数据最后修改的时间,还需要 st_ctim
i-node最后被修改的时间呢?
因为 文件数据 和 i-node 里的信息是 完全分开存放的。
4.20 futimens 、utimensat 和 utimes 函数
这仨函数用来更改文件的访问和修改时间
1 |
|
timeval
是结构体,它的结构如下
1 | struct timeval{ |
times
是个数组,第一个元素指定访问时间 st_atim
,第二个元素指定修改时间 st_mtim
4.21 mkdir 、mkdirat 和 rmdir 函数
mkdir 、mkdirat 创建目录,rmdir 删除目录
1 |
|
创建目录的时候需要注意,对于目录通常至少要设置执行权限位,以允许访问该目录中的文件名
1 |
|
rmdir
可以删除一个空目录
4.22 读目录
1 |
|
dirent
结构体至少包含 ino_t d_ino
char d_name[]
这两个成员,前者是 i-node 编号,后者是文件名
4.23 chdir 、fchdir 和 getcwd 函数
进程调用chdir 和 fchdir 函数可以更改进程当前的工作目录
1 |
|
pathname
和 fd
都用来指定新的工作目录
4.25 文件访问权限小结

S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR
S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH