无法让 alarm() 工作两次以上

can't get alarm() to work more than twice

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 循环中。有人可以解释为什么吗?

您的程序在某些 POSIXy 操作系统上可能会像您预期的那样工作——事实上,它确实在我正在打字的计算机上像您预期的那样工作。但是,它依赖于一系列与信号相关的未指定行为,我认为您已经被其中一个绊倒了:我认为在您的计算机上,信号是 "blocked" — 它无法再次传递 —当它的处理程序正在执行时,并且使用 longjmp 跳出处理程序不会 not 解除信号阻塞。所以你绕过一次循环,然后第二个 SIGALRM 永远不会被传递,因为它被阻塞了。还有其他几个相关的问题。

您可以确定所有未指定的行为并使程序在所有 POSIXy 操作系统上都可靠,但您必须使用不同的函数来设置:sigsetjmp and sigaction. You should also get rid of the busy-waiting by using 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 up top 的调用所做的。)如果你在主线程执行 除了 睡眠等待信号到达之外还有什么要做的,你将有成为 much more careful about what you did in the signal handler, and I would advocate for using pselect and/or the self-pipe trick 而不是跳出处理程序,如果可能的话。