This is an example from <Advanced Linux Programming>, chapter 3.4.4. The programs fork() and exec() a child process. Instead of waiting for the termination of the process, I want the parent process to clean up the children process (otherwise the children process will become a zombie process) asynchronously. The can be done using the signal SIGCHLD. By setting up the signal_handler we can make the clean-up work done when the child process ends. And the code the following:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
int spawn(char *program, char **arg_list){
pid_t child_pid;
child_pid = fork();
if(child_pid == 0){ // it is the child process
execvp(program, arg_list);
fprintf(stderr, "A error occured in execvp\n");
return 0;
}
else{
return child_pid;
}
}
int child_exit_status;
void clean_up_child_process (int signal_number){
int status;
wait(&status);
child_exit_status = status; // restore the exit status in a global variable
printf("Cleaning child process is taken care of by SIGCHLD.\n");
};
int main()
{
/* Handle SIGCHLD by calling clean_up_process; */
struct sigaction sigchld_action;
memset(&sigchld_action, 0, sizeof(sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchld_action, NULL);
int child_status;
char *arg_list[] = { //deprecated conversion from string constant to char*
"ls",
"-la",
".",
NULL
};
spawn("ls", arg_list);
return 0;
}
However, When I run the program in the terminal, the parent process never ends. And it seems that it doesn't execute the function clean_up_child_process (since it doesn't print out "Cleaning child process is taken care of by SIGCHLD."). What's the problem with this snippet of code?
The parent process immediately returns from
main()
after the child pid is returned fromfork()
, it never has the opportunity to wait for the child to terminate.I'm using Mac, so my answer may be not quite relevant, but still. I compile without any options, so executable name is
a.out
.I have the same experience with the console (the process doesn't seem to terminate), but I noticed that it's just terminal glitch, because you actually can just press Enter and your command line will be back, and actually
ps
executed from other terminal window doesn't showa.out
, norls
which it launched.Also if I run
./a.out >/dev/null
it finishes immediately.So the point of the above is that everything actually terminates, just the terminal freezes for some reason.
Next, why it never prints
Cleaning child process is taken care of by SIGCHLD.
. Simply because the parent process terminates before child. TheSIGCHLD
signal can't be delivered to already terminated process, so the handler is never invoked.In the book it's said that the parent process contiunes to do some other things, and if it really does then everything works fine, for example if you add
sleep(1)
afterspawn()
.for GNU/Linux users
I already read this book. Although the book talked about this mechanism as a:
quote from 3.4.4 page 59 of the book:
but it just said that you can use
sigaction
to handle this situation.Here is a complete example of how to handle processes in this way.
First why do ever we use this mechanism? Well, since we do not want to synchronize all processes together.
real example
Imagine that you have 10
.mp4
files and you want to convert them to.mp3
files. Well, I junior user does this:and repeats this command 10 times. A little higher users does this:
This time, this command pipes all 10
mp4
files per line, each one-by-one toxargs
and then they one by one is converted tomp3
.But I senior user does this:
and this means if I have 10 files, create 10 processes and run them simultaneously. And there is BIG different. In the two previous command we had only 1 process; it was created then terminated and then continued to another one. But with the help of
-P 0
option, we create 10 processes at the same time and in fact 10ffmpeg
commands are running.Now the purpose of cleaning up children asynchronously becomes cleaner. In fact we want to run some new processes but the order of those process and maybe the exit status of them is not matter for us. In this way we can run them as fast as possible and reduce the time.
First you can see
man sigaction
for any more details you want.Second seeing this signal number by:
sample code
objective: using the
SIGCHLD
to clean up child processThis is what the sample code does:
man pause
signal_handler
functionsleep 9
output: (17 means
SIGCHLD
)when you run this sample code, on the other terminal try this:
As you can see
a.out
process has 5 children. And They are running simultaneously. Then whenever each of them terminates, kernel sends the signalSIGCHLD
to their parent that is:a.out
NOTE
If we do not use
pause
or any mechanism so that the parent canwait
for its children, then we will abandon the created processes and the upstart (= onUbuntu
orinit
) becomes parent of them. You can try it if you removepause()