1. 文件 I/O
Linux/Unix 上的通用 I/O 模型,并主要关注磁盘文件的 I/O 操作
概述
所有执行 IO 的系统调用均以文件描述符为目标,包括管道、FIFO、socket、终端设备和普通文件。每个进程中的文件描述符是互不相关的。 各个进程中默认会有三个标准文件描述符:
0
标准输入
STDIN_FILENO
stdin
1
标准输出
STDOUT_FILENO
stdout
2
标准错误
STDERR_FILENO
stderr
其中 POSIX 名称定义在头文件 <unistd.h> 中。
通用 I/O
UNIX I/O 模型的特点为 I/O 的通用性概念。针对系统所有的文件,包括设备、管道、socket 等可用文件描述符表示的资源,都可以通过同一套系统调用执行 I/O 操作。
open 打开文件
#include <sys/stat.h> // 文件属性信息结构体的相关申明
#include <fcntl.h> // 文件操作的相关声明
/*
@brief 打开一个文件
@param pathname 要打开文件的路径,如果是符号链接会解引用
@param flags 位掩码,指定文件访问模式
@param mode 可忽略,标识文件的所有权限,一般在创建文件时使用
- S_IRUSR | S_IWUSR 属主的读写权限
- S_IRGRP | S_IWGRP 数组的读写权限
- S_IROTH | S_IWOTH 其它的去写权限
@return 成功返回文件描述符,失败返回 -1
*/
int open(const char *pathname, int flags, ... /* mode_t mode*/);flags 可取
以上三个必须选择一个。此外还有一下标志:
打开文件失败会返回 -1,并更新错误号 errno,可能的错误号如下所示:
read 读取文件内容
write 写入文件
close 关闭文件
lseek 改变文件偏移量
可以在文件任意位置写入,但当偏移量跨度很大的时候,这部分数据并不会占用磁盘空间,称为文件空洞。
深入探究文件 I/O
所有的系统调用均为原子操作,在编写 I/O 代码时要注意进程间可能的竞争关系。
文件控制 fcntl
文件描述符与文件的关系
对于文件,内核维护以下三个数据结构:
进程级的文件描述符表
close-on-exec 标志
文件句柄的引用,指向系统的文件表
系统级的打开文件表
每一条目是打开文件句柄,指向 i-node 表
文件偏移量
文件状态标志
文件访问模式
与信号驱动 I/O 相关的设置
文件系统的 i-node 表
文件类型
指向文件持有锁的指针
文件各种属性
上面的结构揭示了:
不同的文件描述符,若指向同一个文件句柄,则共享同一文件偏移量
文件标志位的修改也同上
close-on-exec 为进程私有
复制文件描述符
特定偏移量处的 I/O
可方便多线程应用,因为系统调用为原子操作。
分散输入和集中输出
截断文件
非阻塞 I/O
一般情况下文件 I/O 默认为非阻塞的,除非使用强制文件锁。对于管道、FIFO、套接字、设备要启用非阻塞需要通过 fcntl 函数设置 O_NONBLOCK 标志。
/dev/fd 目录
此目录为虚拟目录,其中 /dev/fd/n 对应进程打开的各个文件描述符。打开/dev/fd 目录中的一个文件等同于复制相应的文件描述符。
创建临时文件
此功能为 GNU C 提供:
文件 I/O 缓冲
文件 I/O 内核缓冲
read 和 write 系统调用在操作磁盘文件时不会直接发起磁盘访问,而是仅仅在用户空间缓冲区与内核缓冲区高速缓存之间复制数据。可大大减少与磁盘交互的时间。内核缓冲区由内核设置,不能修改。
stdio 库的缓冲
控制文件 I/O 的内核缓冲
有以下两种同步 I/O 类型:
同步 I/O 数据完整性:数据已成功全部写到磁盘,或全部从磁盘读入
同步 I/O 文件完整性:是数据完整性的超级,还将同步文件元数据
直接 I/O
通过 O_DIRECT 绕过内核缓冲区直接与磁盘 I/O 需要注意:
用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
数据传输的开始点,亦即文件和设备的偏移量,必须是块大小的整数倍
待传递数据的长度必须是块大小的整数倍
混合系统调用与库文件 I/O
最后更新于
这有帮助吗?