僵尸進程
僵尸進程是當子進程比父進程先結束,而父進程又沒有回收子進程,釋放子進程占用的資源,此時子進程將成為一個僵尸進程。
在unix進程管理中,如果你新開的子進程運行結束,父進程將會收到一個SIGCHLD信號,子進程成為僵尸進程(保存了進程的狀態等信息),等待父進程的處理,如果父進程一直不處理,該進程將會一直存在,占用系統進程表項,如果僵尸進程過多,導致系統沒有可用的進程表項,于是再也無法運行其他的程序
為了更容易理解,本文使用pcntl擴展進行進程管理
例如:
<?php
$num = 1;
$str = "EasySwoole,Easy學swoole\n";
$pid = pcntl_fork();
if ($pid > 0) {//主進程代碼
echo "我是主進程,id是".getmypid().",子進程的pid是{$pid}\n";
pcntl_async_signals(true);
pcntl_signal(SIGCHLD, function () {
echo '子進程退出了,請及時處理' . PHP_EOL;
});
while (1) {//主進程一直不退出
sleep(1);
}
} elseif ($pid == 0) {
echo "我是子進程,我的pid是" . getmypid() . "\n";
} else {
echo "我是主進程,我慌得一批,開啟子進程失敗了\n";
}
使用ps查看僵尸進程:
ps -A -ostat,ppid,pid,cmd |grep -e '^[Zz]'
輸出:
Z+ 7136 7137 [php] <defunct>
當主進程退出之后,子進程將會被init接管并處理
回收僵尸進程
回收僵尸進程
通過pcntl_wait和pcntl_waitpid等函數等待子進程結束
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('fork error');
} else if ($pid > 0) {
//父進程阻塞著等待子進程的退出
// pcntl_wait($status);
// pcntl_waitpid($pid, $status);
//非阻塞方式
// pcntl_wait($status, WNOHANG);
// pcntl_waitpid($pid, $status, WNOHANG);
} else {
sleep(3);
echo "child \r\n";
exit;
}
通過signal函數為SIGCHLD安裝handler,因為子進程結束后,父進程會收到該信號,可以在handler中調用pcntl_wait或pcntl_waitpid來回收.
<?php
pcntl_async_signals(true);
pcntl_signal(SIGCHLD, function () {
echo "SIGCHLD \r\n";
//阻塞方式
pcntl_wait($status);
//pcntl_waitpid(-1, $status);
//非阻塞
//pcntl_wait($status, WNOHANG);
//pcntl_waitpid(-1, $status, WNOHANG);
});
$pid = pcntl_fork();
if ($pid == -1) {
die('fork error');
} else if ($pid) {
sleep(10);
} else {
sleep(3);
echo "child \r\n";
exit;
}
忽略掉子進程結束信號,交給init進程管理
<?php
pcntl_async_signals(true);
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
if ($pid == -1) {
die('fork error');
} else if ($pid>0) {
while(1){
sleep(1);
}
} else {
sleep(3);
echo "child \r\n";
exit;
}