Sleep not working in child process after the popen function call

737 views Asked by At

I have written a small C program which is creating a child process and then running a shell command using popen. I have created a signal handler to wait for the child process to end while the parent process just runs in an infinite loop. When I am calling 'sleep(5)' after the popen system call the sleep call does not work.

Instead if I put the sleep call before the popen then it is working. Also if I remove the signal handler and just wait for the child process in the parent process then there is no problem(i.e. sleep runs properly anywhere).

//Assume all required header files and function prototypes added

int main()
{
    int pid,status;
    signal(SIGCHLD,reaper);
    pid = fork();
    if(pid == 0)
       runCommand();
    while(1);
}

void runCommand()
{
    FILE *pipen;
    char *buffer;

    buffer = (char*) malloc(100);
/*sleep #1*/    sleep(5);
    if ((pipen = popen("uname -a", "r")) == NULL) {
        perror("\nError during shell command execution: ");
        exit(0);
    }
/*sleep #2*/    sleep(5);
    fgets(buffer, 100, pipen);
/*sleep #3*/    sleep(5);
    printf("\n%s\n",buffer);
    exit(0);
}

void reaper(int signal)
{
    int status;
    waitpid(-1,&status,WNOHANG);
}

When I am running the above program sleep #1 works and the process sleeps for the given time. I am getting to know this by observing the time it takes to print the result of the command (by the printf statement). But for sleep #2 and sleep #3 I expect the process to sleep but that does not seem to be happening as the result of the command is printed on the console instantaneously.

The behavior can be observed by keeping only the sleep #2 or sleep #3 call, and removing the other two sleep calls.

Does anyone know why this is happening?

1

There are 1 answers

2
Barmar On

popen() works by forking a child to execute /bin/sh -c "uname -a". When that child exits, the process receives a SIGCHLD signal. This interrupts the sleep() call and runs your reaper function.

I haven't been able to find this mentioned in any Linux or POSIX documentation, but it's in the SunOS documentation:

The signal handler for SIGCHLD should be set to default when using popen(). If the process has established a signal handler for SIGCHLD, it will be called when the command terminates. If the signal handler or another thread in the same process issues a wait(2) call, it will interfere with the return value of pclose(). If the process's signal handler for SIGCHLD has been set to ignore the signal, pclose() will fail and errno will be set to ECHILD.

You should set the SIGCHLD handler back to the default in the child process.

void runCommand()
{
    FILE *pipen;
    char *buffer;

    signal(SIGCHLD, SIG_DFL);

    buffer = (char*) malloc(100);
/*sleep #1*/    sleep(5);
    if ((pipen = popen("uname -a", "r")) == NULL) {
        perror("\nError during shell command execution: ");
        exit(0);
    }
/*sleep #2*/    sleep(5);
    fgets(buffer, 100, pipen);
/*sleep #3*/    sleep(5);
    printf("\n%s\n",buffer);
    exit(0);
}