c - 在处理程序中阻止新信号

标签 c linux multithreading signals

我有一个管理子进程的父进程(fork、execve)。我在父级中创建了一个处理程序来捕获来自子级的 SIGCHLD 信号,以便调用 waitpid() 并采取适当的操作,例如重新启动子级。

我从 sigaction() 的手册页了解到,在信号处理程序中,默认情况下会阻止更多相同类型的信号。我绝对希望有这种行为,所以我决定对其进行测试。

我在信号处理程序的末尾放置了一个 sleep (我自己的实现使用 clock_nanosleep() 在一个循环中恢复)在信号处理程序的末尾发送一个 SIGINT 给 child 。这适本地使它退出并将 SIGCHLD 发送给父级。我记下了这个事实并开始了 10 秒钟的 sleep 。现在,我向新 child 发送了另一个 SIGINT(sighandler 第一次重新启动它)并且惊讶地看到另一个日志和 sleep 发生。

这怎么可能?当我使用调试器附加到父级时,它清楚地显示了两个不同的线程被中断以调用我的信号处理程序,现在都处于 sleep 状态。如果再这样下去,我的线程就会用完了!

我知道将长时间 sleep 放入信号处理程序是一件愚蠢的事情,但它确实说明了这一点;我希望在/proc/[PID]/status 中看到第二个信号被标记为待处理,但它却被发送了。

这是我的代码的相关部分:

设置 SIGCHLD 处理程序:

typedef struct SigActType {
    struct sigaction act;
    int              retval;
    void             (*func)(int);
}SigActType;
static SigActType sigActList[64];

public void setChildHandler(void (*func)(int)) {
    SigActType *sat = &sigActList[SIGCHLD];

    sat->act.sa_sigaction = sigchldHandler;
    sigemptyset(&sat->act.sa_mask);
    sigaddset (&sat->act.sa_mask, SIGTERM);
    sigaddset (&sat->act.sa_mask, SIGINT);
    sigaddset (&sat->act.sa_mask, SIGCHLD);
    sat->act.sa_flags = SA_SIGINFO;
    sat->retval = 0;
    sat->func = func;
    sigaction(SIGCHLD, &sat->act, NULL);
}

static void sigchldHandler(int sig, siginfo_t *si, void *thing) {
    SigActType *sat = &sigActList[SIGCHLD];

    if (sat->func) {
        sat->func(si->si_pid);
    }
}

并使用这个:

int main(int argc, char **argv) {
    setChildHandler(manageChildSignals);
    ...
}

static void manageChildSignals(int d) {
    if ((pid = waitpid(-1, &stat, WAIT_MYPGRP)) > 0) {
        ... restart child if appropriate
    }
    printf("start of pause...\n");
    mySleep(10);
    printf("end of pause...\n");
}

Stdout 清楚地显示:

(when I type kill -2 [PID]
start of pause
(when the new child is started and I type kill -2 [NEWPID]
start of pause
...10 seconds slide past...
end of pause
end of pause

我很困惑为什么会这样。如您所见,我什至将 SIGCHLD 添加到 sigaction() 的 block 掩码中,以尝试鼓励它做正确的事情。

欢迎任何指点!

最佳答案

signals of the same type would be blocked by default.

是的,但仅适用于线程 sigaction()被调用自。

来自 man sigaction (由我大胆强调):

sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler.

由于信号处理是按进程进行的,因此任何其他未阻塞相关信号的线程都可能会收到它,即被中断并处理它。

如果这种行为不是您想要的,您或许应该修改程序处理信号的方式的设计,使每个线程默认阻塞所有信号,并且只有一个特定线程的信号接收未被阻塞。

更新:

信号掩码由子线程从父线程继承。

如果信号处理只能由一个特定的线程完成,让主线程在创建任何其他线程之前阻塞所有信号。然后创建一个特定的线程来进行信号处理,并让这个线程解除对要处理的信号的阻塞。这个概念还允许像每个信号一个线程这样的模型。

在多线程环境中使用 pthread_sigmask()在每个线程基础上屏蔽信号。

请注意 sigprocmask() 在多线程进程中的行为是未指定的,然后使用 pthread_sigmask()

关于c - 在处理程序中阻止新信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22255817/

相关文章:

multithreading - 从 Go 应用程序中的线程低级 C/C++ 代码层调用 Go 回调

c - IMAP协议(protocol)的解释?

c - double sscanf

c - groovy "def"声明转换为 C 语言?

c - 除 gcc 外,我可以使用哪个程序在 Linux 中进行调试?

c++ - 如何使用来自不同目录的make文件中的文件

c - 使用 malloc 分配页面时发生内存泄漏

windows - 线程 ID 与线程句柄

c# - 线程基础

java - 如何在二维矩阵中找到相同元素的行?