1. 文件 I/O

Linux/Unix 上的通用 I/O 模型,并主要关注磁盘文件的 I/O 操作

概述

所有执行 IO 的系统调用均以文件描述符为目标,包括管道、FIFO、socket、终端设备和普通文件。每个进程中的文件描述符是互不相关的。 各个进程中默认会有三个标准文件描述符:

文件描述符
用途
POSIX 名称
stdio 流

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

最后更新于

这有帮助吗?