fork()

fork()

fork() can be thought of as a ticket to power. Power can sometimes be thought of as a ticket to destruction. Therefore, you should be careful while messing with fork() on your system.

The parent process (the one that already exists) fork()'s a child process (the new one). The child process gets a copy of the parent's data. Voila! You have two processes where there was only one!

When a process dies, it doesn't really go away completely. It's dead, so it's no longer running, but a small remnant is waiting around for the parent process to pick up. This remnant contains the return value from the child process and some other goop. So after a parent process fork()s a child process, it must wait() (or waitpid()) for that child process to exit. It is this act of wait()ing that allows all remnants of the child to vanish.

The parent can ignore the SIGCHLD signal (SIGCLD on some older systems) and then it won't have to wait().

main()

{

signal(SIGCHLD, SIG_IGN); /* now I don't have to wait()! */

.

.

fork();fork();fork(); /* Rabbits, rabbits, rabbits! */

Now, when a child process dies and has not been wait()ed on, it will usually show up in a ps listing as “<defunct>”. It will remain this way until the parent wait()s on it, or it is dealt with as mentioned below.

Now there is another rule you must learn: when the parent dies before it wait()s for the child (assuming it is not ignoring SIGCHLD), the child is reparented to the init process (PID 1). This is not a problem if the child is still living well and under control. However, if the child is already defunct, we're in a bit of a bind. See, the original parent can no longer wait(), since it's dead. So how does init know to wait() for these zombie processes?

The answer: it's magic! Well, on some systems, init periodically destroys all the defunct processes it owns. On other systems, it outright refuses to become the parent of any defunct processes, instead destroying them immediately. If you're using one of the former systems, you could easily write a loop that fills up the process table with defunct processes owned by init.

Your mission: make sure your parent process either ignores SIGHCLD, or wait()s for all the children it fork()s. Well, you don't always have to do that (like if you're starting a daemon or something), but you code with caution if you're a fork() novice. Otherwise, feel free to blast off into the stratosphere.

To summerize: children become defunct until the parent wait()s, unless the parent is ignoring SIGCHLD. Furthermore, children (living or defunct) whose parents die without wait()ing for them (again assuming the parent is not ignoring SIGCHLD) become children of the init process, which deals with them heavy-handedly.

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

int main(void)

{

pid_t pid;

int rv;

switch(pid = fork()) {

case -1:

perror("fork"); /* something went wrong */

exit(1); /* parent exits */

case 0:

printf(" CHILD: This is the child process!\n");

printf(" CHILD: My PID is %d\n", getpid());

printf(" CHILD: My parent's PID is %d\n", getppid());

printf(" CHILD: Enter my exit status (make it small): ");

scanf(" %d", &rv);

printf(" CHILD: I'm outta here!\n");

exit(rv);

default:

printf("PARENT: This is the parent process!\n");

printf("PARENT: My PID is %d\n", getpid());

printf("PARENT: My child's PID is %d\n", pid);

printf("PARENT: I'm now waiting for my child to exit()...\n");

wait(&rv);

printf("PARENT: My child's exit status is: %d\n", WEXITSTATUS(rv));

printf("PARENT: I'm outta here!\n");

}

return 0;

}

pid_t is the generic process type. Under Unix, this is a short. So, I call fork() and save the return value in the pid variable. fork() is easy, since it can only return three things:

0: If it returns 0, you are the child process. You can get the parent's PID by calling getppid(). Of course, you can get your own PID by calling getpid().

-1: If it returns -1, something went wrong, and no child was created. Use perror() to see what happened. You've probably filled the process table.

else: Any other value returned by fork() means that you're the parent and the value returned is the PID of your child. This is the only way to get the PID of your child, since there is no getcpid() call (obviously due to the one-to many relationship between parents and children.)

When the child finally calls exit(), the return value passed will arrive at the parent when it wait()s. What's this WEXITSTATUS() stuff, anyway? Well, that is a macro that extracts the child's actual return value from the value wait() returns. Yes, there is more information buried in that int.

“How,” you ask, “does wait() know which process to wait for? I mean, since the parent can have multiple children, which one does wait() actually wait for?” Parent waits for whichever one happens to exit first. If you must, you can specify exactly which child to wait for by calling waitpid() with your child's PID as an argument.

Another interesting thing to note from the above example is that both parent and child use the rv variable. Does this mean that it is shared between the processes? NO! If it was. Each process has its own copy of all variables. There is a lot of other stuff that is copied, too.

if (!fork()) {

printf("I'm the child!\n");

exit(0);

} else {

printf("I'm the parent!\n");

wait(NULL);

}

How to wait() if you don't care what the return value of the child is: you just call it with NULL as the argument.