等待来自多个进程的信号时 sigwait 有什么问题
What's wrong with sigwait while waiting signals from multiple processes
本例我有multi-processes3、1parent2children。我希望每个 child 从它们的信号处理程序返回后继续。但有时它们会卡住,有时只有其中一个会继续。我的错误是什么?实际上我正在尝试用信号做一些类似于 waking-up 的事情。我可以有更多的信号来做到这一点,但如果需要 100 children 怎么办?所以,我只想通过使用 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
---
当我将 children 数量增加到 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
这里的基本问题是 parent 发送 SIGUSR2 信号和 child 调用 sigsuspend 之间的竞争条件。如果 child 启动速度较慢并且 parent 先运行,它可能会在 child 调用 sigsuspend 之前发送信号。由于 child 开始(来自 fork() 的 returns),信号处理程序处于活动状态且未屏蔽,它可能会立即捕获信号(并打印有关捕获它的消息),然后 return 并调用 sigsuspend。由于此时信号已被处理,sigsuspend 将等待从未出现的第二个信号。
修复是为了确保 SIGUSR2 在 child 中被阻止,直到它调用 sigsuspend。在调用 fork 的循环之前放置代码来执行此操作:
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR2);
sigprocmask(SIG_BLOCK, &ss, 0);
本例我有multi-processes3、1parent2children。我希望每个 child 从它们的信号处理程序返回后继续。但有时它们会卡住,有时只有其中一个会继续。我的错误是什么?实际上我正在尝试用信号做一些类似于 waking-up 的事情。我可以有更多的信号来做到这一点,但如果需要 100 children 怎么办?所以,我只想通过使用 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
---
当我将 children 数量增加到 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
这里的基本问题是 parent 发送 SIGUSR2 信号和 child 调用 sigsuspend 之间的竞争条件。如果 child 启动速度较慢并且 parent 先运行,它可能会在 child 调用 sigsuspend 之前发送信号。由于 child 开始(来自 fork() 的 returns),信号处理程序处于活动状态且未屏蔽,它可能会立即捕获信号(并打印有关捕获它的消息),然后 return 并调用 sigsuspend。由于此时信号已被处理,sigsuspend 将等待从未出现的第二个信号。
修复是为了确保 SIGUSR2 在 child 中被阻止,直到它调用 sigsuspend。在调用 fork 的循环之前放置代码来执行此操作:
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR2);
sigprocmask(SIG_BLOCK, &ss, 0);