#c #linux #signals #fork
#c #linux #сигналы #fork
Вопрос:
В данном случае у меня много процессов 3, 1 родительский, 2 дочерних. Я ожидаю, что каждый дочерний элемент продолжит работу после возврата из своих обработчиков сигналов. Но иногда они зависают, иногда продолжается только один из них. В чем моя ошибка? На самом деле я пытаюсь сделать что-то похожее на пробуждение с помощью signal. У меня может быть больше сигнала для этого, но что, если требуется 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 signaln", 33);
printf("pid %u Teller has caught SIGUSR2 signaln", getpid());
}
int main() {
int NUM_OF_CHILDREN = 2;
struct sigaction sa;
memset(amp;sa, 0, sizeof(sa));
sa.sa_flags = 0;
sa.sa_handler = tellerHandler;
if (sigaction(SIGUSR2, amp;sa, NULL) == -1) {
perror("sigaction error");
exit(-1);
}
sigset_t new_mask;
sigfillset(amp;new_mask);
sigdelset(amp;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(amp;new_mask);
printf("child %u returned from handlern", getpid());
} else {
for (int i = 0; i < NUM_OF_CHILDREN; i) {
printf("child %un", 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
Комментарии:
1.
printf
небезопасно вызывать обработчик сигналов.2. Почему вы звоните
sigsuspend
?3. @WilliamPursell сэр, это всего лишь небольшая часть моего кода, в которой я застрял. Я использую его для синхронизации.
4. @WilliamPursell распечатайте файл просто для проверки
Ответ №1:
Ваша основная проблема здесь — это состояние гонки между родителем, отправляющим сигнал SIGUSR2, и дочерним устройством, вызывающим sigsuspend. Если дочерний процесс запускается медленно (er), а родительский запускается первым, он может отправить сигнал ДО того, как дочерний процесс вызовет sigsuspend . Поскольку дочерний элемент запускается (возвращается из fork()) с активным и незамаскированным обработчиком сигнала, он может сразу перехватить сигнал (и напечатать сообщение о его перехватывании), а затем вернуться и вызвать sigsuspend . Поскольку к этому моменту сигнал уже обработан, sigsuspend будет ждать второго сигнала, который никогда не приходит.
Исправление заключается в том, чтобы гарантировать, что SIGUSR2 заблокирован в дочернем элементе, пока он не вызовет sigsuspend. Поместите код для этого ПЕРЕД циклом, который вызывает fork:
sigset_t ss;
sigemptyset(amp;ss);
sigaddset(amp;ss, SIGUSR2);
sigprocmask(SIG_BLOCK, amp;ss, 0);