对于每个进程,内核都提供有一个特殊的虚拟目录/dev/fd。该目录中包含"/dev/fd/n"形式的文件名,其中n是与进程中打开文件描述符相对应的编号。也就是说,/dev/fd/0就对应于进程的标志输入。
打开/dev/fd目录中的一个文件等同于复制对应的文件描述符,所以下面两行代码是等价的:
fd = open("/dev/fd/1", O_WRONLY);
fd = dup(1);
在为 open()调用设置 flag 参数时,需要注意将其设置为与原描述符相同的访问模式。这一场景下,在 flag 标志的设置中引入其他标志,诸如 O_CREAT,是毫无意义的(系统会将其忽略)。
/dev/fd实际上是一个符号链接,链接到Linux专有的/proc/self/fd目录。后者又是Linux特有的/proc/PID/fd目录族的特例之一,此目录族中每一个目录都包含有符号链接,与一进程所打开的所有文件相对应
程序中很少会使用/dev/fd 目录中的文件。其主要用途在 shell 中。许多用户级 shell 命令将文件名作为参数,有时需要将命令输出至管道,并将某个参数替换为标准输入或标准输出。出于这一目的,有些命令(例如,diff、ed、tar 和 comm)提供了一个解决方法,使用“-”符号作为命令的参数之一,用以表示标准输入或输出(视情况而定)。所以,要比较 ls 命令输出的文件名列表与之前生成的文件名列表,命令就可以写成
$ ls | diff - oldfilelist
这种方法有不少问题。首先,该方法要求每个程序都对“-”符号做专门处理,但是许多程序并未实现这样的功能,这些命令只能处理文件,不支持将标准输入或输出作为参数。其次,有些程序还将单个“-”符解释为表征命令行选项结束的分隔符。
使用/dev/fd 目录,上述问题将迎刃而解,可以把标准输入、标准输出和标准错误作为文件名参数传递给任何需要它们的程序。所以,可以将前一个 shell 命令改写成如下形式
$ ls | diff /dev/fd/0 oldfilelist
方便起见,系统还提供了 3 个符号链接:/dev/stdin、/dev/stdout 和/dev/stderr,分别链接到/dev/fd/0、/dev/fd/1 和/dev/fd/2。
运用虚拟目录/dev/fd 中的编号文件,进程就可以通过文件描述符编号来访问自己打开的文件,这在 shell 命令中尤其有用