在 Unix 和类 Unix 操作系统中,档案描述符(FD)是每个进程的唯一识别码,用于识别档案或其他输入/输出资源,如管道或网路套接字。这些描述符通常使用非负整数值,大多数情况下,负值则表示「无值」或错误条件。档案描述符是POSIX API 的一部分,所有Unix 进程通常都需要三个标准的POSIX 档案描述符,这些描述符对应于三个标准流:标准输入(stdin)、标准输出(stdout)和标准错误(stderr )。
每个Unix 进程的档案描述符都对应一个由内核维护的进程档案描述符表,这表格又索引到所有进程共用的一个系统级文件表,该表记录了档案被打开的模式,例如:读取、写入及附加等模式。
档案描述符的使用是执行输入和输出的基础。当进程需要进行这些操作时,它会透过系统调用将档案描述符传递给内核,内核则代表进程访问档案。进程无法直接访问档案或索引表。对于 Linux 系统,进程中打开的档案描述符可以在路径 /proc/PID/fd/
下找到,这里的 PID
是进程识别码。
例如,档案描述符/proc/PID/fd/0
对应标准输入,/proc/PID/fd/1
是标准输出,而 /proc/PID/fd/2
则是标准错误。任何正在运行的进程也可以透过 /proc/self/fd
和 /dev/fd
这两个资料夹来访问自身的档案描述符。
在现代类 Unix 系统上,档案描述符的操作非常多样。通常这些操作包含创建档案描述符、衍生档案描述符,以及针对单一或多个档案描述符进行的操作等。以下是一些基本的档案描述符操作:
创建档案描述符的典型方法包括:
open()
creat()
socket()
pipe()
可以通过以下方法衍生档案描述符:
dirfd()
fileno()
对档案描述符进行操作例如:
read()
write()
lseek()
处理多个档案描述符的操作包括:
select()
poll()
这些操作不仅使得档案描述符成为优雅的功能接口,也简化了进程间的通信。
Unix 档案描述符在许多方面表现得像是能力(capabilities)。它们可以透过 Unix 域套接字在进程间传递,这使得资源的共享变得灵活。但要注意的是,这会导致可变状态的问题,因为多个进程之间共享同一个打开档案的描述符时,可能会互相干扰。
随着 Unix 和类 Unix 系统的演进,许多新的操作和库已被增添,这些操作的目的是防止某类特定的 TOCTOU 攻击。这些操作包括 openat()
和其他相关操作。
了解档案描述符的运作原理不仅对程式员来说至关重要,对于每一个使用类 Unix 系统的人来说都十分必要。它们是进程间交流的关键桥梁,为整个系统的运行提供了基础架构。随着科技的快速发展,这个曾被认为基础的概念或许会引发新的思考与挑战。在这样的情况下,我们不禁要问,未来的档案描述符将朝着何处演进,并如何影响我们的计算和沟通方式?