Не может заставить работать будильник () более двух раз
static void AlarmHandler(int sig) ;
int i=0;
jmp_buf mark;
int main(int argc, char * argv[]){
setjmp(mark);
signal(SIGALRM, AlarmHandler);
alarm(2);
while(1);
return 0;
}
static void AlarmHandler(int sig) {
signal(SIGALRM, SIG_IGN);
printf("I am in AlarmHandler: %d \n",i);
i++;
longjmp(mark, 0);
}
Когда я запускаю этот код, программа проходит AlarmHandler только один раз, а затем просто остается в ловушке внутри цикла while. Может кто-нибудь объяснить почему?
1 ответ
Ваша программа может работать, как вы ожидали, в некоторых операционных системах POSIXy - на самом деле она работает так, как вы ожидали на компьютере, на котором я это печатаю. Тем не менее, он основывается на связке неопределенного поведения, относящегося к сигналам, и я думаю, что вы сработали над одним из них: я думаю, что на вашем компьютере сигнал "заблокирован" - он не может быть доставлен снова - в то время как его выполняется обработчик, а также выпрыгивает из обработчика с longjmp
не разблокирует сигнал Таким образом, вы обходите цикл один раз, а затем второй SIGALRM никогда не доставляется, потому что он заблокирован. Есть несколько других, связанных с этим проблем.
Вы можете зафиксировать все неуказанное поведение и сделать программу надежной во всех операционных системах POSIXy, но вы должны использовать различные функции для настройки: sigsetjmp
а также sigaction
, Вы также должны избавиться от ожидания ожидания, используя sigsuspend
вместо. Исправленная программа будет выглядеть примерно так:
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <unistd.h>
static jmp_buf mark;
static void
handle_SIGALRM(int sig)
{
static int signal_count;
signal_count++;
printf("SIGALRM #%u\n", signal_count);
siglongjmp(mark, signal_count);
}
int
main(void)
{
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
if (sigprocmask(SIG_BLOCK, &mask, &omask)) {
perror("sigprocmask");
return 1;
}
struct sigaction sa;
sigfillset(&sa.sa_mask);
sa.sa_flags = 0; // DO interrupt blocking system calls
sa.sa_handler = handle_SIGALRM;
if (sigaction(SIGALRM, &sa, 0)) {
perror("sigaction");
return 1;
}
if (sigsetjmp(mark, 1) >= 4)
return 0;
alarm(1);
sigsuspend(&omask);
perror("shouldn't ever get here");
return 1;
}
Я, вероятно, должен сказать несколько слов о безопасности сигнала: в этой программе безопасно звонить printf
а также siglongjmp
из обработчика сигнала, потому что я организовал доставку SIGALRM только тогда, когда основной поток выполнения заблокирован sigsuspend
, (Это то, что призыв к sigprocmask
наверх.) Если бы у вас было что-то делать в основном потоке выполнения, кроме режима ожидания в ожидании поступления сигнала, вам нужно было бы быть гораздо более осторожным в отношении того, что вы делали в обработчике сигналов, и я бы рекомендовал использовать его. pselect
и / или трюк с самоотводом вместо того, чтобы выпрыгнуть из обработчика, если это вообще возможно.