【问题标题】:C Language - Fail to reset SIGINT to defaultC 语言 - 无法将 SIGINT 重置为默认值
【发布时间】:2021-03-22 17:33:20
【问题描述】:

我正在学习 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 中是如何工作的),这将非常有帮助。

【问题讨论】:

  • 恕我直言,以我的拙见,您应该首先查看此geeksforgeeks.org/signals-c-language 如果您的目标只是测试信号,那么选择一个 hello world 会更好。专注于程序的目标可以减少代码中不必要的复杂性。
  • 顺便说一句,试图在信号处理程序中捕获信号是行不通的。到那时信号将处于挂起状态。这就像在中断处理程序中等待中断一样。那也可以是你的问题。
  • 感谢您的建议。我已经查看了您附加的链接,但没有发现任何有用的信息......在这里我试图在handlermain 之间跳转,但我不明白为什么@987654333 中else 语句中的signal(SIGINT, handler) @ 不起作用
  • @breiters 因为我不能堆叠信号,所以我不知道如何像alarm 那样迭代地运行main =&gt; handler =&gt; main =&gt; handler。或者也许我可以在SIGINT handler 中发出警报并在SIGALRM handler 中处理主要部分
  • @SanaeKochiya 如果您不需要在信号处理期间将信号处理程序设置为SIG_DFL,您也可以简单地从信号处理程序返回没有 longjmp

标签: c signals


【解决方案1】:

您需要保存信号掩码,因此使用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() 和 siglongjmp() 也执行非本地 goto,但提供进程信号掩码的可预测处理

【讨论】:

  • 感谢您的回答!我对&lt;setjmp.h&gt; 库一无所知,但这听起来像所谓的“goto”(即setjmp + longjmp)组合只能在两个不同的上下文之间工作一次。
【解决方案2】:

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

修复是三重的。

首先,使用sigsetjmp(buf, 1) 保存调用掩码并使用siglongjmp 进行跳转。这会将信号掩码恢复到执行时的预期值。

其次,使用sigaction 而不是signalsigaction 将强制您明确选择在处理程序执行期间屏蔽信号等行为。 signal, on the other hand, does not mandate consistent behavior across platforms.

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

【讨论】:

  • 感谢您的详细解释。这是我第一次听说SIGINT 将在处理程序中被屏蔽。请问SIGINT是唯一会被屏蔽的信号吗?例如,SIGALRM 似乎不会被屏蔽(示例可以在这篇文章中的代码中找到)和SIGQUIT
  • @SanaeKochiya,如果您使用signal,答案因平台而异。 Use sigaction instead,您将知道您的详细问题的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多