无缓冲文件IO和目录操作
引言
在后台开发中,对于文件I/O我们通常不使用C语言封装的fopen、fread、fwrite标准I/O,而是直接使用Linux提供的系统调用函数。因为这些系统调用没有使用用户缓冲区,我们直接与内核打交道,效率更高,且可以自己定制一些符合应用场景的操作。下面介绍Linux用于文件I/O的数据结构,以及一些具体的系统调用函数。
文件描述符
所有打开的文件都通过文件描述符引用,文件描述符只在当前进程有效,因为每个进程有一个PCB结构体,PCB包含一个文件描述符表。
文件描述符0对应标准输入、1对应标准输出、2对应标准错误,这些是在进程创建时默认绑定的文件描述符。
以上分别对应unistd.h中的STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。
每个进程的最大文件描述符可通过ulimit -a
命令查看,并可通过ulimit -n 数字
设置。
无缓冲文件IO
我们这里所说的无缓冲IO指的是无用户缓冲区,如fopen、fwirte、fread等函数,进程会在用户进程空间维护缓冲区,然后内核还有缓冲区,最后才是磁盘。而无缓冲IO是指只有内核缓冲区而无用户缓冲区,并不是没有任何缓冲区。
内核用于文件I/O的数据结构
每个进程在进程表中都有一个记录项,记录项包含一张打开文件描述符表。
在打开文件描述符表中,每个描述符占用一项:
- 文件描述符标志:目前只有CLOEXEC。
- 指向文件表项的指针
内核为所有打开文件维持一张文件表,每个文件表项包括:
- 文件状态标志:包括文件类型和访问权限。
- 当前文件偏移量
- 指向该文件inode节点的指针
他们之间的关系如下:


不同的文件表项可以指向相同的文件(i节点即索引结点),这可以使不同的进程有它自己的对该文件的偏移量和打开访问权限。
不同的文件描述符可以指向相同的文件表项。如在fork后,父子进程的每个相同文件描述符指向同一个文件表项。
【PS】:注意文件描述符标志(file descriptor flags)和文件状态标志(file status flags)的区别。
open函数
flag参数可以为:
- 必选参数:O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)、O_EXEC(只执行)、O_SEARCH(只搜索,应用于目录文件)。这5个参数互斥,即只能指定一个。
- 可选参数,按位或:O_APPEND(每次写时都追加到文件尾端)、O_CLOEXEC(将FD_CLOEXEC常量设置为文件描述符标志)、O_CREAT(若文件不存在则创建它,用该参数时需要指定第三个参数即文件的权限位mode如
0644
,存在则直接打开)、O_DIRECTORY(若path不是目录,则返回错误)、O_EXCL(与O_CREAT同时指定,若文件已经存在,则出错。可以原子地测试和创建文件。)、O_NONBLOCK(设置非阻塞)、O_SYNC(每次write等待物理I/O完成)、O_TRUNC(若文件存在,且以只写或读写打开,则将其长度截断为0)。
creat函数
creat函数的一个缺点是它以只写方式打开新创建的文件。
close函数
当进程终止时,内核会自动关闭它打开的所有文件。
lseek函数
每个打开文件都有一个当前文件偏移量(在系统全局的打开文件表项中)。读、写操作都从当前偏移量开始,并使偏移量增加所读的字节数。按系统默认,当打开一个文件时,若没有指定O_APPEND选项,该偏移量被设置为0。
whence可以取以下值:
- SEEK_SET:将文件偏移量设置为距文件开始处offset字节处,offset为正。
- SEEK_CUR:将文件偏移量设置为距现在位置offset字节处,offset可正可负。
- SEEK_END:将文件偏移量设置为距文件尾端offet字节处,offset可正可负。
文件偏移量可以大于文件当前长度,对该文件的下一次写将加长该文件,并在文件中形成空洞。位于文件中但没有写过的字节都被读为0.
read函数
读操作从文件当前偏移量开始,读成功后,偏移量增加读到的字节数。
write函数
write返回值通常与参数nbytes的值相同,否则表示出错。
read和write的阻塞、非阻塞
read和write对于常规文件不会阻塞,一定会在有限时间内返回。read终端或网络设备时可能阻塞,终端在用户输入换行时才会刷新输入缓冲区,网络设备则不确定何时有输入。写网络设备可能阻塞。可以在open
文件时指定O_NONBLOCK
设定为非阻塞,则read时需要循环读。
dup和dup2函数
dup返回最小未用的文件描述符,使返回的文件描述符指向和fd相同的文件表项。
dup2先关闭fd2,然后使fd2指向fd指向的文件表项,并返回fd2。
新的描述符的执行时关闭标志(close-on-exec)总是被dup函数清除。
sync和fsync函数
当向文件中写入数据时,内核先将数据复制到内核缓冲区,然后排入队列,晚些时候再写入磁盘,被称为延迟写。
sync将所有修改过的块缓冲区排入写队列,然后立即返回,并不等待实际写磁盘操作结束。
fsync只对由文件描述符fd指定的文件起作用,并且等待写磁盘操作结束后才返回,并不立即返回。
stat函数
stat函数用于查看文件的属性信息。
获取文件类型:
方法①:按位与。
方法②:使用宏。
获取文件权限:按位与。
fcntl函数
cmd可以取以下值:
- F_DUPFD、F_DUPFD_CLOEXEC复制描述符
- F_GETFD、F_SETFD设置获取文件描述符标志,目前只能改变
O_CLOEXEC
。 - F_GETFL、F_SETFL设置获取文件状态标志,只能改变部分属性:
O_APPEND
、O_ASYNC
、O_NONBLOCK
、O_DIRECT
、O_NOATIME
。PS:改变文件描述符标志和文件状态标志都需要先获取原来的,然后在原来的基础上按位或(设置)、按位与非(取消设置),最后设置。 - F_GETOWN、F_SETOWN设置获取异步IO所有权
- F_GETLK、F_SETLK、F_SETLKW设置获取记录锁
getcwd函数
getcwd用于获取进程的当前工作目录。
chdir函数
chdir用于改变进程的当前工作目录。
opendir函数
opendir用于打开一个目录文件。
readdir函数
readdir用于读取目录文件中的目录项。
closedir函数
closedir用于关闭打开的目录文件。
参考资料
- 《APUE》
- 《Linux内核设计与实现》
__EOF__

本文链接:https://www.cnblogs.com/hickey2048/p/15834979.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话