声明:维基百科上没有僵死进程的词条,这里认为僵死进程同僵尸进程,即ZOMBIE。
一、定义
什么是僵尸进程
维基百科的定义:在类UNIX系统中,僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致)但在操作系统的进程表中仍然有一个表项(进程控制块PCB),处于”终止状态”的进程。
这个定义很准确,但并不好理解,通俗的说法是一个进程fork了一个子进程,子进程先于父进程退出,但父进程没有调用wait(通过wait系统调用读取退出进程的退出态,退出进程的在进程表中的表项就被删除),导致这个进程已经退出但是仍在进程表中占有一个位置,这种进程称为僵尸进程。
什么是孤儿进程
孤儿进程:一个进程fork了一个子进程, 父进程先于子进程退出,运行中的子进程称为孤儿进程。
孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
下面,让我们来看2个示例:
二、僵尸进程示例
0、通过swoole在一个进程中创建子进程,让子进程先于父进程退出。
1 2 3 4 5 6 7 8 9 10 11 12 | echo 'ppid = ' . getmypid (), PHP_EOL; $process = new swoole_process( function (swoole_process $worker ) { echo 'pid = ' . getmypid (), PHP_EOL; sleep(10); echo 'child process exit' , PHP_EOL; }, false); $process ->start(); sleep(1000); echo 'parent process exit' , PHP_EOL; |
1、以上代码保存文件zombie.php, 执行
1 2 3 4 | [root@salmonl process] # php zombie.php ppid = 4899 pid = 4900 child process exit |
2、另开一个窗口查看进程
1 2 3 4 5 6 7 8 9 10 11 12 | # 子进程退出前 $ ps aux | grep - v 'grep' | grep 'zombie' root 4899 0.3 0.6 321644 24612 pts /0 S+ 16:07 0:00 php zombie.php root 4900 0.0 0.2 321644 8956 pts /0 S+ 16:07 0:00 php zombie.php # 子进程退出后 $ ps aux | grep - v 'grep' | grep 'zombie' root 4899 0.0 0.6 321644 24612 pts /0 S+ 16:07 0:00 php zombie.php # 查看僵尸进程(状态为Z, 或者COMMAND中出现defunct) $ ps aux | grep - v 'grep' | grep 'defunct' root 4900 0.0 0.0 0 0 pts /0 Z+ 16:07 0:00 <defunct> |
3、kill僵尸进程无效
1 2 3 | $ kill 4900 $ ps aux | grep - v 'grep' | grep 'defunct' root 4900 0.0 0.0 0 0 pts /0 Z+ 16:07 0:00 <defunct> |
4、kill父进程,僵尸进程就会消亡
1 2 | $ kill 4899 $ ps aux | grep - v 'grep' | grep 'defunct' |
三、孤儿进程示例
0、通过swoole在一个进程中创建子进程,让父进程先于子进程退出。
1 2 3 4 5 6 7 8 9 10 11 12 | echo 'ppid = ' . getmypid (), PHP_EOL; $process = new swoole_process( function (swoole_process $worker ) { echo 'pid = ' . getmypid (), PHP_EOL; sleep(1000); echo 'child process exit' , PHP_EOL; }, false); $process ->start(); sleep(10); echo 'parent process exit' , PHP_EOL; |
1、以上代码保存文件orphan.php, 执行
1 2 3 4 | $ php orphan.php ppid = 5041 pid = 5042 parent process exit |
2、另开一个窗口查看进程
1 2 3 4 5 6 7 8 | # 父进程退出之前,进程状态 $ ps aux | grep - v 'grep' | grep 'orphan' root 5041 0.5 0.6 321644 24612 pts /0 S+ 16:22 0:00 php orphan.php root 5042 0.0 0.2 321644 8956 pts /0 S+ 16:22 0:00 php orphan.php # 父进程退出之后进程状态(子进程的ppid变为1) $ ps -ef | grep - v 'grep' | grep orphan root 5042 1 0 16:22 pts /0 00:00:00 php orphan.php |
四、总结
0、swoole中避免出现僵尸进程,在父进程中调用wait即可
1 2 3 | while ( $res = swoole_process::wait()) { echo PHP_EOL, 'worker process exit pid: ' . $res [ 'pid' ] . PHP_EOL; } |
1、僵尸进程占用进程ID,多了之后,影响进程调度。
2、清除僵尸进程可通过清除父进程,或者等待很长时间后被内核清除。
参考:
维基百科: 僵尸进程
CoolShell: LINUX 的僵尸(ZOMBIE)进程
知乎: 僵尸进程和僵死进程有什么区别?修改