C 语言 - 无法将 SIGINT 重置为默认值

C Language - Fail to reset SIGINT to default

我正在学习 C 语言的信号概念,在构建练习程序时遇到了问题。 在下面的代码中,我试图在每次用户按下“ctrl-c”后重置SIGINT并记录用户按下“ctrl-c”的次数。

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>

void handler(int signo);
jmp_buf buf;
int int_counting = 1;

void handler(int signo)
{
    signal(SIGINT, SIG_DFL);
    int_counting++;
    // just tried to add another "signal(SIGINT, handler)" here
    // but saw the same result
    longjmp(buf, 1);
}

int main()
{
    if ((signal(SIGINT, handler) == SIG_ERR))
    {
        printf("Fail to catch the signal\n");
    }
    if (!setjmp(buf))
    {
        printf("Waiting for any signals ... \n");
    }
    else
    {   
        if (!setjmp(buf)){} // to reset "setjmp" to zero
        printf("Pressed 'ctrl-c' for %d times\n", int_counting);
        printf("Waiting for another signal\n");
        signal(SIGINT, handler);
    }
    while (int_counting <= 5)
    {
        sleep(1);
        printf("Processing ...\n");
    }
}

但是,在第一个信号之后,无法将其他信号发送到 handler,输出如下所示:

谁能解释一下原因吗?

下面是信号似乎不会被屏蔽的示例。

// Examples for SIGALRM

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

int counting = 0;

void handler(int signo)
{
    printf("%d\n", counting);
    while (counting < 5)
    {
        signal(SIGALRM, handler);
        printf("%d\n", beeps);
        counting++
        alarm(1);
    }
}

void main(void)
{
    if (signal(SIGALRM, handler) == SIG_ERR)
    {
        printf("cannot catch SIGALRM\n");
    }
    alarm(1);
    while (counting < 5)
    {
        pause();
    }
    return;
}
// Example for SIGQUIT

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>

jump_buf buf;

void handler(int signo)
{
    signal(SIQQUIT, handler);
    longjmp(buf, 1);
}

int main()
{
    signal(SIQQUIT, handler);
    if (!setjmp(buf))
    {
        printf("begin ...\n");
    }
    else
    {
        print("restart ...\n");
    }
    while (1)
    {
        sleep(1);
        printf("waiting for sinals ...\n");
    }
}

虽然我原来的问题得到了回答,但如果有任何进一步的解释 为什么这些信号不会被屏蔽(或者请告诉我它们在 C 中是如何工作的),这将非常有帮助。

您需要保存 信号掩码 ,因此使用 siglongjmp()sigsetjmp()

这按预期工作:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>

void handler(int signo);
sigjmp_buf buf;
int int_counting = 0;

void handler(int signo)
{
    signal(SIGINT, SIG_DFL);
    int_counting++;
    // just tried to add another "signal(SIGINT, handler)" here
    // but saw the same result
    siglongjmp(buf, 1); // 1: "fake" return value
}
int main()
{
    if ((signal(SIGINT, handler) == SIG_ERR))
    {
        printf("Fail to catch the signal\n");
    }
    if (!sigsetjmp(buf, 1)) // 1 (or any non-zero value): save sigmask
    {
        printf("Waiting for any signals ... \n");
    }
    else // this code is executed when the "fake" return value of sigsetjmp is non-zero
    {
        printf("Pressed 'ctrl-c' for %d times\n", int_counting);
        printf("Waiting for another signal\n");
        signal(SIGINT, handler);
    }
    while (int_counting <= 5)
    {
        sleep(1);
        printf("Processing ...\n");
    }
}

它在手册页中有描述,例如man setjmp:

sigsetjmp() and siglongjmp() also perform nonlocal gotos, but provide predictable handling of the process signal mask

SIGINT 在您的信号处理程序执行期间被屏蔽(阻塞),并且在您 longjmp 退出时保持屏蔽。这就是为什么您看不到后续 SIGINT 的原因——信号掩码阻止了它们的传递。

修复是三方面的。

首先,使用sigsetjmp(buf, 1) to save the calling mask and siglongjmp进行跳跃。这会将信号掩码恢复到执行时的预期值。

二、使用sigaction rather than signal. sigaction will force you to explicitly choose behavior like masking out signals during handler execution. signal, on the other hand, does not mandate consistent behavior across platforms.

第三,根本不要使用(sig)longjmp。您遇到这个麻烦是因为您从异步执行的用户代码(您的处理程序)中发出非本地 goto。推理那种代码很容易出错