SIGINT 只捕获了一次

SIGINT caught only one time

鉴于此代码:

#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(内核 4.13.12)编译:-Wall -pedantic -ansi -std=gnu11.

第一个信号总是被捕获,但有时第二个信号捕获不到,有时捕获到。

我在进程启动时发送垃圾邮件 Ctrl-C 时遇到了这个错误。

我错过了什么来捕获所有信号?

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

希望对您有所帮助!

作为Martin James observed in a :

Your SIGINT handler has only one line - a call to a function that is not async-signal safe:(

稍后,我 :

You've no idea what the other fields in the struct sigaction are set to because you didn't initialize act. Maybe you will get better behaviour if you set the documented fields to known values. You can use write() in a POSIX signal handler (not in standard C, but fortunately you're not using standard C). You shouldn't use printf(), though in this context it is unlikely to cause any trouble. One minor advantage of write() — there's no application level buffering to worry about.

问题 How to avoid using printf() in a signal handler discusses which functions can be used in a signal handler. Note that the functions from the <string.h> header such as strlen() and strchr() are not listed amongst those that are async-signal safe. I find that omission puzzling, but that's what POSIX (2008 and earlier) says. (This was accurate for POSIX 2008. One of the changes in POSIX 2016 is that a number of signal-safe routines have been added to the list in 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 != '[=10=]')
        x++;
    if (*x != '[=10=]')
        *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() 而不是在一个繁忙的循环中旋转。这是一个不寻常的系统调用;它通常不会 return(exec*() 函数族通常也不会 return)。

我使用严格的警告选项进行编译:

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

主要寓意是"make sure your variables are properly initialized"。次要道德是确保您的信号处理程序是干净的。