Improving mod_perl Driven Site's Performance -- Part VI: Forking and Executing Subprocesses from mod_perl Page 3

By Stas Bekman (Send Email)
Posted Feb 27, 2001


So the simplest way is to freeing the parent process is to close all three STD* streams if we don't need them and untie the Apache socket. In addition you may want to change the process' current directory to / so the forked process won't keep the mounted partition busy, if this is to be unmounted at a later time. To summarize all this issues, here is an example of the fork that takes care of freeing the parent process.

  use Apache::SubProcess;
  defined (my  = fork) or die "Cannot fork: \n";
  if () {
    # Parent runs this block
  } else {
    # Child runs this block
      ->cleanup_for_exec(); # untie the socket
      chdir '/' or die "Can't chdir to /: ";
      close STDIN;
      close STDOUT;
      close STDERR;

    # some code comes here

      CORE::exit(0);
  }
  # possibly more code here usually run by the parent

Of course between the freeing the parent code and child process termination the real code is to be placed.

Detaching the Forked Process

Now what happens if the forked process is running and we decided that we need to restart the web-server? This forked process will be aborted, since when parent process will die during the restart it'll kill its child processes as well. In order to avoid this we need to detach the process from its parent session, by opening a new session with help of setsid() system call, provided by the POSIX module:

  use POSIX 'setsid';

  defined (my  = fork) or die "Cannot fork: \n";
  if () {

    # Parent runs this block
  } else {
    # Child runs this block
      setsid or die "Can't start a new session: ";
      ...
  }

Now the spawned child process has a life of its own, and it doesn't depend on the parent anymore.

Avoiding Zombie Processes

Now let's talk about zombie processes.

Normally, every process has its parent. Many processes are children of the init process, whose PID is 1. When you fork a process you must wait() or waitpid() for it to finish. If you don't wait() for it, it becomes a zombie.

A zombie is a process that doesn't have a parent. When the child quits, it reports the termination to its parent. If no parent wait()s to collect the exit status of the child, it gets ''confused'' and becomes a ghost process, that can be seen as a process, but not killed. It will be killed only when you stop the parent process that spawned it!

Generally the ps(1) utility displays these processes with the <defunc> tag, and you will see the zombies counter increment when doing top(). These zombie processes can take up system resources and are generally undesirable.

So the proper way to do a fork is:

  my  = shift;
  ->send_http_header('text/plain');

  defined (my  = fork) or die "Cannot fork: ";
  if () {
    waitpid(,0);
    print "Parent has finished\n";
  } else {
      # do something
      CORE::exit(0);
  }

In most cases the only reason you would want to fork is when you need to spawn a process that will take a long time to complete. So if the Apache process that spawns this new child process has to wait for it to finish, you have gained nothing. You can neither wait for its completion (because you don't have the time to), nor continue because you will get yet another zombie process. This is called a blocking call, since the process is blocked to do anything else before this call gets completed.



Comment and Contribute

Your name/nickname

Your email

(Maximum characters: 1200). You have characters left.