默认情况下,文件描述符在 exec 函数中保持打开状态。对于描述符 0-2 来说,这种好处也许是可以理解的。但是是否有保持其他描述符打开的实际用例?有没有任何实际的应用程序依赖于这个事实?
答案1
如果您不希望将 fd 传递给已执行的命令,则可以在文件描述符上设置一个标志(在open()
:O_CLOEXEC 或稍后使用:FD_CLOEXEC)。fcntl()
如果您要执行命令,这就是您应该对内部文件描述符执行的操作。
例如,在 shell 中,这就是ksh93
您所做的事情。exec 3< some-file
对于使用打开的其他 shell 或 fd ,如果您不想或访问该 fd: ,{ cmd1; cmd2; } 3< file
则需要手动关闭。cmd1
cmd2
{cmd1 3<&-; cmd2; } 3< file
这是很好的做法,但并不总是遵循,因为如果您不这样做,通常并不重要。
现在,该功能是否有用。是的,有几个命令依赖于它。
一些命令采用文件描述符作为参数,该文件描述符意味着已由调用者打开。我想到的几个例子:
xterm
及其-S
选项qemu
对于各种事情flock
(锁定调用者 fd 上的文件)- 命令
test
又名[
它的-t
选项(好吧,test
现在大多数类似 Bourne 的 shell 中都内置了该实用程序,但仍然有一个test
可以执行的命令)。 dialog
需要文件描述符用于用户的输入、用户的输出和错误以及调用者的输入和输出,因此您可以为此使用额外的 fd。gpg
或者openssl
您可以指定一个文件描述符来传达密码或其他信息。
有许多帮手实用程序(例如,需要执行可能是使用依赖于此的 setuid/setgid 可执行文件以不同的用户或组身份运行命令的一部分。
进程替换依赖于它:
在 中diff <(cmd1) <(cmd2)
,2 个文件描述符(到管道)被传递到diff
并 diff 通过通过作为参数传递的特殊 /dev/fd/n 打开它们来访问它们。
对于没有进程替换的 shell,您可以手动执行以下操作:
cm1 | { cmd2 | diff /dev/fd/3 -; } 3<&0
答案2
TinyMUSH 以及可能它的许多同级和子代码库都使用 exec 的此功能来达到很好的效果。人们可以发出命令来重新启动服务器,可能会升级到全新的二进制文件,同时保持用户连接。
这是通过写入有关每个连接用户的信息的小数据库(包括他们的文件描述符)来完成的。新执行的 TinyMUSH 副本会读取重新启动数据库,以恢复其对连接用户的了解,并从中断的地方继续。
最终结果:新功能发布,用户只能看到短暂的暂停。
Nginx 所做的事情有点类似于在不丢失连接的情况下进行二进制升级。
答案3
连接的套接字可以通过这种方式传递给子进程,因此例如接受传入连接的网络服务器可以将处理它们完全传递给另一个进程。
请参阅inetd 的源代码,举一个普遍存在的例子。
答案4
我不认为有真正的通用程序可以传递描述符 2 之外的任何内容。如果确实如此,则 exec()ed 程序将不得不假设硬编码的文件描述符编号,例如
write(3, ...);
或者
fp = fdopen(3, "r");
这是不好的编码习惯。只有在您严格控制父程序和子程序并且没有其他人可以干预的情况下,它才有意义。