Linux编程接口中的信号处理程序示例

下面的例子来自Michael Kerrisk编程的Linux编程接口

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) 在笔者自己的例子中,他最后input了两遍,最后用Control- \(CTRL + \)退出terminal。

当我这样做时,程序按CTRL + C的第一次执行按预期工作。 如果我第二次input它,就像作者在他的例子中那样,我的程序退出了terminal – 它不打印“哎呀! 也不会继续运行(循环)。

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

Ouch.c

通常情况下, signal需要重新安装信号处理程序。 否则,它将显示SIG_DFL(对应于该信号的默认操作)。 SIGINT的默认操作是终止程序。

请注意, printf(3)不是异步安全函数之一。 所以你可以写(2)来做同样的事情。 请参阅POSIX 异步信号安全功能列表。

重新安装它应该使预期的工作:

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

这就是为什么你应该避免signal和使用sigaction的原因之一。 上面提到的行为不是跨平台通用的。 所以也许,你运行的平​​台不是作者测试他的代码,或者你正在使用不同的Linux内核。

系统V行为时,需要在接收信号时重新安装信号处理程序的行为。 但是BSD语义不需要重新安装。 直到最近,Linux才显示出系统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语义。

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