信号 - c99 与 gnu99

发布于 2024-12-18 09:51:11 字数 572 浏览 4 评论 0原文

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

I have the following code. When I compile it with the gnu extensions (-std=gnu99), the program will catch 5 SIGINT before ending (which I would expect). When compiled without it (-std=c99) ends after the second (and only outputs one line).

What am I missing?

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

女皇必胜 2024-12-25 09:51:11

使用 sigaction(2) 而不是 信号(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,它也是隐式定义的,并且当然可以是显式的
    明确定义。
    在 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。

Use sigaction(2) rather than signal(2).

The Linux man page has this, in particular, in the Portability section:

In the original UNIX systems, when a handler that was established using signal() was invoked by the
delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did
not block delivery of further instances of the signal. System V also provides these semantics for
signal(). This was bad because the signal might be delivered again before the handler had a chance
to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive
invocations of the handler.

BSD improved on this situation by changing the semantics of signal handling (but, unfortunately,
silently changed the semantics when establishing a handler with signal()). On BSD, when a signal
handler is invoked, the signal disposition is not reset, and further instances of the signal are
blocked from being delivered while the handler is executing.

The situation on Linux is as follows:

  • The kernel's signal() system call provides System V semantics.

  • By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system
    call. Instead, it calls sigaction(2) using flags that supply BSD semantics. This default behav‐
    ior is provided as long as the _BSD_SOURCE feature test macro is defined. By default, _BSD_SOURCE
    is defined; it is also implicitly defined if one defines _GNU_SOURCE, and can of course be explic‐
    itly defined.
    On glibc 2 and later, if the _BSD_SOURCE feature test macro is not defined, then signal() provides
    System V semantics. (The default implicit definition of _BSD_SOURCE is not provided if one
    invokes gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines various other feature
    test macros such as _POSIX_SOURCE, _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)

Using std=gnu99, you're getting BSD semantics. Using -std=c99, you're getting System V semantics. So the signal handler is "reinstalled" in one case (BSD), and the signal disposition is reset back to SIG_DFL in the other (System V).

娇纵 2024-12-25 09:51:11

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

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

这是如何使用旧的过时的 signal() 调用来做到这一点。
请注意 int_stage 和 got_signal 必须是 sig_atomic_t。
您也可以只调用异步安全函数,请查看 此处查看列表。

#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,您可以调用任何函数或处理任何数据。如果您愿意,我可以向您展示示例代码。

The problem is that signal also resets the signal handling mechanism, you have to reset sigint as the signal handler. From the manual

In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. System V also provides these semantics for signal(). This was bad because the signal might be delivered again before the handler had a chance to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive invocations of the handler.

This is how to do it with the old antiquated signal() call.
Note how int_stage and got_signal have to be sig_atomic_t.
You can also only call async safe functions, look at here for a list.

#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;
}

Please consider either using sigaction, or sigwait.

Sigaction would have practically the same idea, but no nonsense with re-initializing the signal handler. Sigwait would stop your thread until a signal is received. So, for sigwait, you can call any function or deal with any data. I can show you example code if you desire.

国产ˉ祖宗 2024-12-25 09:51:11

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

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

I agree with Ethan Steinberg - the "busy wait" is So Wrong...

But the problem is that you're failing to reset the signal handler. AFAIK, you must do this (call "signal(SIGINT,sigint)" again) with any version of C.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文