鉴于此代码:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void sigint_handler(int h)
{
printf("Hey! I caught a SIGINT! :)\n");
}
int main()
{
struct sigaction act;
act.sa_handler = &sigint_handler;
if (0 != sigaction(SIGINT, &act, NULL)) {
perror("unable to setup the SIGINT handler!\n");
return 1;
}
while(1) { }
return 0;
}
使用以下选项使用gcc 7.2.0
(kernel 4.13.12
)编译:-Wall -pedantic -ansi -std=gnu11
。
第一个信号总是被捕获,但有时,第二个信号没有被捕获,有时它是。
我在进程启动时发送垃圾邮件Ctrl-C
时遇到了这个错误。
我错过了捕捉所有信号的内容?
正如Martin James在comment观察到的那样:
您的SIGINT处理程序只有一行 - 对非同步信号安全的函数的调用:(
稍后,我observed:
你不知道
struct sigaction
中的其他字段是什么设置的,因为你没有初始化act
。如果将记录的字段设置为已知值,则可能会获得更好的行为。您可以在POSIX信号处理程序中使用write()
(不是在标准C中,但幸运的是您没有使用标准C)。你不应该使用printf()
,虽然在这种情况下它不太可能造成任何麻烦。write()
的一个小优势 - 无需担心应用程序级缓冲。
问题How to avoid using printf()
in a signal handler讨论了哪些函数可以在信号处理程序中使用。
请注意,<string.h>
标头中的函数(如strlen()
和strchr()
)未列在异步信号安全的函数中。我觉得遗漏令人费解,但这就是POSIX(2008年及之前)所说的。
(这对POSIX 2008来说是准确的.POSIX 2016的一个变化是在Signal Concepts的列表中添加了许多信号安全例程,包括strlen()
和strchr()
- 这对我来说很有意义。)
我改编了这样的代码:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void sigint_handler(int h)
{
char message[] = "Hey! I caught a SIGINT x! :\n";
char *x = message;
while (*x != 'x' && *x != '\0')
x++;
if (*x != '\0')
*x = (h % 10) + '0';
write(1, message, sizeof(message) - 1);
}
int main(void)
{
struct sigaction act = { 0 };
act.sa_handler = &sigint_handler;
if (0 != sigaction(SIGINT, &act, NULL))
{
perror("Unable to setup the SIGINT handler!\n");
return 1;
}
while (1)
{
printf("Pausing for a moment...\n");
pause();
printf("You interrupted my dozing\n");
}
return 0;
}
代码使用pause()
而不是在繁忙循环中旋转。这是一个不寻常的系统调用;它永远不会正常返回(exec*()
系列函数也不会正常返回)。
我用严格的警告选项编译:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes sig13.c -o sig13
$
如果我没有使用h
(信号处理程序的参数),代码将无法编译,所以我使用它。代码避免使用字符串处理函数(char *x = strchr(message, 'x'); if (x != 0) *x = (h % 10) + '0';
会更清晰)。该函数是static
,因为它不会在此文件之外使用 - 因此没有标头来声明它。
执行时(在Mac上运行macOS High Sierra 10.13.2,使用GCC 7.2.0),它会产生如下输出:
$ ./sig13
Pausing for a moment...
^CHey! I caught a SIGINT 2! :
You interrupted my dozing
Pausing for a moment...
^CHey! I caught a SIGINT 2! :
You interrupted my dozing
Pausing for a moment...
^CHey! I caught a SIGINT 2! :
You interrupted my dozing
Pausing for a moment...
^CHey! I caught a SIGINT 2! :
You interrupted my dozing
Pausing for a moment...
^CHey! I caught a SIGINT 2! :
You interrupted my dozing
Pausing for a moment...
^\Quit: 3
$
这个主要的道德是“确保你的变量被正确初始化”。次要道德是确保您的信号处理程序是干净的。
我的代码有两个问题,首先,正如@MartinJames和@ j31d0所提到的,printf
函数不是async-signal-safe,所以它不能在信号处理程序中使用。它可以很容易地被write
系统调用替换,它是异步信号安全的:
char message[255] = "Hey! I caught a SIGINT :)\n";
write(1, message, 255);
其次,变量act
没有正确初始化(正如@JonathanLeffler所提到的):
sigset_t mask;
struct sigaction act;
sigemptyset(&mask);
act.sa_handler = &sigint_handler;
act.sa_mask = mask;
act.sa_flags = 0;
最后,一个工作代码将是以下内容:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void sigint_handler(int h)
{
char message[255] = "Hey! I caught a SIGINT :)\n";
write(1, message, 255);
}
int main()
{
sigset_t mask;
struct sigaction act;
sigemptyset(&mask);
act.sa_handler = &sigint_handler;
act.sa_mask = mask;
act.sa_flags = 0;
if (0 != sigaction(SIGINT, &act, NULL)) {
perror("unable to setup the SIGINT handler!\n");
return 1;
}
while(1) { }
return 0;
}
希望这可以帮助!