SIGINT只抓了一次

问题描述 投票:-1回答:2

鉴于此代码:

#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时遇到了这个错误。

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

c signals posix system-calls
2个回答
2
投票

正如Martin Jamescomment观察到的那样:

您的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
$

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


0
投票

我的代码有两个问题,首先,正如@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;
}

希望这可以帮助!

© www.soinside.com 2019 - 2024. All rights reserved.