为什么这个进程不再处理SIGUSR1?

时间:2017-05-15 14:14:19

标签: c signals fork signal-processing

我必须解决两个进程之间的同步问题,我必须用信号来解决。

两个进程应该执行一定量的工作,然后发出另一个进程的信号。这将无限期地发生。

我的代码会发生什么:

我向父进程发送一个信号,该进程向子进程发送一个信号,该进程再次向父进程发送一个信号,该进程被停止并且不再执行。

理想情况下,父母和孩子应该无限期地互相发出信号。

int main () {
    parent_pid = getpid();

    pid_t pid = fork();
    if (pid < 0) {
        syserr_quit("fork error");
    } else if (pid == 0) {
        signal(SIGUSR2, child_work);
        while (1) {}
    } else {
        child_pid = pid;

        signal(SIGUSR1, parent_work);
        while (1) {}
    }
}

void child_work (int signo) {
    sleep(1); // fake child work

    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");

    // wait for parent signal
    pause();
}

void parent_work (int signo) {
    sleep(1); // fake parent work

    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");

    // wait for child signal
    pause();
}

无法弄清楚出了什么问题,因为它只是一小段代码而且似乎一切都在我身上。

如果有人想在他们的机器上将其旋转,这里的a full demo可以在复制/粘贴后运行。

更新:sigaction(3)而不是signal(2)

根据一些评论,我使用sigaction来避免竞争条件,并在演示中停止使用sleep。 Docs声明,为了避免在执行处理程序期间阻止信号接收,必须设置SA_NODEFER标志。

代码仍然存在故障,只有这一次是停止接收信号的孩子。

代码段与上面相同,但signal被替换除外 - 两次 - 与

struct sigaction action;
action.sa_handler = parent/child_work;
action.sa_flags = SA_NODEFER;
sigaction(signo, &action, NULL);

完整代码

要启动信令,请从控制台检查并发送信号(即kill -SIGUSR1 <parentid>

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void parent_work(int signo);
void child_work(int signo);

void syserr_quit(char *msg);

pid_t parent_pid;
pid_t child_pid;

int main () {
    parent_pid = getpid();
    printf("parent pid: %d\n", parent_pid);

    pid_t pid = fork();
    if (pid < 0) {
        syserr_quit("fork error");
    } else if (pid == 0) {
        struct sigaction action;
        action.sa_handler = child_work;
        action.sa_flags = SA_NODEFER;
        sigaction(SIGUSR2, &action, NULL);
        while (1) {}
    } else {
        child_pid = pid;
        printf("child pid: %d\n", pid);

        struct sigaction action;
        action.sa_handler = parent_work;
        action.sa_flags = SA_NODEFER;
        sigaction(SIGUSR1, &action, NULL);
        while (1) {}
    }
}

void child_work (int signo) {
    for (int i = 0; i < 100000000; ++i); // fake child work

    // signal parent that a write is now possible
    printf("send signal to %d\n", parent_pid); fflush(stdout);
    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");
    printf("signal sent\n"); fflush(stdout);

    // wait for parent signal
    pause();
}

void parent_work (int signo) {
    for (int i = 0; i < 100000000; ++i); // fake child work

    // signal child that a read is possible
    printf("send signal to %d\n", child_pid); fflush(stdout);
    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");
    printf("signal sent\n"); fflush(stdout);

    // wait for child signal
    pause();
}

void syserr_quit(char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

好吧,伙计们看来更新的代码不起作用只是因为我没有正确初始化sigaction掩码。必须使用sigemptyset完成,您可以找出原因right here

the answer中的更多详情。

2 个答案:

答案 0 :(得分:0)

当您调用SIGUSR1信号处理程序parent_work时,SIGUSR1信号将映射到SIG_DFL(默认信号处理)或忽略父进程(参见。 signal):

  

当一个信号出现,并且func指向一个函数时,它是实现定义的是否相当于:

signal(sig, SIG_DFL);
     

被执行或者实现阻止一些实现定义的信号集(至少包括sig)发生,直到当前信号处理完成。

当子进程再次为父进程生成SIGUSR1信号时,由于该原因,它不会再次调用parent_work信号处理程序。

因此,在信号处理程序中,如果您希望后续信号也由信号处理程序处理,则需要再次重新注册信号:

void parent_work(int signo) {
    /* your handler code */
    signal(signo, parent_work);
}

请注意,这仍然为比赛条件留出空间 - 即。如果第二个SIGUSR1在信号处理程序重新注册之前到达。但实际上,这是你设计所固有的。

当然有更好的选择和一般建议。其中一些已在评论中提及过,所以如果您有兴趣我会参考这些。

答案 1 :(得分:0)

似乎更新的代码不起作用只是因为我没有正确初始化sigaction掩码。必须使用sigemptyset完成,您可以找出原因right here

为执行处理程序时希望能够捕获的信号正确初始化sigaction

struct sigaction sa;
sigemptyset(&sa.sa_mask);
action.sa = handler;
action.sa = SA_NODEFER;
sigaction(signo, &sa, NULL);

最终代码

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void parent_work(int signo);
void child_work(int signo);

void syserr_quit(char *msg);

pid_t parent_pid;
pid_t child_pid;

int main () {
    parent_pid = getpid();
    printf("parent pid: %d\n", parent_pid);

    pid_t pid = fork();
    if (pid < 0) {
        syserr_quit("fork error");
    } else if (pid == 0) {
        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_handler = child_work;
        sa.sa_flags = SA_NODEFER;
        sigaction(SIGUSR2, &sa, NULL);
        while (1) {}
    } else {
        child_pid = pid;
        printf("child pid: %d\n", pid);

        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_handler = parent_work;
        sa.sa_flags = SA_NODEFER;
        sigaction(SIGUSR1, &sa, NULL);
        while (1) {}
    }
}

void child_work (int signo) {
    for (int i = 0; i < 100000000; ++i); // fake child work

    // signal parent that a write is now possible
    printf("send signal to %d\n", parent_pid); fflush(stdout);
    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");
    printf("signal sent\n"); fflush(stdout);

    // wait for parent signal
    pause();
}

void parent_work (int signo) {
    for (int i = 0; i < 100000000; ++i); // fake child work

    // signal child that a read is possible
    printf("send signal to %d\n", child_pid); fflush(stdout);
    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");
    printf("signal sent\n"); fflush(stdout);

    // wait for child signal
    pause();
}

void syserr_quit(char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}