在调试logd守护进程时,概率性出现/etc/init.d/logd stop无法停止的问题。stop是通过发送SIGTERM命令实现的。测试发现通过SIGINT信号可以杀死logd。
由于测试过程中,需要重新编译,然后覆盖旧的bin文件,覆盖bin是strip命令-o选项覆盖的,以为是覆盖bin导致当前可执行段发生异常导致。后面调试过程中发现,不重新编译也会复现问题。
怀疑是信号没有传递到logd,使用gdb分析,发现可以接收到信号,修改gdb调试,让SIGTERM传递给程序,发现还是杀不死。
$ (gdb) handle SIGTERM stop pass
$ info handle
以为是代码的问题,以为poll会导致问题,注释代码,直接while (1) sleep(1); 发现还是有问题,又注释了其它代码,发现还是有问题。
最后找到复现方法,第一次执行/etc/init.d/start,可以stop。如果执行/etc/init.d/restart拉起来就停不掉了。
怀疑是SIGTERM信号被忽略了。添加调试信息,发现确实被忽略了。
struct sigaction act;
int ret = sigaction(SIGTERM, NULL, &act);
printf("sigaction ret %d handler %p flag %d\n",
ret, act.sa_handler, act.sa_flags &SA_SIGINFO);
以为是sh忽略了SIGTERM,导致被拉起的守护进程继承了这个忽略。所以写个小程序来测试,发现没有被忽略。
那就很明朗了。是/etc/init.d/logd的问题,它的restart是在/etc/rc.common定义的。查看代码
restart() {
trap '' TERM
stop "$@"
start "$@"
}
TERM被忽略了。这个是从openwrt复制过来的,查看openwrt的提交记录:
commit 2ac1a57677ce4e21513dca2a8efab1eb6e0a9c58
Author: Linus Kardell <linus@telliq.com>
Date: Thu Nov 22 11:35:08 2018 +0100
base-files: fix unkillable processes after restart
When restart is run on an init script, the script traps SIGTERM. This is
done as a workaround for scripts named the same name as the program they
start. In that case, the init script process will have the same name as
the program process, and so when the init script runs killall, it will
kill itself. So SIGTERM is trapped to make the init script unkillable.
However, the trap is retained when the init script runs start, and thus
processes started by restart will not respond to SIGTERM, and will thus
be unkillable unless you use SIGKILL. This fixes that by removing the
trap before running start.
Signed-off-by: Linus Kardell <linus@telliq.com>
diff --git a/package/base-files/files/etc/rc.common b/package/base-files/files/etc/rc.common
index 3e237170b4..d9773a0c5a 100755
--- a/package/base-files/files/etc/rc.common
+++ b/package/base-files/files/etc/rc.common
@@ -23,6 +23,7 @@ reload() {
restart() {
trap '' TERM
stop "$@"
+ trap - TERM
start "$@"
}
定位到问题:
原来在脚本执行restart的时候,stop的时候会去杀死进程。如果init.d脚本和进程同名。
比如/etc/init.d/logd和守护程序logd同名。在/etc/init.d/logd里面执行killall logd。那么可能把脚本自己给杀死了。因此就添加 trap ' ' TERM,来让脚本忽略TERM信号。但是在start的时候没有修改,导致新拉起的守护进程继承了,也是忽略,从而无法杀死。
修改方法就是在start之前,把TERM重新修改成DFL。
参考:
https://stackoverflow.com/questions/33890418/find-signal-handler-function-using-gdb