在Unix和类Unix的操作系统中,档案描述符是进程唯一的标识符,专门用来处理档案或其他输入/输出资源,如管道或网络套接字。这些档案描述符通常为非负整数值,负值则用来表示「无值」或错误条件。这项技术已成为POSIX API的一部分,对于每个Unix进程来说,除了某些守护进程之外,都应拥有三个标准POSIX档案描述符,这三个档案描述符分别对应于标准输入、标准输出和标准错误流。
在传统的Unix实现中,档案描述符索引到由内核维护的每个进程的档案描述符表,该表进而索引到系统范围内的由所有进程打开的档案的表,称为档案表。
档案表记录了档案或其他资源的打开模式,包括读取、写入、附加等模式。此外,它还索引到一个称为inode表的第三个表,该表描述了实际的底层档案。要执行输入或输出,进程通过系统调用将档案描述符传递给内核,内核将代进程存取档案。需要注意的是,进程无法直接存取档案或inode表。在Linux中,可以在路径/proc/PID/fd/下访问一个进程所打开的档案描述符,其中PID是进程识别码。
/proc/PID/fd/0是标准输入,/proc/PID/fd/1是标准输出,/proc/PID/fd/2是标准错误输出。
在显示的目录下,任何运行中的进程也可以通过/proc/self/fd和/dev/fd文件夹访问自己的档案描述符。 Unix类系统中的档案描述符可以指向档案系统中任何命名的Unix档案类型,这不仅包括普通档案,还涉及目录、区块设备和字符设备(也称为特殊档案)、Unix域套接字和命名管道。
档案描述符还可以指向一些并非通常存在于档案系统中的对象,例如匿名管道和网络套接字。在Unix类系统中,C标准I/O库中的FILE数据结构通常包含一个低级档案描述符,该数据结构提供了额外的抽象,并被称为档案句柄。
在现代Unix类系统上,对档案描述符的操作可分为几类。大多数这些功能在<unistd.h>
标头中声明,但某些则在<fcntl.h>
标头中定义。
创建档案描述符的常用函数包括open()
、socket()
、pipe()
等。这些函数使得用户可以根据需求创建新的档案或资源以进行操作。
常见的单个档案描述符操作包括read()
、write()
、lseek()
等。这些操作允许用户读取或写入档案数据,或者修改档案的读写位置。
在需要处理多个档案描述符的场合,可以使用select()
、poll()
等函数进行监听,这在网络通讯和多进程应用中特别重要。
Unix档案描述符在许多方面表现出能力的特性。它们可以通过Unix域套接字在进程之间传递,使用sendmsg()
系统调用。然而,真正传递的是对「打开档案描述」的引用,这有状态(如档案偏移量和档案状态标志),这使得安全地使用这些档案描述符变得复杂。
Unix进程的档案描述符表是能力列表(C-list)的例子。
总体来说,档案描述符不仅是系统对资源管理的一种重要手段,同时也展现了它们在进程间协作中的核心作用。
当然,这一切激发了一个重要的问题:在未来的技术发展下,档案描述符的角色是否会随着捕获新型态的资料流和更复杂的系统架构而演变呢?