不能让闹钟()工作两次以上

时间:2018-02-20 22:05:16

标签: c alarm setjmp

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 个答案:

答案 0 :(得分:2)

您的程序可能正如您在某些POSIXy操作系统上所期望的那样 - 事实上, 按照您在计算机上的预期工作<我>打字。然而,它依赖于一系列与信号有关的未明确的行为,我认为你已经绊倒了其中一个:我认为在你的计算机上,一个信号已被阻止&#34; - 它不能再次传递 - 当它的处理程序正在执行时,并且,使用longjmp跳出处理程序会使解锁信号。所以你绕过循环一次,然后第二个SIGALRM永远不会被传递,因为它被阻止了。还有其他一些相关问题。

您可以确定所有未指定的行为并使程序在所有POSIXy操作系统上都可靠,但您必须使用不同的功能进行设置:sigsetjmpsigaction。你也应该使用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;
}

我应该对信号安全说几句话:在这个程序中, 可以安全地从printfsiglongjmp调用信号处理程序,因为我已安排SIGALRM 可交付,而主要执行线程在sigsuspend被阻止。 (那就是对sigprocmask的调用是什么。)如果你在主执行线程中有任何事情除了睡觉等待信号到达,你会必须much more careful关于你在信号处理程序中所做的事情,我会主张使用pselect和/或self-pipe trick而不是跳出处理程序,如果可能的话。