跳转至

系统级 I/O

[!question] 为什么要学习 Unix I/O - 帮助理解其他的系统概念: I/O 在进程的创建和执行中扮演着重要的角色, 繁殖, 进程创建又在不同进程间的文件共享中扮演着关键角色. 因此, 要真正理解 I/O, 必须要理解进程, 反之亦然. - 别无选择: 在某些重要的情况中, 使用高级 I/O 函数不太可能或不太合适: 标准 I/O 库没有提供读取文件元数据的方式, 且 I/O 库中存在的问题, 使得用它来进行网络编程非常冒险.

Unix I/O

在 linux 中, 所有的 I/O 设备都被抽象为文件 (Everything is file) , 而所有的 I/O 都会被当做对相应的文件的读和写来执行. 这种将设备优化地映射为文件的方式, 允许 Linux 内核引出一个简单且低级的应用接口, 这些接口称为 Unix I/O, 这为所有的输入和输出以一种统一的方式来进行: - 打开文件: 应用程序通过要求内核打开相应的文件, 来告诉内核它要访问一个 I/O 设备. 内核向这个应用程序返回一个称为描述符的非负整数, 描述符用于在后续对文件的操作中标识这个文件且描述符是唯一的, 应用程序只需要记住这个描述符, 就可以对 I/O 设备进行访问, 而内核负责记录有关这个打开文件的所有信息. - Linux shell 创建的每个进程开始时都有 3 个打开的文件: - 标准输入 (描述符 0) -> STDIN_FILENO - 标准输出 (描述符 1) -> STDOUT_FILENO - 标准错误 (描述符 2) -> STDERR_FILENO - 改变当前的文件位置: 对于每个打开的文件, 内核维护一个文件位置 k, 且初始值为 0. 文件位置标识了从文件开头起始的字节偏移量, 应用程序可以通过执行 seek 操作, 显式的修改 k - 读写文件: - 读: 从文件中复制 n 个字节到内存, 范围 k ~ k + n - 对于大小为 m 字节的文件, 当 k >= m 时执行读操作会触发 end-of-file (EOF)的条件, 应用程序可以检测到这个条件. 文件中并没有存储 EOF. - 写: 从内存复制 n 个字节到文件的 k 位置, 然后更新 kk + n - 关闭文件: 当应用程序完成了对文件的访问过后, 就通知内核关闭这个文件. 内核释放文件打开时候所创建的所有数据结构, 并释放描述符到可用的描述符池中 - 无论进程因为什么原因终止, 内核都会关闭所有打开的文件并释放其内存资源

文件