第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);
}
答案 0 :(得分:7)
根据标准,你真的不允许在信号处理程序中做很多事情。保证能够在信号处理功能中完成所有操作,而不会导致未定义的行为,是调用信号,并为 sig_atomic_t类型的易失性静态对象赋值强>
我在Ubuntu Linux上运行这个程序的前几次,看起来你在信号处理程序中对 alarm 的调用不起作用,所以main中的循环在第一次运行后才继续运行报警。当我稍后尝试时,程序运行了几次信号处理程序,然后挂起。所有这些都与未定义的行为一致:程序有时会以各种或多或少的有趣方式失败。
具有未定义行为的程序在调试器中以不同方式工作的情况并不少见。调试器是一个不同的环境,例如,您的程序和数据可以以不同的方式在内存中布局,因此错误可以以不同的方式显示,或者根本不显示。
我通过添加变量来使程序正常工作:
volatile sig_atomic_t got_interrupt = 0;
然后我将你的信号处理程序改为这个非常简单的处理程序:
void sig_alarm(int signo) {
got_interrupt = 1;
}
然后我将实际工作插入main中的无限循环:
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环境中的高级编程”一书,我在这里没有,所以我不知道这个例子的目的是否表明你不应该'弄乱信号处理程序内部的东西,或者只是通过中断程序的正常工作可能导致问题。
答案 1 :(得分:1)
根据规范,函数getpwnam不可重入,不保证是线程安全的。由于您在两个不同的控制线程中访问结构(信号处理程序有效地在不同的线程上下文中运行),因此您遇到了这个问题。无论何时你有并发或并行执行(如使用pthreads或使用信号处理程序时),你必须确保不要修改共享状态(例如'getpwnam'所拥有的结构),如果你这样做,那么适当的锁定/同步必须使用。
此外,不推荐signal函数支持sigaction函数。为了确保在注册信号处理程序时的可移植行为,您应该始终使用sigaction调用。
使用sigaction函数,您可以使用SA_RESETHAND标志来重置默认处理程序。您还可以使用sigprocmask功能启用/禁用信号传递,而无需修改其处理程序。
答案 2 :(得分:1)
#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);
}
例如,我的代码在main中运行什么都不做,每3秒我的程序说我活着x)
我认为如果你按照我在处理函数中调用值3进行调用,问题就解决了:)