僵尸进程

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。

在unix进程管理中,如果你新开的子进程运行结束,父进程将会收到一个SIGCHLD信号,子进程成为僵尸进程(保存了进程的状态等信息),等待父进程的处理,如果父进程一直不处理,该进程将会一直存在,占用系统进程表项,如果僵尸进程过多,导致系统没有可用的进程表项,于是再也无法运行其他的程序

为了更容易理解,本文使用pcntl扩展进行进程管理

例如:

  1. <?php
  2. $num = 1;
  3. $str = "EasySwoole,Easy学swoole\n";
  4. $pid = pcntl_fork();
  5. if ($pid > 0) {//主进程代码
  6. echo "我是主进程,id是".getmypid().",子进程的pid是{$pid}\n";
  7. pcntl_async_signals(true);
  8. pcntl_signal(SIGCHLD, function () {
  9. echo '子进程退出了,请及时处理' . PHP_EOL;
  10. });
  11. while (1) {//主进程一直不退出
  12. sleep(1);
  13. }
  14. } elseif ($pid == 0) {
  15. echo "我是子进程,我的pid是" . getmypid() . "\n";
  16. } else {
  17. echo "我是主进程,我慌得一批,开启子进程失败了\n";
  18. }

使用ps查看僵尸进程:

  1. ps -A -ostat,ppid,pid,cmd |grep -e '^[Zz]'

输出:

  1. Z+ 7136 7137 [php] <defunct>

当主进程退出之后,子进程将会被init接管并处理

回收僵尸进程

回收僵尸进程

通过pcntl_wait和pcntl_waitpid等函数等待子进程结束

  1. <?php
  2. $pid = pcntl_fork();
  3. if ($pid == -1) {
  4. die('fork error');
  5. } else if ($pid > 0) {
  6. //父进程阻塞着等待子进程的退出
  7. // pcntl_wait($status);
  8. // pcntl_waitpid($pid, $status);
  9. //非阻塞方式
  10. // pcntl_wait($status, WNOHANG);
  11. // pcntl_waitpid($pid, $status, WNOHANG);
  12. } else {
  13. sleep(3);
  14. echo "child \r\n";
  15. exit;
  16. }

通过signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用pcntl_wait或pcntl_waitpid来回收.

  1. <?php
  2. pcntl_async_signals(true);
  3. pcntl_signal(SIGCHLD, function () {
  4. echo "SIGCHLD \r\n";
  5. //阻塞方式
  6. pcntl_wait($status);
  7. //pcntl_waitpid(-1, $status);
  8. //非阻塞
  9. //pcntl_wait($status, WNOHANG);
  10. //pcntl_waitpid(-1, $status, WNOHANG);
  11. });
  12. $pid = pcntl_fork();
  13. if ($pid == -1) {
  14. die('fork error');
  15. } else if ($pid) {
  16. sleep(10);
  17. } else {
  18. sleep(3);
  19. echo "child \r\n";
  20. exit;
  21. }

忽略掉子进程结束信号,交给init进程管理

  1. <?php
  2. pcntl_async_signals(true);
  3. pcntl_signal(SIGCHLD, SIG_IGN);
  4. $pid = pcntl_fork();
  5. if ($pid == -1) {
  6. die('fork error');
  7. } else if ($pid>0) {
  8. while(1){
  9. sleep(1);
  10. }
  11. } else {
  12. sleep(3);
  13. echo "child \r\n";
  14. exit;
  15. }