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

C 为什么这个过程不再处理SIGUSR1?,c,signals,fork,signal-processing,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);

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

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

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();
}
我的代码会发生以下情况:

我向父进程发送信号,父进程向子进程发送信号,子进程再次向父进程发送信号,父进程停止,不再行动

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

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();
}
完整代码

要启动信号,请从控制台检查并发送一个信号,即kill-SIGUSR1

鳍 好了,伙计们,更新的代码似乎不起作用,只是因为我没有正确初始化sigaction掩码。这必须通过SIGEPTYSET完成,您可以找到原因

有关详细信息,请参见。

当调用SIGUSR1信号处理程序父级\u工作时,SIGUSR1信号要么映射到SIG\u DFL默认信号处理,要么忽略父进程参考:

当出现信号且func指向函数时,实现定义是否等效于:

signal(sig, SIG_DFL);
或者该实现防止至少包括sig的一些实现定义的信号集发生,直到当前信号处理完成

当子进程随后再次为父进程生成SIGUSR1信号时,它不会因此再次调用父进程的工作信号处理程序

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

void parent_work(int signo) {
    /* your handler code */
    signal(signo, parent_work);
}
请注意,这仍然为竞争条件留有空间-即,如果第二个SIGUSR1在信号处理程序重新注册之前到达。但实际上,这是你设计的本质


当然,还有更好的选择和一般性建议。其中一些已经在评论中提到了,因此如果您感兴趣,我将只参考这些内容。

似乎更新的代码不起作用,只是因为我没有正确初始化sigaction掩码。这必须通过SIGEPTYSET完成,您可以找到原因

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

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


如前所述,您的代码甚至不会编译。信号处理程序引用未声明的变量,所有函数都调用未声明的函数syserr\u quit。这是一个好的开始,但还没有开始。@JohnBollinger让我读一下。同时,我在问题的最后一行提供了一个完整的60行工作演示。不确定这是否与您的问题有关,因为您的代码不会编译,但引用Linux signal2手册页:避免使用:改用sigaction2。。顺便说一句,永远不要在信号处理程序中执行复杂的工作,而只设置标志等。如果处置设置为函数,则首先要么将处置重置为SIG_DFL,要么阻止信号(请参见下面的可移植性),然后使用参数signum调用处理程序。从使用sigaction开始,并按照前面的建议将处理程序减少到只增加一个标志。请注意,通过信号函数注册的信号处理程序的行为中固有的实现定义是通过使用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);
}