在 Unix 和类 Unix 操作系统中,档案描述符(FD)作为一种独特的识别符,用来标识文件或其他输入/输出资源。它们使系统中的文件操作变得更加方便和灵活。
档案描述符通常由一个非负整数值组成,负值则被保留用以指示「没有值」或错误条件。
每个 Unix 过程至少会有三个标准的 POSIX 档案描述符,分别对应于三个标准流:标准输入、标准输出和标准错误。这种设计不仅影响了程序的性能,还影响了用户的操作方式。
在传统的 Unix 实现中,档案描述符是指向一个由内核维护的每个过程的档案描述符表的索引,这又索引到一个系统范围的由所有过程开启的文件表中。这个表记录了文件的打开模式:读取、写入、附加,甚至可能有其他模式。
为了执行输入或输出,过程将档案描述符通过系统调用传递给内核,内核将会代表过程访问文件。
因此,过程并不直接访问文件或 inode 表。在 Linux 系统中,过程打开的档案描述符可以通过路径 /proc/PID/fd/
访问,其中 PID 是过程识别符。每个档案描述符分别代表标准输入(/proc/PID/fd/0
)、标准输出(/proc/PID/fd/1
)和标准错误( /proc/PID/fd/2
)。
在 Unix 类系统中,档案描述符可以指向文件系统中命名的任何 Unix 文件类型。这不仅包括常规文件,还包括目录、块设备和字符设备(也称为「特殊文件」)、Unix 域套接字和命名管道。档案描述符还可以参考其他通常不在文件系统中存在的对象,如匿名管道和网络套接字。
在 C 标准 I/O 库中,FILE 数据结构通常包括所涉及对象的低级档案描述符。
这意味着复杂的抽象和底层操作已经被封装进入了更加易于使用的接口中,这就是我们所说的档案句柄。
以下是现代 Unix 类系统中针对档案描述符的一些典型操作。大多数这些函数在 <unistd.h>
标头中声明,但某些则在 <fcntl.h>
标头中。
开启档案描述符的几种方式包括:
纠正或获取档案描述符的状态的各类函数包括:
为了有效管理多个描述符,您可以使用:
这使得处理并发连接和事件成为可能。
例如:
Unix 系统还提供档案锁定和套接字操作的能力,包括:
档案描述符在许多方面类似于能力。通过 Unix 域套接字进行的 sendmsg()
系统调用允许描述符在过程之间进行传递,这为资源的安全共享提供了潜力。
但是,这种能力的安全性受到潜在可变状态的影响。
因为一旦程序共享同一档案描述符的访问,就可能会干扰彼此的使用,这使得使用档案描述符作为能力变得复杂。
随着 UNIX 系统不断演化,许多新操作也在被标准化,以提供更好的保护和灵活性。
作为使用者,您是否曾经想过档案描述符对于过程管理和数据流控制的真正影响是什么呢?