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

时间:2009-11-01 06:50:29

标签: c linux signals

第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);
}

3 个答案:

答案 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进行调用,问题就解决了:)