【问题标题】:Signal Handler Example in The Linux Programming InterfaceLinux 编程接口中的信号处理程序示例
【发布时间】:2015-10-10 11:53:21
【问题描述】:

Michael Kerrisk 的 The Programming Linux Programming Interface 中的以下示例

static void sigHandler(int sig){
    printf("Ouch!\n");
}

int main(int argc, char *argv[])
{
    int j;

    if (signal(SIGINT, sigHandler) == SIG_ERR)
    errExit("signal");

    for (j = 0; ; j++){
        printf("%d\n", j);
        sleep(3);
    }
}

应该打印“哎哟!”每当用户键入 Control-C (CTRL+C) 时到终端;在作者自己的例子中,他输入了两次,最后用 Control-\ (CTRL+\) 退出了终端。

当我这样做时,程序在第一次执行 CTRL+C 时按预期工作。如果我第二次输入它,就像作者在他的示例中所做的那样,我的程序会退出终端 - 它不会打印“哎哟!”它也不会继续运行(循环)。

我在本书的网站上使用了与此处给出的完全相同的代码:

Ouch.c

【问题讨论】:

  • 注意:至少在标准 C++ 中(在信号处理程序中调用 printf)是未定义的行为

标签: c++ c linux operating-system signals


【解决方案1】:

您不应在信号和异常处理程序中使用 printf(),因为它们不可重入。 printf 还在内存中缓冲数据,然后将其放入控制台,因此使用 fflush() 将有助于打印,但不推荐。出于测试目的,在处理程序中使用计数器(标志)并在处理程序外使用 printf。不要使用 signal() 来注册处理程序,因为每种风格的 unix(BSD,Linux) 都没有提供相同的实现。而是使用 sigaction。

【讨论】:

  • 这不是我的代码;这是教科书中的一个例子。作者谈到了你刚才提到的一点:]
【解决方案2】:

通常signal 需要重新安装信号处理程序。否则,它默认为 SIG_DFL(与信号对应的默认操作)。 SIGINT 的默认操作是终止程序。

请注意,printf(3) 不是异步安全函数之一。所以你可以写(2)来做同样的事情。查看Async-signal-safe functions.的POSIX列表

重新安装它应该可以按预期工作:

static void sigHandler(int sig){
    signal(SIGINT, sigHandler);
    write(STDOUT_FILENO, "Ouch!\n", 6);
}

这是您应该避免使用signal 而使用sigaction 的原因之一。上述行为并非跨平台通用。因此,您运行的平台可能不是作者测试他的代码的平台,或者您使用的是不同的 Linux 内核。

在收到信号时需要重新安装信号处理程序的行为是 System V 行为。但是 BSD 语义不需要重新安装。直到最近,Linux 还显示了 System V 行为,但它似乎已在最近的内核中得到修复,我在 3.19 内核上看不到这一点,但可以看到 2.6.32 内核(相当旧)。

Signal 的文档说明:

 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
     behavior 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
     explicitly 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).)

因此您可以通过定义_BSD_SOURCE 来获得BSD 语义。因此,您观察到的行为很可能是因为您系统上的 signal 遵循 System V 语义,而最近的 Linux(可能是 Kerrisk 对其进行测试的)遵循 BSD 语义。

【讨论】:

  • 这在大约 2 十年前已在 Linux(libc 级别)上修复。大概 OP 正在使用一个古怪的系统,并没有提到这一点。
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 2023-03-26
  • 1970-01-01
相关资源
最近更新 更多