Blog·Tanky WooABOUTTAGSRSS

场景

(适当修改了,方便简单模拟)

daemontools管理一个服务serviceA, run脚本:

#!/bin/bash

exec /opt/srv/mysrv.sh

/opt/srv/mysrv.sh脚本:

#!/bin/bash

# other statements...

ping tankywoo.com >> /tmp/srv.log

这里ping命令用来模拟一直在前台运行的程序。

使用svc -d停止当前服务,发送的是TERM信号。(参考)

因为run脚本用到exec,mysrv.sh脚本停止了,但是ping命令的父进程变为1(init),然后继续执行。

解决

根本原因是信号没有传递到子进程中。

参考这几个帖子:

使用trap命令来指定在接收到信号后做相应的动作。 (可以看看这篇)

#!/bin/bash

# other statements...

_term() {
    echo "[`date`] Caught SIGTERM in mysrv.sh!" >> /tmp/srv.log
}

trap _term TERM

ping tankywoo.com >> /tmp/srv.log

kill -TERM <pid_of_run>时,子shell并不会收到TERM信号。

换个更简单的例子,run脚本:

#!/bin/bash

ping tankywoo.com >> /tmp/srv.log

直接执行run脚本,ping命令是当前shell的一个子shell,如果发送TERM信号,则ping变为父进程是1的进程了。

所以需要加上exec,这也是为何第一个场景里需要exec /opt/srv/mysrv.sh

所以这里的最简单的解决办法就是在/opt/srv/mysrv.sh中也使用exec:

#!/bin/bash

# other statements...

exec ping tankywoo.com >> /tmp/srv.log

另外,How to propagate SIGTERM to a child process in a Bash script这篇文章总结的很好。使用的是trapwait的用法。 (建议好好读一读)

/opt/srv/mysrv.sh改为:

#!/bin/bash

# other statements...

trap 'kill -TERM $PID' TERM INT

ping tankywoo.com >> /tmp/srv.log &   # 注意这行,将ping命令改为后台执行

PID=$!   # 获取上一个放入后台执行的pid
wait $PID  # 等待指定pid的后台进程运行完毕
trap - TERM INT  # 恢复TERM和INT信号的默认行为
wait $PID
EXIT_STATUS=$?

# other statements...

至于为何要两次wait,我还没弄太明白。