带有SA_SIGINFO的macOS的sigaction()处理程序不包括si_pid

时间:2018-11-20 19:08:21

标签: c macos signals sigaction

我正在尝试编写一个信号处理程序,该处理程序需要知道发送信号的进程的pid。我无法从siginfo_t中获得任何有用的东西,这些东西都通过Xcode 10传递给了macOS 10.14上的处理程序。

我已将代码简化为以下最小示例,以演示该问题。在此示例中,我生成了一个子进程来发送要测试的信号,该信号默认为SIGTERM,但是我尝试过的其他信号都无法正常工作。

假设您要在Mac上进行构建和测试,您可能想告诉lldb在收到信号时不要停止。您可以使用以下lldb命令:pro hand -p true -s false SIGTERM

我也正在使用C ++进行编译,但是我相信我已经将所有内容都删除了,示例代码现在应该是纯C了。

请注意,信号是否来自子进程,终端或其他进程都无关紧要,结果始终是si_pid始终为0(以及{{1}以外的所有内容) }和si_signo)。我发送信号的次数无关紧要,所以这似乎不只是一种竞争条件。

如何获取在macOS 10.14上发送信号的进程的pid?我不记得以前在10.12上遇到过此问题。

这只是说明问题的一个示例,因此请忽略实际上没有引起问题的任何内容。

如果代码看起来像我期望的那样工作,那么我将有兴趣查看关于它也可以工作的系统的评论。

si_addr

2 个答案:

答案 0 :(得分:4)

我当前正在使用macOS Mojave 10.14.1。

  

如何获取在macOS上发送信号的进程的pid   10.14?我不记得以前在10.12上遇到过这个问题。

以下代码简单地满足了您的愿望。如果您发送SIGTERM,则可以看到发件人进程的pid。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

static void hdl (int sig, siginfo_t *siginfo, void *context)
{
    printf ("Sending PID: %ld, UID: %ld\n",
            (long)siginfo->si_pid, (long)siginfo->si_uid);
}

int main (int argc, char *argv[])
{
    struct sigaction act;

    fprintf(stderr, "%i pp %i\n",getpid(), getppid());

    memset (&act, '\0', sizeof(act));

    /* Use the sa_sigaction field because the handles has two additional parameters */
    act.sa_sigaction = &hdl;

    /* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
    act.sa_flags = SA_SIGINFO;

    if (sigaction(SIGTERM, &act, NULL) < 0) {
        perror ("sigaction");
        return 1;
    }

    while (1)
        sleep (10);

    return 0;
}

对于您的代码,

  

经验法则:即使您确定子进程在父进程之前结束,也不要忘记执行埋葬程序。通过调用wait(...),您告诉操作系统我已经为孩子完成了工作,因此现在您可以清除分配的字段等。

我希望在派生之前初始化信号实用程序,如果父进程没有机会注册信号动作该怎么办?而且,我不明白您为什么要在0中处理1switch的情况。本质上来说,这种情况没有发生,所以总是省略。

此外,您没有在break内的if条件下使用main()。过一会儿它不会进入if中,但是以下情况是无法预料的,​​那就是该程序永远停留在while()循环中。我更愿意将signaled置于while()循环的条件下。

最后但并非最不重要的是,由于在子进程中进行了sleep()的调用,直到signaled被证明0为止,SIGTERM才被成功捕获了几次。当发出0信号时,循环停止。

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <memory.h>
#include <sys/wait.h>

volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;

void sigaction_handler(int sig, siginfo_t* info, void* context)
{
    switch (info->si_pid) {
    case 0:
    case 1:
        histogram[info->si_pid]++;
        break;

    default:
        fprintf(stderr, "sender pid -> %i\n", info->si_pid);
        histogram[2]++;
        break;
    }
    signaled = 1;
}

int main(int argc, const char * argv[]) {


    struct sigaction sigAction;
    memset( &sigAction, 0, sizeof( sigAction ) );

    sigAction.sa_sigaction = sigaction_handler;
    sigemptyset (&sigAction.sa_mask);
    sigAction.sa_flags = SA_SIGINFO;
    sigaction(testsig, &sigAction, NULL);

    pid_t mainpid = getpid();
    pid_t pid = fork();
    if (pid == 0) {
        fprintf(stderr, "my pid -> %i parent's pid-> %i\n", getpid(), getppid());
        if (kill(mainpid, 0) == 0) { // signals are not queued not need loop
            sleep(1);
            kill(mainpid, testsig);
        }
        _exit(0);
    } else {

        wait(NULL); // play with this line to see what the difference is
        while ( signaled ) {
            printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
            signaled = 0;
            sleep(1);
        }
        // wait(NULL); // play with this line to see what the difference is

    }
}

答案 1 :(得分:2)

事实证明,通过 Xcode LLDB进行调试是罪魁祸首。如果我正常构建并运行该程序,则可以正常运行。如果我知道为什么我将更新此答案。

我已经在问题中指出了在lldb中为SIGTERM设置了“ PASS”,所以似乎Xcode 10.0附带的lldb版本中存在一个错误,并且通过创建一个“传递”信号新的结构并设置信号号,而不是通常会收到的结构。正如我之前所说,在macOS 10.12随附的任何lldb版本中,它的确可以正常工作

如果有人有更好的解释,请发布答案,我将接受并奖励赏金。