APUE学习笔记2——第四章——文件和目录
来源:互联网 发布:java object的方法 编辑:程序博客网 时间:2024/05/22 19:54
APUE学习笔记2——第四章——文件和目录
学号:16340043
中山大学
本博客为《UNIX环境高级编程》的学习笔记,希望能对大家有所帮助
1.前面的废话
额,其实这个笔记是昨天做的啦(今天把它搞成了markdown格式),所以今天会放两篇啦(今天直接用这个格式记笔记了)。习题都还没做完…先空着再补吧…好多事要干
2.博客正文
4.1 引言
本章将描述文件系统的其他特性和文本的所有属性,以及说明修改这些属性的各个函数
4.2 函数stat、fstat、fstatat和lstat
本章主要讨论4个函数和他们的返回信息
#include <sys/stat.h>int stat(const char *restrict pathname, struct stat *restrict buf);int fstat(int fd, struct stat buf);int lstat(const char *restrict pathname, struct stat *restrict buf);int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
- pathname:文件名字
一旦给出pathname:
stat函数将返回与此命名文件相关的信息结构
lstat函数类似于stat函数,但会返回符号链接(而不是它引用的文件)的有关信息
fstat函数获取描述符fd上打开文件的相关信息
fstatat在fd为AT_FDCWD相对于fd所指当前目录的路径名返回统计信息(path为绝对路径名时fd被忽略)
- flag:控制是否跟随一个符号链接
- buf:一个指针,指向我们想要的信息结构(很长,后面会一一描述的吧,相关信息我加$号)
- 返回值:若成功,返回0;若出错,返回-1
- $成员timespec结构按照秒和纳秒定义了时间,至少包含:
time_t tv_sec; long tv_nsec;
使用stat函数最多的地方可能就是ls -l命令。(终于看到眼熟的东西了/哭
4.3 文件类型
- 1)普通文件(regular file)包含了某种形式的数据,无论数据是文本还是二进制,对UNIX内核无区别
- 2)目录文件(directory file)包含了其他文件的名字以及指向与这些文件有关信息的指针
内核才能直接写目录文件,进程必须使用本章介绍的函数来更改目录 - 3)快特殊文件(block special file)提供对设备带缓冲的访问(每次都是固定长度)
- 4)字符特殊文件(character special file)提供对设备不带缓冲的访问(每次长度可变)
- 5)FIFO用于进程间的通信,也称谓命名管道
- 6)套接字(socket)用于进程间的网络通信,也可用于在一台宿主机上进程间的非网络通信
- 7)符号链接(symbolic link)指向另一个文件
- $成员st_mode描述了文件类型信息
可通过S_ISREG(buf.st_mode) 这样的宏来判断文件类型(好多我不列出来了)
特殊地,进程间通信对象(IPC)也允许被说明为文件,它们也有自己一套宏
tip:在命令行末端输入\告知shell要在下一行继续键入命令
4.4 设置用户ID和设置组ID
与一个进程相关联的ID有6个或更多:
- $成员st_uid、st_gid分别指定一个文件的所有者和组所有者
设置用户ID位和设置用户组ID位可分别用常量S_ISUID和S_ISGID测试
4.5 文件访问权限
每个文件有9个访问权限位:
chmod命令可用于修改这九个权限位(u用户 g组 o其他)
当我们用名字打开任一类型的文件时,对该名字中包含的每个目录都应具有执行权限
进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试
除了超级用户,别的用户都要设置适当地访问权限来操作该文件
4.6 新文件和目录的所有权
- 新文件的用户ID设置为进程的有效用户ID
- 新文件的组ID可以是进程的有效组ID或它所在目录的组ID(二选其一)
4.7 函数access和faccessat
access和faccessat按实际用户ID和实际组ID测试访问权限:
#include <unistd.h>int access(const char *pathname, int mode);int faccessat(int fd, const char *pathname, int mode, int flag);
- mode: R_OK测试读权限 W_OK测试写权限 X_OK测试执行权限
- fd:为AT_FDCWD时为相对路径
- flag:设置为AT_EACCESS时检查用的是有效XX而不是实际XX
- 返回值:若是,返回0;若不是,返回-1
4.8 函数umask
umask为进程设置文件模式创建屏蔽字:
#include <sys/stat.h>mode_t umask(mode_t cmask);
- cmask:是4.5中列出的9个常量中的若干位”或操作”构成的
- 返回值:之前的文件模式创建屏蔽字
在文件模式创建屏蔽字中为1的位,在文件mode中的相应位一定被关闭
通过更改umask的值可以设置权限2:
命令行中:
4.9 函数chmod、fchmod和fchmodat
chmod、fchmod和fchmodat用于更改现有文件访问权限:
#include <sys/stat.h>int chmod(const char *pathname, mode_t mode);int fchmod(int fd, mode_t mode);int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
- chmod在指定文件上进行操作
- fchmod对打开文件进行操作
- fchmodat中的fd参数与前面情况一样
- flag:当设置了AT_SYMLINK_NOFOLLOW标志时,fchmodat不会跟随符号链接
- mode:比起4.5列出的,多了:
- 返回值:若成功,返回0;若失败,返回-1
可以通过 statbuf.st_mode & ~S_IXGRP 这样的位操作来关闭某一位哦
tip:chmod更新的是i节点最近一次被修改的时间,而ls -l列出的是最后修改文件内容的时间,故看不到变化
若新文件的组ID不等于进程的有效组ID或进程的附属组ID中的一个(可能是父目录的组ID),而且用户没有超级权限,那么设置组ID位会被自动关闭3
4.10 粘着位
- 老系统的粘着位:
如果一个程序的粘着位被设置,其正文部分(机器指令)会被保存在交换区,下次执行时能更快地载入内存
现在,由于大多较新的UNIX系统都配置了虚拟存储系统和快速文件系统,所以不再需要这种技术 - 新系统的粘着位:
如果一个目录设置了粘着位,只有对该目录具有写权限且满足下列条件之一,才能删除或重命名该目录下的文件:- a拥有此文件
- b拥有此目录
- c是超级用户
有了粘着位,用户可在/tmp目录中读、写和执行,但不能删除或重命名他人的文件
4.11 函数chown、fchown、fchownat和lchown
chown、fchown、fchownat和lchown用于更改用户ID和组ID
#include <unistd.h>int chown(const char *pathname, uid_t owner, git_t group);int fchown(int fd, uid_t owner, git_t group);int fchownat(int fd, const char *pathname, uid_t owner, git_t group,int flag);int lchown(const char *pathname, uid_t owner, git_t group);
- owner或group中的任意一个是-1,则对应ID不变
- 其他参数类似stat那四个函数
- flag:若设置AT_SYMLINK_NOFOLLOW,则行为与lchown相同,否则,与chown相同
- 返回值:若成功,返回0;若出错,返回-1
一般只有超级用户才能更改一个文件的所有者
若_POSIX_CHOWN_RESTRICTED生效,则:
- 1)超级用户可以更改该文件的用户ID
- 2)如果进程拥有此文件,参数owner等于-1或文件的用户ID,且group等于进程的有效组ID或附属组ID之一,那么一个非超级用户可以修改一个文件的组ID(这意味着若_POSIX_CHOWN_RESTRICTED生效,你可以更改你所拥有文件的组ID)
4.12 文件长度
- $成员st_size表示以字节为单位的文件的长度。此字段只对普通文件、目录文件和符号链接有意义
- 对于普通文件,文件长度可以是0,一开始读就会读到EOF
- 对于目录,文件长度通常是一个数(如16或512的整数倍)
- 对于符号链接,文件长度就是文件名的实际字节数
现在大多数UNIX系统提供字段st_blksize和st_blocks
第一个是对文件I/O的较合适的块长度,第二个是所分配的实际XXX4字节块块数
4.13 文件截断
truncate和ftruncate在文件尾端截去一些数据以缩短文件
#include <unistd.h>int truncate(const char *pathname, off_t length);int ftruncate(int fd, off_t length);
其中: 两个函数将一个现有文件长度截断为length 若以前的长度大于length,则超过length的部分不能再访问 若以前的长度小于length,文件长度将增加(多的都是0) 返回值:若成功,返回0;若出错,返回-1
4.14 文件系统
- 每一种文件系统都有它各自的特性,如对大小写是否敏感等
- 一个磁盘可以分成一个或多个分区,每个分区可以包含一个文件系统
- i节点是固定长度的记录项,它包含有关文件的大部分信息
- 每个i节点中都有个链接计数,其值是指向该i节点的目标项数,连接计数减为0时才可删除该文件
stat结构中的大部分数据都取自i节点,只有文件名和i节点编号(ino_t)存放在目录项
一个目录项不能指向另一个文件系统的i节点,所以ln命令不能跨文件系统
给文件重命名时,构造一个指向现有i节点的新目录项,并删除老目录项(mv的工作方式)任何一个叶目录的连接计数总是2,一个来自命名该目录的目录,一个是.项
- 在父目录中的每个子目录都使该父目录连接计数加1
4.15 函数link、linkat、unlink、unlinkat和remove
link、linkat5创建一个指向现有文件的链接:
#include <unistd.h>int link(const char *existingpath, const char *newpath);int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
- newpath:创建的新目录项(若存在会出错)
- existingpath:链接的现有文件
- efd、nfd:同前,分别辅助指定现有路径名和新路径名。若任一个被设置为AT_FDCWD,那么相应的路径名就按相对路径计算
- flag:若指定AT_SYMLINK_FOLLOW,就创建指向符号链接目标的链接,反之,创建符号本身的链接
- 返回值:若成功,返回0;若出错,返回-1
为了删除一个现有的目录项,可以调用unlink函数:
#include <unistd.h>int unlink(const char *pathname);int unlinkat(int fd, const char *pathname, int flag);
- 删除后,pathname所引用文件的连接计数减1
- 只要有进程打开了该文件,其内容不会删除(这时连接计数不为0),进程结束时才删除
- 若出错,不做任何操作
- fd:与之前类似
- flag:当设置为AT_REMOVEDIR时,可类似于rmdir一样删除目录,反之,与unlink执行一样的操作
- 返回值:若成功,返回0;若出错,返回-1
在给出符号链接名的情况下,没有一个函数能删除该链接所引用的文件
remove函数能直接解除一个文件或者目录的链接:
#include <stdio.h>int remove(const char *pathname);
- 返回值:若成功,返回0;若出错,返回-1
4.16 函数rename和renameat
rename和renameat可重命名文件或目录:
#include <stdio.h>int rename(const char *oldname, const char *newname);int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
- 若oldname为文件,newname已存在,只有当newname不为目录且拥有两个文件的写权限时,才能更改
- 若oldname为目录,newname已存在,newname包含oldname作为其路径前缀时不能修改
- 不能对.和..重命名
- 若oldname与newname相同,则函数不做处理
- 返回值:若成功,返回0;若出错,返回-1
4.17 符号链接
符号链接是对一个文件的间接指针,引入符号链接是为了避开硬链接的一些限制:
- 硬链接要求链接和文件位于同一文件系统中
- 只有超级用户才能创建指向目标的硬链接
使用以名字引用文件的函数时,要注意函数是否能跟随符号链接到它所指的文件
使用符号链接可能在文件系统中引入循环,导致一些索引函数报错
ls使用选项-F会在文件名后添加@帮助识别符号链接(-l中第一个字符是1,有->也表明这是个符号链接)
4.18 创建和读取符号链接:
可以用symlink或symlinkat函数创建一个符号链接
#include <unistd.h>int symlink(const char *actualpath, const char *sympath);int symlinkat(const char *actualpath, int fd, const char *sympath);
- 函数创建了指向actualpath的新目录项newpath
- 返回值:若成功,返回0;若出错,返回-1
因为open跟随符号链接,所以需要一种方法打开链接本身,readlink和readlinkat函数提供了这种功能:
#include <unistd.h>ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);ssize_t readlinkat(int fd, const char *restrict pathname, char *restrict buf, size_t bufsize);
- 两个函数组合了open、read和close所有操作
- 返回值:若成功,返回读取的字节数;若出错,返回-1
4.19 文件的时间
系统并不维护对i节点的最后访问时间
增加、删除或修改目录项会影响它所在目录的3个时间
4.20 函数futimens、utimensat和utimes
更改文件的访问和修改时间(前两个可以指定到纳秒级精度)
#include <sys/stat.h>int flutimens(int fd, const struct timespec times[2]);int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
- timspec的结构见4.2
- times:若为空指针,访问时间和修改时间都设定为当前时间
其指向两个timespec结构的数组- 若任一数组tv_nsec的值为UTIME_NOW,相应的时间戳就设定为当前时间
- 若任一数组tv_nsec的值为UTIME_OMIT,相应的时间戳保持不变
- 若该字段不是上面两个,时间戳就设定为相应的tv_sec和tv_nsec的值
- flag:用于修改是否跟随符号链接(见上)
- 返回值:若成功,返回0;若出错,返回-1
#include <sys/stat.h>int utimes(const char *pathname, const struct timeval times[2]);//timval的定义: struct timval { time_t tv_sec; long tv_usec;};
- 两个时间戳用秒和微秒来表示
- 返回值:若成功,返回0;若出错,返回-1
我们不能对st_ctim指定一个值,因为调用utime时,此字段会被自动更新
4.21 函数mkdir、mkdirat和rmdir
用mkdir、mkdirat创建目录:
#include <sys/stat.h>int mkdir(const char *pathname, mode_t mode);int mkdirat(int fd, const char *pathname, mode_t mode);
- 对于目录应至少指定一个执行权限位,以确保能访问到里面的文件
- 返回值:若成功,返回0;若出错,返回-1
用rmdir函数可以删除一个空目录:
#include <unistd.h>int rmdir(const char *pathname);
- 若有一个或多个进程打开此目录,此函数在返回前删除.和..
- 返回值:若成功,返回0;若出错,返回-1
4.22 读目录
对某个目录具有访问权限的任意用户都可以读该目录:
#include <dirent.h>DIR *opendir(const char *pathname);DIR *fdopendir(int fd); //返回值:若成功,返回指针;若出错,返回nullstruct dirent *readdir(DIR *dp); //返回值:若成功,返回指针;若在文件尾出错,返回nullvoid rewinddir(DIR *dp);int closedir(DIR *dp); //返回值:若成功,返回0;若出错,返回-1long telldir(DIR *dp); //返回值:与dp关联的目录中的当前位置void seekdir(DIR *dp, long loc);
- dirent结构至少包含以下成员
ino_t d_ino; //i节点编号char d_name[]; //目录名
(书中没有详细地说这些函数,但给出了一个实例:不跟随符号链接的ftw的实现)
4.23 函数chdir、fchdir和getcwd
进程调用chdir或fchdir函数可以更改当前工作目录:
#include <unistd.h>int chdir(const char *pathname);int fchdir(int fd);
shell的当前工作目录不会随程序调用chdir而改变,为改变shell工作目录,cd内建在shell中
进程调用getcwd得到当前工作目录绝对路径名:
#include <unistd.h>char *getcwd(char *buf, size_t size);
- 返回值:若成功,返回buf;若出错,返回null
buf应有足够的长度容纳绝对路径名加上一个终止null字节,否则出错
4.24 设备特殊文件
- $成员st_dev和st_rdev为设备号
主设备号,标识设备驱动程序
次设备号,标识特定的子设备
宏major和minor可以访问主、次设备号(如:major(buf.st_rdev))
4.25 文件访问权限位小结
(一张大大的表)
4.26 小结
终于完了……太多了……把stat给过了一遍
3.习题
空
- 通常,有效用户ID==实际用户ID,有效组ID==实际组ID ↩
- 更改进程的umask的值并不影响其父进程的屏蔽字 ↩
- 自动关闭组ID位是为了防止恶意用户修改数据 ↩
- XXX取决于环境变量POSIXLY_CORRECT的设置 ↩
- 这两个操作为原子操作 ↩
- APUE学习笔记——第四章 文件和目录
- APUE学习笔记2——第四章——文件和目录
- 《APUE》读书笔记—第四章文件和目录
- apue学习第八天——文件和目录(第四章)
- apue学习笔记(第四章 文件和目录)
- 《APUE》笔记-第四章-文件和目录
- APUE学习: 第四章, 文件和目录
- APUE学习——Ch4.文件和目录
- APUE读书笔记——文件和目录
- [APUE]第四章 文件和目录
- APUE第四章 文件和目录
- APUE读书笔记-第四章 文件和目录
- APUE第四章 文件和目录
- [APUE]第四章文件和目录
- 文件和目录之stat族函数<stat | fstat | lstat | fstatat>——APUE学习笔记(2)
- 文件和目录之stat族函数<stat | fstat | lstat | fstatat>——APUE学习笔记(2)
- 《apue》读书笔记 第四章 文件和目录(2)
- apue第四章<文件和类型> 笔记
- GC(1.安全点的相关知识)
- 树状数组模板
- 慕课网JAVA入门第二季综合练习答答租车系统
- String、StringBuffer和StringBuilder
- GC(1.1OOPMap)
- APUE学习笔记2——第四章——文件和目录
- 什么是A类、B类、C类地址?
- 学习Caffe代码的方法心得
- Python 使用字符串
- java数据结构和算法(栈)
- CrapApiV6效果图
- 【安卓】安卓App开发思路 一步一个脚印(三)BaseActivity BaseFragment
- python挑战之level 28
- 循环