Angelo
Angelo

Reputation: 344

Execl() in a child process and wait() in the parent process

I don't understand why the parent process continues executing even if his child process didn't terminate.

This is a.c:

if (!fork()){
    execl("/bin/sh", "sh", "-c", "gnome-terminal -x ./b", (char *) 0);
}
else {

    int status;
    wait(&status);
    if ((status & 255) == 0) printf("\nProcess regularly exited ");
    printf("adios");
    return 0;
}

and this is b.c:

printf("hello\n");
fflush(0);
getchar();
exit(0);

It happens that the parent process prints "process regularly exited" and "adios" when the child process hasn't terminated and it is waiting for input by getchar() ,and I don't understand why.

Finally ,how can I force the parent process to wait until the execution of b.c is completed?

Upvotes: 1

Views: 3716

Answers (2)

mrconfu
mrconfu

Reputation: 31

You could use signals. This is a simple, not elegant solution.

a.c

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

void death_handler();

int main(){

sigset_t set;
sigfillset(&set);
sigprocmask(SIG_BLOCK,&set,NULL);
sigemptyset(&set);
sigaddset(&set, SIGUSR1); 
sigprocmask(SIG_UNBLOCK, &set, NULL); //unlock SIGUSR1
signal(SIGUSR1, death_handler);

printf("my pid is %d\n", getpid());

char pid[50];
snprintf(pid, 50, "gnome-terminal -x ./b %d",(int)getpid()); 

if (fork() == 0){

    execl("/bin/sh","sh", "-c", pid,(char *) 0);
}
else {

    int status;
    //nobody, apart from the other process (and anyone that sends me a SIGUSR1 signal) can tell me to exit. It has to send a SIGUSR1 signal.

    pause();

    //UNBLOCK ALL PREVIOUSLY BLOCKED SIGNALS 
    sigemptyset(&set);
    sigfillset(&set);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    //remove ex-child handler
    signal(SIGUSR1, SIG_DFL);

    printf("adios\n");
    return 0;
}   

}

void death_handler(){

printf("ex-child dead\n");    
}

and this is b.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char** argv){

int xmax;
xmax = atoi(argv[1]);
printf("parent pid = %s \n", argv[1]);
fflush(0);
getchar();
kill((pid_t)xmax, SIGUSR1);
printf("sendinga signal to the ex-parent to abort pause()...\n");
exit(0);

}

this basically pauses the parent until someone sends the 10 signal. It can only be unpaused by someone who send it the SIGUSR1 signal.

Another solution, would probably be to use semaphores. You initialize the semaphore to 0. The parent performs a wait, and then, when the child process is finished you just do a post (in the child), that frees the parent.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 754150

Converting comments into an answer.

One key question when facing a C program that doesn't work as you expect when executing another is 'what happens in the shell'. For this question, that translates to: When you run sh -c "gnome-terminal …" from the command line, how long does it take before you get the prompt back?

It is instantly.

So — surprise, surprise — the same thing happens when you run it from your C code. The shell of the sh -c … exits promptly, so your parent process spots its death and exits. I think your second program is run as a grandchild or great-grandchild of the process you fork off. You have sh, which runs gnome-terminal, which probably forks and the parent exits while the child goes on to manage the terminal and fork a shell that runs your second command (or maybe doesn't use a shell and simply executes your second command). A process can only wait for its direct children to die; it can't wait for its children's children or more distant descendants.

Now if I add while(1){} at the end of a.c before return 0, and at the end of b.c before exit(0), the top command indicates that I have 4 processes: 2 of a.c and 2 of b.c. Isn't it strange?

There are multiple things that could be going on. It could be leftover processes from previous runs. You'd need to look at the process trees to know if those processes are related and how they're related.

Thank you they were simply leftover processes.

Finally, how can I force the parent process to wait until the execution of b.c is completed?

Succinctly, you can't make A wait for B to finish. You'd need a way for A to know which process is the B that it should wait for. It can't wait directly for it because B is not its own child, so you'd probably end up doing some polling operation to see if B is still around. That might be testing with kill() and signal 0; on Linux, you might be able to play with iNotify on the /proc file system (and avoid polling).

Upvotes: 3

Related Questions