跨 exec 的文件描述符

r.v*_*r.v 8 c file-descriptors exec

默认情况下,文件描述符在 exec 函数中保持打开状态。对于描述符 0-2 的好处也许是可以理解的。但是是否有保持其他描述符打开的实际用例?是否有任何真正的应用程序依赖于这个事实?

Sté*_*las 12

如果您不希望将该 fd 传递给已执行的命令,则可以在文件描述符上设置一个标志(在open(): O_CLOEXEC 或更高版本上使用fcntl():FD_CLOEXEC)。

如果您要执行命令,这就是您应该为内部文件描述符做的事情。

例如,在 shell 中,这就是ksh93您所做exec 3< some-file的。对于用 开启的其他 shell 或 fds { cmd1; cmd2; } 3< file,如果您不想cmd1cmd2访问该 fd: ,则需要手动关闭{cmd1 3<&-; cmd2; } 3< file这是一种很好的做法,但并不总是遵循,因为如果您不这样做,通常并不重要

现在,该功能是否有用。是的,有几个命令依赖于它。

一些命令将文件描述符作为参数,这意味着已被调用者打开。想到的几个例子:

  • xterm与它的-S选择
  • qemu 对于各种事情
  • flock (在调用者的 fd 上锁定文件)
  • test又名命令[它的-t选项(OK的test程序内置在大多数类似Bourne外壳的今天,但仍然是一个test可以执行的命令)。
  • dialog 需要文件描述符用于来自用户的输入、向用户的输出和错误以及向调用者的输入和输出,因此您可以为此使用额外的 fds。
  • gpg或者openssl您可以指定一个文件描述符来传递密码或其他信息。

有许多辅助实用程序(例如,执行的需要可能是使用 setuid/setgid 可执行文件以不同用户或组的身份运行命令的一部分)依赖于此。

进程替换依赖于它:

在, diff <(cmd1) <(cmd2), 2 个文件描述符(到管道)被传递给diff并且 diff 通过作为参数传递的特殊 /dev/fd/n 打开它们来访问它们。

对于没有进程替换的 shell,您可以手动执行以下操作:

cm1 | { cmd2 | diff /dev/fd/3 -; } 3<&0
Run Code Online (Sandbox Code Playgroud)


小智 5

TinyMUSH 以及可能它的许多同级和子代码库都使用 exec 的此功能来达到很好的效果。人们可以发出命令来重新启动服务器,可能会升级到全新的二进制文件,同时保持用户连接。

这是通过写入有关每个连接用户的信息的小数据库(包括他们的文件描述符)来完成的。新执行的 TinyMUSH 副本会读取重新启动数据库,以恢复其对连接用户的了解,并从中断的地方继续。

最终结果:新功能发布,用户只能看到短暂的暂停。

Nginx 所做的事情有点类似于在不丢失连接的情况下进行二进制升级。