如何在 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_SIGINFOsignalfd(),它们允许程序接收非常关于发送信号的详细信息,包括发送者的 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_pidsi_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 中判断哪个进程向我的进程发送了信号的主要内容,如果未能解决你的问题,请参考以下文章

[转帖]判断Linux进程在哪个CPU核运行的方法

linux中的进程间通信应该使用哪个时钟?

如何在 Linux 中显示哪个进程拥有磁盘空间

在 Linux 中,如何判断进程正在使用多少内存?

我的 pm2 进程在哪个端口运行? (Linux)

Braintree API:如何判断哪个 payment_method 与我的 nonce 相关联?