# 10.1 Unix I/O

一个 Linux 文件就是一个 m 个字节的序列：

$$
B_0,B_1,\cdots,B_k,\cdots,B_{m-1}
$$

所有的 I/O 设备（例如网络、磁盘和终端）都被模型化为文件，而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式，允许 Linux 内核引出一个简单、低级的应用接口，称为 Unix I/O，这使得所有的输入和输出都能以一种统一且一致的方式来执行：

* **打开文件。**&#x4E00;个应用程序通过要求内核打开相应的文件，来宣告它想要访问一个 I/O 设备。内核返回一个小的非负整数，叫做描述符，它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。
* Linux shell 创建的每个进程开始时都有三个打开的文件：**标准输入**（描述符为 0）、**标准输出**（描述符为 1）和**标准错误**（描述符为 2）。头文件 **\<unistd.h>** 定义了常量 STDIN\_FILENO、STDOUT\_FILENO 和 STDERR\_FILENO，它们可用来代替显式的描述符值。
* **改变当前的文件位置。**&#x5BF9;于每个打开的文件，内核保持着一个文件位置 k，初始为 0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行 seek 操作，显式地设置文件的当前位置为 k。
*   **读写文件。**&#x4E00;个读操作就是从文件复制 n > 0 个字节到内存，从当前文件位置 k 开始，然后将 k 增加到 k+n。给定一个大小为 m 字节的文件，当$$\small k\geqslant m$$时执行读操作会触发一个称为 end-of-file（EOF）的条件，应用程序能检测到这个条件。在文件结尾处并没有明确的 “EOF 符号”。

    类似地，写操作就是从内存复制 n > 0 个字节到一个文件，从当前文件位置 k 开始，然后更新 k。
* **关闭文件。**&#x5F53;应用完成了对文件的访问之后，它就通知内核关闭这个文件。作为响应，内核释放文件打开时创建的数据结构，并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时，内核都会关闭所有打开的文件并释放它们的内存资源。
