在 Linux 系统中,进程无法直接查看是谁发送了信号(如 SIGTERM
、SIGHUP
等),因为信号机制本身不记录发送者的身份。但可以通过以下方法间接排查信号来源:
1. 使用 auditd
监控信号发送(系统级)
通过审计工具 auditd
跟踪所有信号的发送行为:
# 添加审计规则(监控所有 kill()/tkill() 调用)
sudo auditctl -a always,exit -F arch=b64 -S kill -S tkill -S tgkill
# 查看日志(需 root)
sudo ausearch -sc kill,tkill,tgkill | grep "pid=目标进程PID"
- 输出示例:
其中type=SYSCALL ... pid=1234 uid=1000 auid=1000 comm="bash" sig=SIGTERM target_pid=5678
comm="bash"
是发送信号的进程名,uid
/auid
是用户信息。
2. strace
动态跟踪进程
对目标进程实时跟踪系统调用(适合调试场景):
strace -p <目标PID> -e trace=signal,kill,tkill,tgkill
- 输出示例:
tgkill(5678, 5678, SIGTERM) = 0 # PID为1234的进程向5678发送了SIGTERM
3. /proc/<PID>/status
& /proc/<PID>/wchan
检查进程状态和阻塞点:
cat /proc/<目标PID>/status | grep SigBlk # 查看阻塞的信号
cat /proc/<目标PID>/wchan # 查看当前等待点(可能关联信号)
**4. 自定义信号处理函数(编程捕获)
在程序内部捕获信号并记录上下文(如时间、调用栈等):
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void handler(int sig, siginfo_t *info, void *ucontext) {
printf("Received signal %d from PID=%d (UID=%d)\n",
sig, info->si_pid, info->si_uid);
}
int main() {
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction(SIGTERM, &sa, NULL); // Example for SIGTERM
while(1) pause(); // Wait for signals
}
- 关键字段:
siginfo_t.si_pid
: 发送者进程的 PID(仅适用于kill()
/tkill()
)。
⚠️ 注意:
si_pid
仅在同步信号时有效(如通过kill()
)。如果是终端关闭产生的SIGHUP
,则不会记录。- Root用户或相同 UID的进程可以互相隐藏真实 PID。
5. SystemTap/eBPF高级追踪
编写脚本监听内核事件(需root权限):
(a) SystemTap示例
stap -e 'probe kernel.function("__send_signal") {
printf("Signal %d sent to %d by %s(%d)\n",
$sig, $t->pid, execname(), pid());
}'
(b) eBPF/bcc工具
sudo ./trace.py 'do_send_sig_info "sig=%d pid=%d comm=%s" arg1,target_task->pid,target_task->comm'
常见信号来源总结
信号类型 | 典型触发源 |
---|---|
SIGHUP |
Terminal关闭、会话首进程退出 |
SIGINT/SIGQUIT |
Ctrl+C/Ctrl+\ (来自终端) |
SIGKILL/SIGSTOP |
Kernel或root用户强制操作 |
SIGUSR1/2 |
User-defined (其他进程显式发送) |
限制与注意事项
- 匿名性:
- Root用户可通过
kill(-1)
广播信号,此时无法追踪具体来源。 - Kernel触发的信号无明确发送者。
- Root用户可通过
- 容器环境:
在Docker/K8s中,需检查宿主机日志或审计。
如果需要针对特定场景进一步分析,可以提供更多细节!
内容由零声教学AI助手提供,问题来源于学员提问