为啥要在 bash 中休眠和等待?

Posted

技术标签:

【中文标题】为啥要在 bash 中休眠和等待?【英文标题】:Why do sleep & wait in bash?为什么要在 bash 中休眠和等待? 【发布时间】:2019-11-20 17:47:37 【问题描述】:

我无法理解此docker-compose.yml 中服务的启动命令。 .yml 中的两个相关行是:

command: "/bin/sh -c 'while :; do sleep 6h & wait $$!; nginx -s reload; done & nginx -g \"daemon off;\"'"

entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $$!; done;'"

为什么要将sleep 命令发送到后台然后等待呢?为什么不直接做sleep 6h?另外,双美元符号是否只是在 $! 中转义美元符号?

我正在寻找其他结合使用睡眠和等待的地方,但似乎没有任何解释原因:

    http://www.masteringunixshell.net/qa17/bash-how-to-wait-seconds.html https://***.com/a/13301329/828584 https://superuser.com/a/753984/98583

【问题讨论】:

见***.com/questions/13296863/… @LinpPy,但这些都没有解释这一点。 sleep 10 & wait $!sleep 10 有什么区别。如果你只是要等待 sleep 命令,为什么要让它成为后台进程? @Edvin 不,不是。这并不能解释为什么你会同时使用后台睡眠和等待,而不仅仅是前台睡眠。提问者已经知道每个部分分别做了什么,只是不知道为什么要这样组合它们。 哦。我刚刚得到你 只是一个猜测:等待时间很长,所以我不认为在正常情况下,我们希望进程真的应该等待几个小时。这意味着在正常情况下,某人(可能是管理 docker 的进程)必须做一些工作,如果它完成了,它希望你想用 docker-compose 运行的进程继续 .让进程继续可以通过杀死睡眠进程来完成。 【参考方案1】:

当人们想要及时处理信号时,在后台休眠然后等待是有意义的。

当 bash 在前台执行外部命令时,它确实 直到前台进程才处理接收到的任何信号 终止

(详细解释here)。

虽然第二个示例实现了一个信号处理程序,但对于第一个示例,睡眠是否在前台执行都没有区别。没有陷阱并且信号不会传播到nginx 进程。 为了让它响应SIGTERM 信号,入口点应该是这样的:

/bin/sh -c 'nginx -g \"daemon off;\" & trap exit TERM; while :; do sleep 6h  & wait $$!; nginx -s reload; done'

测试它:

docker run --name test --rm --entrypoint="/bin/sh" nginx  -c 'nginx -g "daemon off;" & trap exit TERM; while :; do sleep 20 & wait $!; echo running; done'

停止容器

docker stop test

或者发送TERM信号(如果主进程没有退出,docker stop发送TERM后跟KILL

docker kill --signal=SIGTERM test

通过这样做,脚本会立即退出。现在,如果我们删除wait $!,则当sleep 结束时会执行陷阱。所有这些都适用于第二个示例。

注意:在这两种情况下,目的是每 12 小时检查一次证书更新,并每 6 小时重新加载一次配置,如 guide 中所述 这两个命令做得很好。恕我直言,第一个示例中的额外等待只是开发人员的疏忽。

已编辑:

似乎上面的合理化是为了给出后台睡眠背后的可能原因,可能会造成一些混乱。 (有一个相关的帖子Why use nginx with “daemon off” in background with docker?)。

虽然上面答案中建议的命令是对问题中的命令的改进,但它仍然存在缺陷,因为如链接帖子中所述,nginx 服务器应该是主进程而不是子进程。这可以使用exec 系统调用轻松实现。脚本变为:

'while :; do sleep 6h; nginx -s reload; done & exec nginx -g "daemon off;"'

(更多信息参见Docker best practices 中的将应用程序配置为 PID 1 部分)

恕我直言,这要好得多,因为nginx 不仅受到监控,而且还处理信号。例如,配置重新加载(nginx -s reload)也可以手动完成,只需将HUP 信号发送到 docker 容器(参见Controlling nginx)。

【讨论】:

如果是这样,当wait 是前台进程而不是sleep 时,这不是仍然成立吗? wait 是一个可以被打断的shell builtin。 sleep 是一个命令,并作为外部进程执行。睡眠和等待的目的是让sleep 可中断。【参考方案2】:

我看到的唯一原因:

如果你killall -INT sleep,这不会影响主脚本。

试试这个:

while true ;do sleep 12; echo yes;done

然后发送 Interrupt 信号:

killall -INT sleep

这会破坏工作!

立即尝试

while true ;do sleep 12 & wait $! ; echo yes;done

再说一遍:

killall -INT sleep

工作不会中断!

示例输出,从另一个窗口点击killall -INT sleep

user@myhost:~$ while true ;do sleep 12; echo yes;done
break

user@myhost:~$ while true ;do sleep 12 & wait $! ; echo yes;done
[1] 30632
[1]+  Interrupt               sleep 12
yes
[1] 30636
[1]+  Interrupt               sleep 12
yes
[1] 30638
[1]+  Interrupt               sleep 12
yes
[1] 30640

【讨论】:

我在脚本中试过这个:sleep 60; echo second line。执行该脚本并从另一个终端执行killall sleep。它没有杀死脚本。我确实得到了第二行的回声。也许你是对的,但也许它取决于系统。 @F.Hauri :anishsane 是对的:杀死一个进程并不会杀死父进程。如果是这样,那就太奇怪了。 我试图重现它,但我不能。通过我的测试,在这两种情况下,只有 sleep 被杀死。顺便说一句,即使在你的系统上,你应该能够在没有wait 的情况下做到这一点:如果你在子shell 中运行睡眠,即(sleep 12),killall 是否仍然会杀死你的父母? @user1934428 我是INT 信号的一个功能!你必须killall -INT sleep 我使用 SIGINT 杀死了,但在您的“后台”案例中,我只收到“中断”消息,并且循环继续。

以上是关于为啥要在 bash 中休眠和等待?的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核中休眠与唤醒的使用(wait_eventwake_up)

Linux内核中休眠与唤醒的使用(wait_eventwake_up)

Linux内核中休眠与唤醒的使用(wait_eventwake_up)

在执行过程中休眠一个线程

在 JavaScript 中休眠 - 动作之间的延迟

为啥要在hibernate中使用关联?