【问题标题】:Why doesn't this process handle a SIGUSR1 anymore?为什么这个进程不再处理 S​​IGUSR1 了?
【发布时间】:2017-05-15 14:14:19
【问题描述】:

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

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

我的代码会发生什么:

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

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

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)

根据一些 cmets,我使用 sigaction 来避免竞争条件,并在演示中停止使用 sleep。 文档指出,为了避免信号接收在处理程序执行期间被阻塞,必须设置 SA_NODEFER 标志。

代码仍然有问题,只是这一次是孩子停止接收信号。

除了signal 被交换 - 两次 - 与

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

完整代码

要启动信号,请从控制台检查并发送信号(即kill -SIGUSR1 &lt;parentid&gt;

#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

【问题讨论】:

  • 如前所述,您的代码甚至无法编译。信号处理程序引用未声明的变量,您的所有函数都调用未声明的函数syserr_quit()。这是minimal reproducible example 的良好开端,但还没有。
  • @JohnBollinger 让我读一下。与此同时,我在问题的最后一行提供了一个完整的 60 行工作演示。
  • 不确定这是否与您的问题有关(因为您的代码无法编译),但请引用 Linux signal(2) 手册页:“避免使用它:使用 sigaction(2) ”。 man7.org/linux/man-pages/man2/signal.2.html
  • 顺便说一句。永远不要在信号处理程序中执行复杂的工作,而只设置标志等。
  • "如果处置被设置为一个函数,那么首先处置被重置为 SIG_DFL,或者信号被阻塞(参见下面的可移植性),并且然后使用参数 signum 调用处理程序。“首先使用 sigaction 并将您的处理程序减少为之前建议的标志增量。

标签: c signals fork signal-processing


【解决方案1】:

当您的SIGUSR1 信号处理程序parent_work 被调用时,SIGUSR1 信号要么映射到SIG_DFL(默认信号处理),要么被父进程忽略(参考signal):

当一个信号出现时,func指向一个函数,实现定义是否等价于a:

signal(sig, SIG_DFL);

执行或实现阻止某些实现定义的信号集(至少包括 sig)在当前信号处理完成之前发生。

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

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

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

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

当然还有更好的选择和一般建议。其中一些已经在 cmets 中提到过,如果您有兴趣,我将仅参考。

【讨论】:

  • 请注意,通过signal() 函数注册的信号处理程序的行为中固有的实现定义是通过使用sigaction() 注册处理程序来解决的。此外,这种机制使得处理程序不必重新注册自己(取决于选择了哪个选项),因此可以避免竞争条件。
【解决方案2】:

似乎更新后的代码不起作用只是因为我没有正确初始化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);
}

【讨论】:

    猜你喜欢
    • 2017-09-24
    • 2015-01-13
    • 1970-01-01
    • 2015-09-22
    • 2022-10-08
    • 2016-04-30
    • 2019-04-07
    • 2011-09-24
    • 2013-02-10
    相关资源
    最近更新 更多