ServersImproving mod_perl Driven Site's Performance -- Part VI: Forking and Executing Subprocesses...

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




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
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 finishedn";
  } 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.

Latest Posts

Related Stories