在Docker入口脚本中使用exec有什么作用?

58
3个回答

82

正如 @Peter Lyons 所说,使用 exec 会替换父进程,而不是同时运行两个进程。

这对于 Docker 来说很重要,因为需要正确地代理信号。例如,如果 Redis 在没有使用 exec 的情况下启动,将无法在 docker stop 时收到 SIGTERM 并且无法有机会正常关闭。在某些情况下,这可能导致数据丢失或僵尸进程。

如果您启动子进程(即不使用 exec),则父进程变成负责处理和转发信号的责任人。这是在容器中运行多个进程时最好使用 supervisord 或类似工具的原因之一,因为它将适当地转发信号。


感谢您对 Docker 的进一步见解! - m0meni
4
这非常重要。 - atamanroman
@adrianmouat 在 Windows 容器中,exit "$@" 的替代方式是什么? - kat1330
@kat1330,您的意思是exec "$@"吗?我没有太多关于Windows容器的经验 - 我猜您可以查看一下PowerShell,但如果您能在容器中使用WSL,那么它可能会像现在这样工作。 - Adrian Mouat
1
我有些困惑,如果父进程负责处理和转发信号给子进程,那么如果Redis没有使用exec启动,为什么它不会接收到SIGTERM信号呢?难道不应该由PID 1的父Shell将信号转发给Redis吗? - oeter
基本上不行。PID 1进程 - 父shell - 必须捕获信号并将其传递给任何子进程。它不会自动传递。使用一些bash脚本编写测试以验证此行为相当容易。 - Adrian Mouat

27

没有使用 exec 命令,父进程会等待子进程结束后才退出。而有了 exec 命令,子进程会完全替换掉父进程,这样当父进程在分叉出子进程后没有其他任务可执行时,exec 命令可以被认为是略微更加精确/正确/高效的做法。就整体来看,我认为将其归类为轻微优化应该是安全的。

没有使用 exec 命令:

  • 启动父 shell 进程
  • 父 shell 分叉子进程
    • 子进程运行
    • 子进程退出
  • 父 shell 退出

使用 exec 命令:

  • 启动父 shell 进程
  • 父 shell 分叉子进程,用子进程替换自己
  • 子程序运行取代 shell 的进程
  • 子进程退出

通常情况下会创建一个新的 shell,但是 exec 命令表示在当前 shell 中运行它? - m0meni
2
不,只有一个shell,但是与其分叉子进程、等待子进程退出,然后走到“哦,看起来我已经到了脚本文件的末尾,没有什么可做的了”,然后退出,exec版本基本上是“预先退出”父shell,因此当子进程接管时,它已经消失了。但是我必须让更深入的Unix开发人员来解释这个舞蹈的精确机制。 - Peter Lyons
6
我认为这比“小优化”更加重要,因为父进程负责处理信号。我在我的回答中写了��多相关内容。 - Adrian Mouat

3
把它看作像尾递归一样的优化。
如果运行另一个程序是shell脚本的最终行为,那么没有必要让shell在一个新进程中运行该程序并等待其完成。使用exec,shell进程会将自身替换为该程序。
在任何一种情况下,shell脚本的退出值都将是相同的1。无论最初调用shell脚本的程序将看到一个等于exec程序的退出值的退出值(或者如果找不到程序,则为127)。 1模除角落情况,例如程序根据其父级名称执行不同的操作。

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