【问题标题】:What's wrong with sigwait while waiting signals from multiple processes等待来自多个进程的信号时 sigwait 有什么问题
【发布时间】:2019-09-11 20:53:27
【问题描述】:

在这种情况下,我有 3 个多进程,1 个父 2 个子。我希望每个孩子在从他们的信号处理程序返回后继续。但有时它们会卡住,有时只有其中一个会继续。我的错误是什么?实际上我正在尝试做一些类似于用信号唤醒的事情。我可以有更多的信号来做到这一点,但如果需要 100 个孩子怎么办?所以,我只想通过使用SIGUSR2来实现这一点

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


void tellerHandler(int sig) {
    //write(STDERR_FILENO, "Teller has caught SIGUSR2 signal\n", 33);
    printf("pid %u Teller has caught SIGUSR2 signal\n", getpid());
}

int main() {

    int NUM_OF_CHILDREN = 2;
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_flags = 0;
    sa.sa_handler = tellerHandler;
    if (sigaction(SIGUSR2, &sa, NULL) == -1) {
        perror("sigaction error");
        exit(-1);
    }

    sigset_t new_mask;
    sigfillset(&new_mask);
    sigdelset(&new_mask, SIGUSR2);

    int returnedPid = -1;
    pid_t pidList[NUM_OF_CHILDREN];

    for (int i = 1; i < 1 + NUM_OF_CHILDREN; ++i) {
        if ((returnedPid = fork()) == 0) {
            break;
        } else {
            pidList[i - 1] = returnedPid;
        }
    }

    if (returnedPid == 0) {
        sigsuspend(&new_mask);
        printf("child %u returned from handler\n", getpid());
    } else {
        for (int i = 0; i < NUM_OF_CHILDREN; ++i) {
            printf("child %u\n", pidList[i]);
            kill(pidList[i], SIGUSR2);
        }

        for (int i = 0; i < NUM_OF_CHILDREN; ++i) {
            waitpid(pidList[i], 0, 0);
        }
        puts("parent exiting...\n");
    }

    puts("donee");
}

多次运行的一些不同输出,

child 66313
child 66314
pid 66313 Teller has caught SIGUSR2 signal
child 66313 returned from handler
pid 66314 Teller has caught SIGUSR2 signal

---

child 66330
child 66331
pid 66330 Teller has caught SIGUSR2 signal
pid 66331 Teller has caught SIGUSR2 signal

---

当我将孩子人数增加到 3 时,以下输出类似于

child 66738
child 66739
child 66740
pid 66739 Teller has caught SIGUSR2 signal
pid 66738 Teller has caught SIGUSR2 signal
child 66738 returned from handler
donee
pid 66740 Teller has caught SIGUSR2 signal

【问题讨论】:

  • printf 在信号处理程序中调用是不安全的。
  • 你为什么打电话给sigsuspend
  • @WilliamPursell 先生,这只是我卡住的代码的一小部分。我用它来同步。
  • @WilliamPursell printf 只是为了测试

标签: c linux signals fork


【解决方案1】:

这里的基本问题是发送 SIGUSR2 信号的父级和调用 sigsuspend 的子级之间的竞争条件。如果子进程启动速度较慢并且父进程先运行,它可能会在子进程调用 sigsuspend 之前发送信号。由于子进程在信号处理程序处于活动状态且未屏蔽的情况下开始(从 fork() 返回),它可能会立即捕获信号(并打印有关捕获它的消息),然后返回并调用 sigsuspend。由于此时信号已经被处理,sigsuspend 将等待第二个永远不会到来的信号。

修复方法是确保 SIGUSR2 在子进程中被阻止,直到它调用 sigsuspend。在调用 fork 的循环之前放置代码:

sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR2);
sigprocmask(SIG_BLOCK, &ss, 0);

【讨论】:

    猜你喜欢
    • 2020-03-09
    • 2020-02-27
    • 2017-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 2020-05-06
    相关资源
    最近更新 更多