【问题标题】:Why the signal pause will cause the program to sleep forever?为什么信号暂停会导致程序永远休眠?
【发布时间】:2016-12-25 09:08:33
【问题描述】:

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 */
}

【问题讨论】:

  • 如果在处理SIGNT 之前测试了while 条件,则代码将运行到UB,因为sig_int_flag 被读取为未初始化。
  • "...永远休眠",直到处理下一个 SIGINT
  • @alk 不会因为全局而为零吗?不过,我并不是说这是正确的。

标签: c unix signals


【解决方案1】:

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

  • 标志已被检查为假:进入循环
  • 信号自行重置,将标志设置为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 调用被中断,循环立即退出,所以当信号被设置时,如果我们只期望@ 987654331@.

【讨论】:

    【解决方案2】:

    问题已经回答了。但是,额外的答案可以巩固这个想法。

    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()

    所以,我们通常想要第二种情况。为了始终实现它,上述代码块必须是原子。这就是为什么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 */
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 2022-07-23
      • 1970-01-01
      • 1970-01-01
      • 2011-11-22
      • 1970-01-01
      相关资源
      最近更新 更多