如何在 Linux 中判断哪个进程向我的进程发送了信号
Posted
技术标签:
【中文标题】如何在 Linux 中判断哪个进程向我的进程发送了信号【英文标题】:How can I tell in Linux which process sent my process a signal 【发布时间】:2011-12-04 19:38:54 【问题描述】:我有一个获得SIG TERM
的Java 应用程序。我想知道发送此信号的进程的 pid。
这可能吗?
【问题讨论】:
【参考方案1】:两种特定于 Linux 的方法是 SA_SIGINFO
和 signalfd()
,它们允许程序接收非常关于发送信号的详细信息,包括发送者的 PID。
调用sigaction()
并将struct sigaction
传递给它,它在sa_sigaction
中具有所需的信号处理程序,在sa_flags
中设置了SA_SIGINFO
标志。使用此标志,您的信号处理程序将接收 三个 参数,其中一个是包含发送者 PID 和 UID 的 siginfo_t
结构。
调用signalfd()
并从中读取signalfd_siginfo
结构(通常在某种选择/轮询循环中)。内容将类似于siginfo_t
。
使用哪一个取决于您的应用程序是如何编写的;它们可能在纯 C 之外无法很好地工作,而且我不希望让它们在 Java 中工作。它们在 Linux 之外也是不可移植的。它们也可能是您想要实现的目标的非常错误的方式。
【讨论】:
第一个提到的方法 (sigaction/SA_SIGINFO/siginfo_t) 实际上是一个 POSIX 标准,Mac OS、FreeBSD、OpenBSD 也支持,而不仅仅是 Linux。 如果有人在 gdb 下捕获到信号来搜索这个:执行p $_siginfo
,然后查看si_pid
字段,它应该包含发送进程 PID。
请注意,发件人的 pid 并不总是设置。不幸的是,si_pid
字段在一个联合中,所以当您认为您正在阅读 pid 时,您实际上可能会阅读完全不同的内容。例如,当您的程序有段错误时,SIGSEGV
信号没有si_pid
。 si_pid
是否可以安全访问在man sigaction
中进行了描述。如果您有si_code == SI_USER || si_code == SI_QUEUE
,则设置si_pid
。否则会变得复杂。【参考方案2】:
我还需要在程序中识别信号发送者,所以我拿了grawity的answer,在我的程序中使用,效果很好。
示例代码如下:
send_signal_raise.c
// send signal to self test - raise()
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
static int int_count = 0, max_int = 5;
static struct sigaction siga;
static void multi_handler(int sig, siginfo_t *siginfo, void *context)
// get pid of sender,
pid_t sender_pid = siginfo->si_pid;
if(sig == SIGINT)
int_count++;
printf("INT(%d), from [%d]\n", int_count, (int)sender_pid);
return;
else if(sig == SIGQUIT)
printf("Quit, bye, from [%d]\n", (int)sender_pid);
exit(0);
return;
int raise_test()
// print pid
printf("process [%d] started.\n", (int)getpid());
// prepare sigaction
siga.sa_sigaction = *multi_handler;
siga.sa_flags |= SA_SIGINFO; // get detail info
// change signal action,
if(sigaction(SIGINT, &siga, NULL) != 0)
printf("error sigaction()");
return errno;
if(sigaction(SIGQUIT, &siga, NULL) != 0)
printf("error sigaction()");
return errno;
// use "ctrl + c" to send SIGINT, and "ctrl + \" to send SIGQUIT,
int sig;
while(1)
if(int_count < max_int)
sig = SIGINT;
else
sig = SIGQUIT;
raise(sig); // send signal to itself,
sleep(1); // sleep a while, note that: SIGINT will interrupt this, and make program wake up,
return 0;
int main(int argc, char *argv[])
raise_test();
return 0;
编译:
gcc -pthread -Wall send_signal_raise.c
执行:
./a.out
它的作用:
程序发送SIGINT
给自己10次,然后发送SIGQUIT
终止自己。
另外,在执行过程中,按 CTRL+C 发送SIGINT
,或CTRL+\ 发送SIGQUIT
,这将手动终止程序。
程序可以成功识别谁发送了信号。
【讨论】:
【参考方案3】:BCC 包括killsnoop
实用程序。它需要一个支持 BPF 的内核。
摘自killsnoop (8) man-page:
killsnoop traces the kill() syscall, to show signals sent via this method. This may be
useful to troubleshoot failing applications, where an unknown mechanism is sending
signals.
This works by tracing the kernel sys_kill() function using dynamic tracing, and will need
updating to match any changes to this function.
This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than
4.5, see the version under tools/old, which uses an older mechanism.
Since this uses BPF, only the root user can use this tool.
【讨论】:
我有一个信号 (37 (SIGRTMIN+3) ) 被发送到 killsnoop 不记录的进程。是否存在不流经它所监控的系统调用的信号? 好像sigqueue
使用了不同的系统调用
在将sigqueue
也添加到killsnoop
之后,我仍然一无所获【参考方案4】:
不,信号不打算用作进程间通信通道。据我所知,没有通过PID。发送 PID 与我看到的所有信号用途无关。您可以相对确定发送信号的进程要么具有 root 权限,要么与您的进程属于同一 UID。
发送信号的进程可能不再存在。如果使用的是 kill 命令而不是内置的 shell,则几乎可以肯定该进程不再存在。
从 Java 方面来看,这更加困难。该进程在从操作系统抽象出来的 Java 虚拟机中运行。并非所有操作系统概念都存在于这台机器上。
【讨论】:
以上是关于如何在 Linux 中判断哪个进程向我的进程发送了信号的主要内容,如果未能解决你的问题,请参考以下文章