为什么要触发SIGINT两次按预期工作?

时间:2014-09-21 13:18:09

标签: c signals

这里我有一些代码示例:

/* some headers */
…
/****************/

void handler( int signum ){
    printf("Signal number: %d received\n", signum);
    signal(signum, SIG_DFL);
}

int main( int argc, char* argv[] ){
    signal(SIGINT, handler);

    while( "no signal was received" ){
        printf("another loop round\n");
        sleep(3);
    }

    return EXIT_SUCCESS;
}

我期待我的程序退出,在我的处理程序函数中定义消息,在CTRL + C键盘组合之后,就像它通常发生的那样,但我必须输入两次命令,并且只在我第二次尝试之后停止。我还想问一个关于signal功能的另一个问题。我发现了很多引用,人们会建议使用sigaction函数而不是signal,但是其中一些人说它出于兼容性原因而更好地使用{signal 1}}。你能给我一些关于我应该使用哪些提示的提示吗?提前致谢

3 个答案:

答案 0 :(得分:5)

使用此行

signal(SIGINT, handler);

您通过调用handler()替换了默认行为,即退出程序。这就是为什么程序不会在接收到信号时退出的原因。

handler()然后调用

signal(signum, SIG_DFL);

重新设置SIGINT的默认行为,即在(下一次)接收信号时退出程序。


OT:不应该从信号处理程序中调用printf()(以及其他),在它(以及其他人)不保证异步信号安全。有关这些功能的列表,请参阅man 7 signal

答案 1 :(得分:3)

  • 首先Ctrl-C发送SIGINT进行处理,你的程序有一个回调 '处理'对于SIGINT处理,所以你的程序不会退出。
  • 由于处理程序将SIGINT重置为默认操作(SIG_DFL),因此下一步 收到的SIGINT将触发默认操作(退出)。
  • 第二个Ctrl-C触发默认操作,程序退出。

希望它清楚。

答案 2 :(得分:2)

默认情况下,您的程序有一个退出的SIGINT处理程序。您已将其替换为signal(SIGINT, handler);如果您希望程序在运行处理程序时退出,则需要自己调用exit。您可以将处理程序修改为:

void handler( int signum ){
    printf("Signal number: %d received\n", signum);
    exit(1);
}

为避免出现问题,您不应该从信号处理程序中调用printf,因为printf不可重入。 glibc documentation给出了问题的描述:

  

如果函数使用并修改了您提供的对象,则它可能是不可重入的;如果两个调用使用相同的对象,则会发生干扰。   当您使用流进行I / O时会出现这种情况。假设信号处理程序使用fprintf打印消息。假设在传递信号时,程序正在使用相同的流进行fprintf调用。信号处理程序的消息和程序的数据都可能被破坏,因为两个调用都在相同的数据结构上运行 - 流本身。

如果您没有在处理程序中退出程序,那么您的程序将从中断处继续。另一个选项是通过sleep的返回值检测SIGINT。如果sleep因为SIGINT之类的信号被提升而提前退出,则可以使用返回值进行检查。 sleep的{​​{3}}表示

  

返回值顶部

  Zero if the requested time has elapsed, or the number of seconds left
  to sleep, if the call was interrupted by a signal handler.

当sleep返回大于0的值时,你可以修改你的循环以退出,这意味着它被信号提前中断。像这样的东西会起作用:

signal(SIGINT, handler);

while( sleep(3) == 0 ){
    printf("another loop round\n");
}

至于您关于signalsigaction - sigaction的问题是首选方式。关于为什么可以在StackOverflow问题和man page

中找到原因的更多信息