loading...

PHP Child Process and Signal-Part 1

aldora713 profile image ShakalakaB ・Updated on ・3 min read

Take-away

  • PHP: no native support for multithreads
  • Alternative:pcntl_fork() create child processes
  • Reap child processes: pcntl_wait() and pcntl_signal()
    • pcntl_wait() can only handles the status of child process, totally no reaction for status of parent process.
    • pcntl_wait() has no effect when it is called in child process
    • pcntl_signal() only works for the process it is in, which means if I kill parent process, the signal handler in child won't work. While pcntl_wait() works for signals. If I kill the child, the pcntl_wait() in parent would detect it.

Create Child Processes: pcntl_fork()

if ($pid = pcntl_fork()) {
    $parent = getmypid();
    echo "parent: $parent\n";
} else {
    $childPid = getmypid();
    echo "child pid: $childPid\n";
}

(Above example is from Advanced PHP Programming by George Schlossnagle)

The point pcntl_fork() called, child process would be created. In parent process, pcntl_fork() returns the pid of child process, while in the child, it returns 0.

Reap child processes: pcntl_wait() and pcntl_signal()

pcntl_wait() would wait on or returns the status of a forked child. It instructs the calling process to suspend execution until any of its children terminates. Note two things:

  • it can only handles the status of child process, totally no reaction for status of parent process.
  • it has no effect when it is called in child process More detail here.

Detailed example: Cleaning Up After Children part

pcntl_signal would install a signal handler. Signals are the instruction to processes. For example, when we execute command kill to terminate a process, system would send interrupt signal SIGINT. With signal handler, we could define our function to deal with process.
The following example shows how we use them to reap child process.

pcntl_async_signals(true);

pcntl_signal(SIGTERM, function ($signal, $status) {
    echo "inside pcntl_signal\n";
    $pid = pcntl_wait($processStatus, WUNTRACED);
//    $pid = pcntl_wait($processStatus, WNOHANG);

    if (pcntl_wifexited($processStatus)) {
        $code = pcntl_wexitstatus($processStatus);
        print "pid $pid returned exit code: $code\n ";
    } else {
        print "$pid was unnaturally terminated\n ";
    }
//    print_r($processStatus);
//    echo "\n";
//    echo "signal: $signal\n";
//    print_r($status);
});
if ($pid = pcntl_fork()) {
    $parent = getmypid();
    echo "parent: $parent\n";

    sleep(60);
} else {
    $childPid = getmypid();
    echo "child pid: $childPid\n";

    sleep(60);
}

Execute the above script, and according to the output of process pid, first use kill command to kill parent process, then kill child process. The total output would be like this:

parent: <parent pid>
child pid: <child pid>
inside pcntl_signal  //shows when parent gets killed
inside pcntl_signal //shows when child gets killed
pid -1 returned exit code: 0
pid <child pid> returned exit code: 0

So what happened? First we execute the script, it create a parent process and a child. Then we kill parent, which send a SIGINT signal, it invokes the signal handler in parent process. So we get the first 'inside pcntl_signal', and because pcntl_wait only works for forked processes, so it can't handle the signal of parent. The parameter in pcntl_wait is 'WUNTRACED', which means it would wait until it gets a valid signal from the child.
Then we kill the child process, once again, it invokes signal handler. But why there are two lines of 'pid * returned exit code *'? Because the pcntl_wait in parent is working. 'pid -1 returned exit code: 0' is returned from child process, cause pcntl_wait() has no effect when called in child. The pcntl_wait() in parent successfully detects the exit of child process.

So what would happen if we kill child first, then kill the parent?

Discussion

pic
Editor guide