在处理程序中阻止新信号

时间:2014-03-07 16:45:26

标签: c linux multithreading signals

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

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

我在信号处理程序结束时将一个睡眠(我自己的实现使用clock_nanosleep()在循环中恢复),并将SIGINT发送给子节点。这正式退出并将SIGCHLD发送给父母。我记录了这个事实并开始睡了10秒钟。现在,我向另一个孩子发送了另一个SIGINT(sighandler第一次重新启动它),并且惊讶地看到另一个日志和睡眠发生。

这怎么可能?当我使用调试器连接到父节点时,它清楚地显示两个不同的线程被中断以调用我的信号处理程序,两者现在都处于睡眠状态。如果这种情况继续存在,我将耗尽线程!

我知道将长时间的睡眠放入信号处理程序是一项愚蠢的事情,但它确实说明了这一点;我希望在/ proc / [PID] /状态中看到第二个信号标记为待处理,但它已经交付。

以下是我的代码的相关部分:

设置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()的阻止掩码中,以试图鼓励它做正确的事情。

欢迎任何指示!

1 个答案:

答案 0 :(得分:2)

  

默认情况下会阻止相同类型的信号。

是的,但仅从线程sigaction()调用。

来自man sigaction粗体强调我):

  

sa_mask指定应该阻止的信号掩码(即,          添加到信号处理程序所在线程的信号掩码          在执行信号处理程序期间调用

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

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

<强>更新

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

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

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

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