简体   繁体   中英

Why the signal pause will cause the program to sleep forever?

The APUE book says that: If the signal occurs after the test of sig_int_flag but before the call to pause , the process could go to sleep forever.

I don't know why, can somebody tells me? Thanks a lot.

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

If an interrupt signal is issued at the precise time you're describing:

  • the flag has been checked false: entering loop
  • signal resets itself, setting the flag to 1 , but too late (test has been done)
  • since loop has already been entered, pause() is called and the program waits

That said, if CTRL+C/SIGINT is triggered another time, you can exit the loop, so it's not that critical, since that signal can be issued manually.

If you want to check that behaviour, I suggest you add a sleep statement:

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

A workaround would be to remove the pause() statement and replace it by a polling loop:

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

If a SIGINT occurs anywhere in the loop, including between the while and the sleep , then the worse thing that can happen is that the program waits 1 second before noticing that the flag is set, then it exits the loop, and the other, more plausible case it that the sleep call is interrupted, and the loop is exited immediately, so when the signal is set, there's little visible difference between that and a pause call if we only expect SIGINT .

The question's already answered. However, additional answer can consolidate the idea.

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

Having caught, signal handler runs. After it finishes its task, it returns to a point at which the signal is caught, in main() in this case. So, the point is pause() and pause() is called. It waits again SIGINT to catch. To exemplify it, I add sleep(5) equivalently to catch prior pause() .

在此输入图像描述

So, we typically want the second situation. To achieve it always, the aforementioned code block has to be atomic . That's why sigsuspend() is better and should be used.

If you would like to experience the fallible case,

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM