• 僵尸进程
    • 回收僵尸进程
      • 通过pcntl_wait和pcntl_waitpid等函数等待子进程结束
      • 通过signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用pcntl_wait或pcntl_waitpid来回收.
      • 忽略掉子进程结束信号,交给init进程管理

    僵尸进程

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

    在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. }