为什么在UNIX中只分配最低可用的文件描述符?

3
在一个群聊中,我被这个问题吸引了 - 为什么UNIX标准要保证为进程分配的只有最低可用文件描述符?我能想到的唯一可能的答案是可扩展性。因为我们总是选择最小可用描述符,描述符位图的使用部分大多数是密集的,因此数组的增长速度较慢。我在想是否还有其他原因我不知道的。此外,如果我们知道一个给定的描述符比另一个更大/更小,是否存在我们可以在程序中使用的逻辑结论的某些情况?尽管我的理解不允许使用这种技术,因为它不能保证描述符的年龄。
3个回答

6

有各种各样的原因,但最终的原因是“因为一直都是这样做的”。

  1. 通过文件描述符列表很容易找到第一个未使用的。
  2. 它是确定的。在dup2()调用可用之前,这非常重要。

传统上,进程的文件描述符表是固定大小且相当小的(在第7版Unix中为20,如果我没记错的话)。

确定性机制对于Shell中的I/O重定向至关重要。例如:

cat file1 file2 > file3

该 shell 需要将标准输出重定向到 file3。因此,它可以使用以下命令:

close(1);  // Standard output
if (open("file3", O_WRONLY|O_CREAT, 0666) != 1)
   …error creating file…

如果标准输入已经打开(文件描述符为0),那么系统会知道这一点,因此 open() 函数将返回文件描述符1。

现在的情况是,你无法从文件描述符的值中推断出太多信息。例如,我可以这样写:

int fd1 = open(filename, flags, mode);
int fd2 = dup2(fd1, 1024);
close(fd1);

事实上,fd2(应该)包含1024,并不能告诉你它与文件描述符3的打开顺序有何关系(下一个open()调用可能会返回文件描述符3)。


谢谢。我曾经认为,如果我们使用轮询分配,可以更快地找到“第一个未使用”的位置。 - ultimate cause
是的,但你会失去确定性,而确定性比速度更重要,特别是当表只有20个文件描述符时(我认为在Unix的早期版本中,限制可能还更低,比第7版还低)。 - Jonathan Leffler

2
如果关闭标准输入(fd 0)并打开一个新文件,则该文件将获得fd 0。(如果较低的fds没有被关闭,则对于fds 1和2,stdout和stderr同样适用。)这种可预测性非常有用,并在许多Unix程序中使用,直到dup2被标准化以便在子进程中重定向标准输入/输出/错误之前。

谢谢。那么我的问题就从这里开始。我们如何在程序中利用这种可预测性?请详细说明。 - ultimate cause
正如@johnathanleffler所指出的那样,现在你会使用dup2。但传统的方法是fork,关闭stdin,打开你想要重定向stdin的内容(或者对于守护进程,使用/dev/null),重复stdout和stderr,最后执行exec。 - rici

0
简单来说:在1970年,64KB(65536个字节)是非常多的。Unix就是在这种系统上诞生的(确切地说是在PDP-7上,它默认只有9KB,但可以升级到144KB,价格是2万美元)。
在这样的环境中,每一个比特都是宝贵的(这也解释了一些令今天被宠坏的孩子感到困惑的奇怪事情,例如使用整数来保存指针,反之亦然)。
因此,算法会尝试在非常小的表中分配第一个空闲文件描述符(当时大小是固定的)。这也将使算法更快地终止,这也很好,因为大多数计算机每秒只能执行几万到十万条指令。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接