无法让 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 而不是跳出处理程序,如果可能的话。
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 而不是跳出处理程序,如果可能的话。