PHP Child Process and Signal-Part 1
Tags: php, process
Published: 2020-10-11 17:05:49

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](https://www.php.net/manual/en/function.pcntl-wait.php). 

[Detailed example](http://php.find-info.ru/php/016/ch05lev1sec4.html): *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.

```php
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?