SIGALRM的信号处理程序即使在处理程序中重置也不起作用

第10.6节的示例代码,预期的结果是:
经过多次迭代,getpwnam使用的静态结构将被破坏,程序将以SIGSEGV信号终止。

但在我的平台上,Fedora 11,gcc(GCC)4.4.0,结果是

[Langzi @ Freedom apue] $ ./corrupt
在sig_alarm

我只能看到sig_alarm的输出一次,程序似乎因为某种原因挂了,但它确实存在,并且仍在运行。
但是,当我尝试使用gdb来运行程序,似乎没问题,我会定期看到sig_alarm的输出。

而且从我的手册来看,它说信号处理程序将在信号处理后设置为SIG_DEF,系统不会阻塞信号。 所以在我的信号处理程序的开始我重置信号处理程序。

也许我应该用sigaction来代替,但是我只想知道正常运行和gdb运行之间的区别的原因。

任何意见和帮助将不胜感激。

以下是我的代码:

 #include "apue.h" #include <pwd.h> void sig_alarm(int signo); int main() { struct passwd *pwdptr; signal(SIGALRM, sig_alarm); alarm(1); for(;;) { if ((pwdptr = getpwnam("Zhijin")) == NULL) err_sys("getpwnam error"); if (strcmp("Zhijin", pwdptr->pw_name) != 0) { printf("data corrupted, pw_name: %s\n", pwdptr->pw_name); } } } void sig_alarm(int signo) { signal(SIGALRM, sig_alarm); struct passwd *rootptr; printf("in sig_alarm\n"); if ((rootptr = getpwnam("root")) == NULL) err_sys("getpwnam error"); alarm(1); } 

根据标准,你真的不允许在信号处理器中做很多事情。 所有你保证能够在信号处理函数中做的事情,不会导致未定义的行为,就是调用信号 ,并为一个类型为sig_atomic_t的易失性静态对象赋值

前几次我运行这个程序,在Ubuntu Linux上,看起来像在信号处理程序中的警报呼叫没有工作,所以在第一次警报后,主循环刚刚保持运行。 当我稍后尝试时,程序运行了几次信号处理程序,然后挂起。 所有这一切都与未定义的行为一致:有时,程序失败,或多或少有趣的方式。

具有未定义行为的程序在调试器中以不同的方式工作并不罕见。 调试器是一个不同的环境,你的程序和数据可以以不同的方式在内存中进行布局,所以错误可以以不同的方式表现出来,或者根本不表现出来。

我通过添加一个变量来使程序工作:

 volatile sig_atomic_t got_interrupt = 0; 

然后我改变你的信号处理器到这个非常简单的处理器:

 void sig_alarm(int signo) { got_interrupt = 1; } 

然后我将实际的工作插入到主要的无限循环中:

 if (got_interrupt) { got_interrupt = 0; signal(SIGALRM, sig_alarm); struct passwd *rootptr; printf("in sig_alarm\n"); if ((rootptr = getpwnam("root")) == NULL) perror("getpwnam error"); alarm(1); } 

我认为你提到的“apue”是“UNIX环境下的高级编程”一书,这里我没有,所以我不知道这个例子的目的是为了表明你不应该乱七八糟在信号处理程序内部,或者只是通过中断程序的正常工作而产生问题。

根据规范,函数getpwnam不可重入,并不保证是线程安全的。 既然你是在两个不同的控制线程中访问结构(信号处理程序在不同的线程上下文中有效地运行),你就会遇到这个问题。 每当你有并发或并行执行的时候(就像使用pthreads或者使用信号处理器的时候一样),你必须确保不要修改共享状态(比如'getpwnam'所拥有的结构),如果你这样做,那么适当的锁定/同步必须使用。

此外, 信号函数已被弃用,以支持sigaction函数。 为了确保注册信号处理程序时的可移植行为,您应该始终使用sigaction调用。

使用sigaction函数,您可以使用SA_RESETHAND标志来重置默认处理程序。 您也可以使用sigprocmask函数来启用/禁用信号的传递,而无需修改其处理程序。

 #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void sigalrm_handler(int); int main() { signal(SIGALRM, sigalrm_handler); alarm(3); while(1) { } return 0; } void sigalrm_handler(int sign) { printf("I am alive. Catch the sigalrm %d!\n",sign); alarm(3); } 

例如,我的代码运行在主要什么都不做,每隔3秒我的程序说我活着x)

我认为,如果你这样做,我做了处理函数警报与值3调用,问题得到解决:)