为啥要在 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)