前面已经讲到,当父进程是一个常驻进程的时候,子进程退出了,父进程没有回收它,那么该子进程会成为僵尸进程,且必须等到父进程退出了才会被销毁。所以,如果父进程只是个短暂的进程,那么也没必要去回收子进程,当然主动回收更好。
PHP中父进程回收子进程的函数有两个
1、pcntl_waitpid
pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] ) : int
2、pcntl_wait
pcntl_wait ( int &$status [, int $options = 0 ] ) : int
这个函数等同于 pid = -1 时的 pcntl_waitpid 函数。
共同的参数:
$options 取值 0,WNOHANG,WUNTRACED,WNOHANG | WUNTRACED
其中 WNOHANG 是非阻塞的,但是你需要定时轮训。
本质上这两个函数的功能是一样的。
此函数挂起
当前进程的执行直到一个子进程退出,或接收到一个信号要求中断当前进程,或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。
综上所述,这两个函数都是很鸡肋的,如果是个多进程服务,类似于swoole这种,那么是肯定要去回收子进程的,但是不管是阻塞还是定时轮训的方式,主进程都很难分身去做别的事情。
所以,这种回收子进程的方式,只适合做临时的多任务的处理,或者类似于 exec 函数这种阻塞式的场景,下面简单给个例子。
function main()
{
$count = 6;
for ($i = 1; $i <= $count; $i++) {
$pid = pcntl_fork();
if ($pid > 0) {
} else if (0 == $pid) {
task($i);
exit; // 一定要退出,否则子进程会继承上下文继续执行
} else {
echo "fork失败" . PHP_EOL;
}
}
while ($count) {
$pid = pcntl_wait($result);
var_dump($pid);
var_dump($result);
$count--;
}
}
function task($taskId)
{
echo "task {$taskId} is over", PHP_EOL;
}
main();
查看是否有僵尸进程
模拟出现僵尸进程
function main()
{
$count = 6;
for ($i = 1; $i <= $count; $i++) {
$pid = pcntl_fork();
if ($pid > 0) {
} else if (0 == $pid) {
task($i);
exit; // 一定要退出,否则子进程会继承上下文继续执行
} else {
echo "fork失败" . PHP_EOL;
}
}
sleep(10);// 会出现僵尸进程
while ($count) {
$pid = pcntl_wait($result);
var_dump($pid);
var_dump($result);
$count--;
}
}
function task($taskId)
{
echo "task {$taskId} is over", PHP_EOL;
}
main();
运行程序后立马查看僵尸进程
等程序结束后,再查看僵尸进程,发现已经没有了。
至于 pcntl_waitpid 的例子就不写了,是一样的。