SIGINT只抓了一次

时间:2018-01-03 01:24:51

标签: c signals posix system-calls

鉴于此代码:

HandleRequestAsync()

使用以下选项使用#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)进行编译:{{1​​}}。

第一个信号总是被捕获,但有时候,第二个信号没有被捕获,有时它是。

我在处理过程中发现4.13.12时遇到了这个错误。启动。

我错过了捕获所有信号的内容?

2 个答案:

答案 0 :(得分:2)

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
$

这方面的主要道德是“确保你的变量被正确初始化”。次要道德是确保你的信号处理程序是干净的。

答案 1 :(得分:0)

我的代码存在两个问题,首先,正如@MartinJames和@ j31d0所提到的,printf函数不是async-signal-safe,所以它不能在信号处理程序中使用。它可以很容易地被write系统调用替换,它是async-signal-safe:

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

希望这有帮助!