在日常工作中,服务程序(daemon)意外退出,一般是由以下几个原因导致的:
- 程序自身bug;
- 被OS oom killer杀掉;
- 操作人员误杀;
- 被第三方程序杀掉;
对于第一种情况,一般可以通过coredump看出。第二个种情况,也可以通过dmesg找到线索(一般是被oom killer杀掉)。但是当后两种情况出现时,往往是大家最抓瞎最手足无措的时候。
本文将介绍多种方法来帮助大家揭开服务(daemon)被杀之谜。
首先先创建一个daemon
#!/bin/bash
while : ; do sleep 1done
然后在后台运行这个daemon.sh。
第一个方法:ftrace
使用命令
echo 1 > /sys/kernel/tracing/events/signal/enable
使用命令killall daemon.sh杀掉daemon.sh。
然后查看ftrace的输出,cat /sys/kernel/tracing/trace
如红色箭头所指,可以清晰的看出killall命令,给进程daemon.sh(pid为3615)发送了SIGTERM(即sig=15)。
第二个方法:systemtap
编写systemtap脚本,来探测kill调用。
probe syscall.kill{ if (pid2execname(u32_arg(1)) == @1) printf("%s(%d) kill %s with (%s)n", execname(), pid(), pid2execname(u32_arg(1)), argstr);}
简单介绍一下脚本。
- 该脚本接受一个参数@1,表示要监视的服务进程名称。
- u32_arg(1)获得kill系统调用的第一个参数,也就是目标pid。然后调用systemtap的函数pid2execname得到该pid的进程名。
- 当条件为真时,表明kill要发送的目标pid就是我们要监视服务进程。 然后打印调用者的进程名称(execname)和其pid。最后使用通过systemtap的argstr,打印kill的完整参数
看一下执行效果
第三个方法:bpftrace
bpftrace是Brendan Gregg使用bpf开发的一套trace工具。对于当前问题,我们选择其中的killsnoop来trace kill调用。
重复之前的操作,可以看到killsnoop侦测到了killall发送了SIG 15也就是SIGTERM给TPID 9216,也就是我们的daemon.sh。
第四个方法:bcc
bcc与systemtap类似,是一个可以编写ebpf的工具集。除了可以直接编写ebpf程序外,它也提供了一套与bpftrace相似的现成工具,并提供了更多的选项。
对于当前的问题,也只能使用默认行为,监控所有的kill调用。但bcc的killsnoop可以trace指定pid,来追踪指定pid进程的kill调用。
第五个方法:直接编写bpftrace代码
在第三个方法中,我们直接使用bpftrace提供的现成工具,实际上我们也可以编写bpftrace来完成这个任务。
#!/usr/bin/bpftrace
tracepoint:syscalls:sys_enter_kill { if ($1 == args->pid) { printf("process %s send signal%d to pid %dn", comm, args->sig, args->pid); }}
在上面的代码中,我们根据kill这个系统调用,然后打印发送信号的进程名,信号值,以及目标pid。当目标pid与第一个参数,即我们要监控的process,就进行打印。
虽然会有一个告警,但我们这里可以忽略到。
其实除了上面的5种方法外,还会有其它方案来搞定这个问题,比如直接编写ebpf程序,等等。不过我认为上面5种方法,基本上已经可以覆盖绝大部分的场景了。
如果大家还有更好的方法,欢迎留言分享~~~