Трудно сказать, что именно не так, не видя остальной части кода, но в коде, который вы показываете, есть по крайней мере одна проблема:
Перед возвратом sigsuspend(2)
восстанавливает маску сигнала, которая была активной при вызове. Если вы не изменили маску сигнала процесса перед вызовом sigsuspend(2)
, то между возвратом из sigsuspend(2)
и проверкой значения flag
есть промежуток времени, в течение которого могут быть доставлены другие отложенные сигналы (или даже может случиться так, что новые сигналы создаются и доставляются). Если эти сигналы перехватываются и обработчик меняет значение flag
на другое значение, вы «теряете» обновление, которое сделал обработчик SIGUSR2
.
Например, рассмотрим эту программу:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
volatile sig_atomic_t flag;
void handler1(int signo) {
printf("In handler1\n");
flag = 1;
}
void handler2(int signo) {
printf("In handler2\n");
flag = 2;
}
int main(void) {
struct sigaction sigact;
sigact.sa_handler = handler1;
sigact.sa_flags = 0;
sigfillset(&sigact.sa_mask);
if (sigaction(SIGUSR1, &sigact, NULL) < 0) {
perror("sigaction()");
exit(EXIT_FAILURE);
}
sigact.sa_handler = handler2;
if (sigaction(SIGUSR2, &sigact, NULL) < 0) {
perror("sigaction()");
exit(EXIT_FAILURE);
}
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigsuspend(&mask);
printf("%d\n", flag);
return 0;
}
Он ловит SIGUSR1
и SIGUSR2
, блокирует все сигналы, кроме SIGUSR1
, а затем вызывает sigsuspend(2)
.
Запустите его, а затем отправьте SIGUSR2
, а затем SIGUSR1
. Это гарантирует наличие ожидающего сигнала до того, как SIGUSR1
будет замечено и обработано. Это также приводит к доставке SIGUSR2
, как только SIGUSR2
разблокируется, что происходит после возврата sigsuspend(2)
(поскольку мы не коснулись маски сигнала процесса):
filipe@filipe-Kubuntu:~/dev$ kill -SIGUSR2 29755
filipe@filipe-Kubuntu:~/dev$ kill -SIGUSR1 29755
Выход:
filipe@filipe-Kubuntu:~/dev$ ./a.out
In handler1
In handler2
2
Как это исправить? Просто убедитесь, что маска сигнала процесса установлена на ту же маску, которую вы используете в sigsuspend(2)
, чтобы по ошибке не восстановить маску. Это так же просто, как добавить это перед вызовом sigsuspend(2)
:
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) {
perror("sigprocmask()");
exit(EXIT_FAILURE);
}
Итак, обновленная программа:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
volatile sig_atomic_t flag;
void handler1(int signo) {
printf("In handler1\n");
flag = 1;
}
void handler2(int signo) {
printf("In handler2\n");
flag = 2;
}
int main(void) {
struct sigaction sigact;
sigact.sa_handler = handler1;
sigact.sa_flags = 0;
sigfillset(&sigact.sa_mask);
if (sigaction(SIGUSR1, &sigact, NULL) < 0) {
perror("sigaction()");
exit(EXIT_FAILURE);
}
sigact.sa_handler = handler2;
if (sigaction(SIGUSR2, &sigact, NULL) < 0) {
perror("sigaction()");
exit(EXIT_FAILURE);
}
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) {
perror("sigprocmask()");
exit(EXIT_FAILURE);
}
sigsuspend(&mask);
printf("%d\n", flag);
return 0;
}
Это работает так, как ожидалось: гарантируется, что никакие другие обработчики сигналов не будут выполняться между окном времени после возврата sigsuspend(2)
и проверкой flag
.
Еще одно важное замечание: я не знаю, как вы настраиваете обработчик сигнала, но если вы используете signal(2)
, то не делайте этого: его семантика зависит от платформы, и он может вести себя не так, как вы хотите. Вы должны использовать sigaction(2)
точно так, как я показываю в примере программы. Строка sigfillset(&sigact.sa_mask)
необходима для того, чтобы убедиться, что во время работы обработчика сигнала все остальные сигналы по-прежнему заблокированы, иначе доставка сигнала в разгар выполнения обработчика SIGUSR1
может вызвать другой обработчик, который изменил флаг.
person
Filipe Gonçalves
schedule
27.06.2015