为什么信号暂停会导致程序永远睡眠?

时间:2016-12-25 09:08:33

标签: c unix signals

APUE书中说:如果信号发生在sig_int_flag的测试之后,但在调用pause之前,该过程可能会永远进入睡眠状态。

我不知道为什么,有人可以告诉我吗? 非常感谢。

int sig_int();                 /* my signal handling function */
int sig_int_flag;              /* set nonzero when signal occurs */

int main() {
    signal(SIGINT, sig_int)    /* establish handler */
    .
    .
    .
    while (sig_int_flag == 0)
        pause();               /* go to sleep, waiting for signal */
}

int sig_int() {
    signal(SIGINT, sig_int);   /* reestablish handler for next time */
    sig_int_flag = 1;          /* set flag for main loop to examine */
}

2 个答案:

答案 0 :(得分:1)

如果在您描述的确切时间发出中断信号:

  • 该标志已被检查为false:进入循环
  • 信号重置自己,将标志设置为1,但为时已晚(测试已完成)
  • 由于已经输入了循环,因此调用pause()并且程序等待

也就是说,如果再次触发CTRL + C / SIGINT,你可以退出循环,因此它不是那么关键,因为这个信号可以手动发出。

如果您想检查该行为,我建议您添加sleep声明:

while (sig_int_flag == 0)
{
     printf("Hit CTRL+C in the next 10 seconds to trigger the bug\n");
     sleep(10);
     pause();               /* go to sleep, waiting for signal */
}

解决方法是删除pause()语句并将其替换为轮询循环:

while (sig_int_flag == 0)
{
     sleep(1);
}

如果SIGINT出现在循环中的任何地方,包括whilesleep之间,则可能发生的最糟糕的事情是程序在注意到标志设置之前等待1秒,然后它退出循环,另一个更合理的情况是sleep调用被中断,并且循环立即退出,所以当设置信号时,它和它之间几乎没有明显的区别。如果我们只期望pause,则SIGINT来电。

答案 1 :(得分:0)

问题已经回答。但是,其他答案可以巩固这一想法。

while (sig_int_flag == 0) {
          <-----  think it signal is caught here before pause btw while and pause()
    pause();               /* go to sleep, waiting for signal */
}

被捕获后,信号处理程序将运行。完成任务后,返回到捕获信号的位置,在这种情况下为main()。因此,关键点是pause()并调用了pause()。它再次等待SIGINT赶上。为了举例说明,我等效地添加了sleep(5)以捕获先前的pause()

enter image description here

因此,我们通常需要第二种情况。为了始终实现它,上述代码块必须是 atomic 。这就是sigsuspend()更好并且应该使用的原因。

如果您想体验容易犯错的情况,

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

volatile sig_atomic_t sig_int_flag = 0;    /* set nonzero when signal occurs */

char const * handlerMsg = "in handler\n";
int handlerMsgLen;


void sig_int(int s) {
    signal(SIGINT, sig_int);   /* reestablish handler for next time */
    sig_int_flag = 1;          /* set flag for main loop to examine */
    write(2, handlerMsg, handlerMsgLen);
}

void mySleep() {
    for (int i = 0; i < 5; ++i) {
        sleep(1);
        fprintf(stderr, "%d ", i + 1);
    }
}

int main() {
    handlerMsgLen = strlen(handlerMsg);
    signal(SIGINT, sig_int);    /* establish handler */

    while (sig_int_flag == 0) {
        mySleep();
        pause();               /* go to sleep, waiting for signal */
    }
}