【问题标题】:Signals - c99 vs gnu99信号 - c99 与 gnu99
【发布时间】:2011-11-27 19:00:14
【问题描述】:

我有以下代码。当我使用 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;
}

【问题讨论】:

  • 您应该使用 sig_atomic_t,而不是整数来处理信号。如果我是你,我会避免这整个混乱,只使用 sigwait。
  • @EthanSteinberg 呃?根据 C 和 POSIX,函数的原型是 void (*func)(int))
  • @Let_Me_Be:我相信 Ethan 指的是got_signal,而不是信号处理程序的参数。
  • @Let_Me_Be 这是带有 sigwait 且没有忙于等待 ideone.com/6V0No

标签: c signals gnu c99


【解决方案1】:

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

Linux 手册页有这个,特别是在 Portability 部分:

在最初的 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,它也是隐式定义的,当然也可以是显式的。 它的定义。
    在 glibc 2 及更高版本上,如果未定义 _BSD_SOURCE 功能测试宏,则 signal() 提供 System 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。

【讨论】:

    【解决方案2】:

    问题在于 signal 也会重置信号处理机制,您必须将 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,您可以调用任何函数或处理任何数据。如果您愿意,我可以向您展示示例代码。

    【讨论】:

      【解决方案3】:

      我同意 Ethan Steinberg 的观点——“忙碌的等待”是错误的......

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

      【讨论】:

        猜你喜欢
        • 2011-07-15
        • 2013-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-15
        • 2015-08-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多