Не может заставить работать будильник () более двух раз

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 и / или трюк с самоотводом вместо того, чтобы выпрыгнуть из обработчика, если это вообще возможно.

Другие вопросы по тегам