信号 - c99 vs gnu99

时间:2011-11-27 19:00:14

标签: c signals gnu c99

我有以下代码。当我使用gnu扩展(-std=gnu99)编译它时,程序将在结束之前捕获5 SIGINT(我期望)。在没有它的情况下编译(-std=c99)在第二行之后结束(并且只输出一行)。

我错过了什么?

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

int int_stage = 0;
int got_signal = 0;

void sigint(int parameter)
{
  (void)parameter;
  got_signal = 1;
  int_stage++;
}

int main()
{
  signal(SIGINT,sigint);

  while(1)
  {
    if (got_signal)
    {
      got_signal = 0;
      puts("still alive");
      if (int_stage >= 5) exit(1);
    }
  }
  return 0;
}

3 个答案:

答案 0 :(得分:6)

使用sigaction(2)而不是signal(2)

Linux手册页特别在“可移植性”部分中有这个:

  

在原始UNIX系统中,当使用signal()建立的处理程序被调用时          传递信号,信号的处置将重置为SIG_DFL,系统也会          不阻止传递信号的其他实例。 System V还提供了这些语义          信号()。这很糟糕,因为信号可能会在处理程序有机会之前再次传递          重建自己。此外,快速传递相同信号可能导致递归          调用处理程序。

     

BSD通过改变信号处理的语义改进了这种情况(但不幸的是,          在使用signal()建立处理程序时,默默地改变了语义。在BSD上,当一个信号          调用处理程序,不重置信号处理,并且信号的其他实例是          阻止在处理程序执行时传递。

     

Linux的情况如下:

     
      
  • 内核的signal()系统调用提供了System V语义。

  •   
  • 默认情况下,在glibc 2及更高版本中,signal()包装器函数不会调用内核系统        呼叫。相反,它使用提供BSD语义的标志来调用sigaction(2)。这种默认行为        只要定义了_BSD_SOURCE特征测试宏,就会提供ior。默认情况下,_BSD_SOURCE        被定义为;如果定义了_GNU_SOURCE,它也是隐式定义的,当然可以是explic-        它定义了。
           在glibc 2及更高版本上,如果未定义_BSD_SOURCE特征测试宏,则signal()提供        系统V语义。 (如果有的话,则不提供_BSD_SOURCE的默认隐式定义        在其标准模式之一(-std = xxx或-ansi)中调用gcc(1)或定义其他各种功能        测试宏,如_POSIX_SOURCE,_XOPEN_SOURCE或_SVID_SOURCE;见feature_test_macros(7)。)

  •   

使用std=gnu99,您将获得BSD语义。使用-std=c99,您将获得System V语义。因此,在一种情况下(BSD)“重新安装”信号处理程序,并将信号处理重置回另一个(系统V)中的SIG_DFL。

答案 1 :(得分:2)

问题是信号还会重置信号处理机制,你必须将sigint重置为信号处理程序。从手册

  

在原始UNIX系统中,当使用signal()建立的处理程序通过传递信号来调用时,信号的处置将重置为SIG_DFL,并且系统不会阻止其他实例的传递。信号。 System V还为signal()提供了这些语义。这很糟糕,因为信号可能会在处理程序有机会重新建立之前再次传递。此外,快速传递相同信号可能会导致处理程序的递归调用。

这是如何使用旧的陈旧的signal()调用来完成的。 注意int_stage和got_signal必须是sig_atomic_t。 您也可以只调用异步安全函数,查看here以获取列表。

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

sig_atomic_t int_stage = 0;  
sig_atomic_t got_signal = 0;

void sigint(int parameter)
{
  (void)parameter;
  got_signal = 1;
  int_stage++;
}

int main()
{
   signal(SIGINT,sigint);

   while(1)
   {
      if (got_signal)
      {
       signal(SIGINT,sigint);
       got_signal = 0;
       puts("still alive");
       if (int_stage >= 5) exit(1);
    }
 }
 return 0;
}

请考虑使用sigaction或sigwait。

Sigaction实际上具有相同的想法,但重新初始化信号处理程序没有废话。 Sigwait会在收到信号之前停止你的线程。因此,对于sigwait,您可以调用任何函数或处理任何数据。如果你愿意,我可以给你看示例代码。

答案 2 :(得分:0)

我同意Ethan Steinberg - “忙碌的等待”是如此错误......

但问题是你没有重置信号处理程序。 AFAIK,您必须使用任何版本的C来执行此操作(再次调用“signal(SIGINT,sigint)”。