先来看一个在之前遇到的例子:
Tasks: 779 total, 6 running, 753 sleeping, 0 stopped, 20 zombie
Mem: 7.3G total, 6.7G used, 646M free, 868K buffers
Swap: 2.0G total, 1.2G used, 732M free, 670M cached
800%cpu 27%user 15%nice 360%sys 390%idle 0%iow 6%irq 2%sirq 0%host
这是我们在稳定性测试中遇到的,出现问题后机器是特别的卡,按下power按键等待几秒钟才能亮屏。
从top的数据中可以看出一下几点:
当然了,我们本节重点分析僵尸进程产生的原因,僵尸进程是怎么产生的。
在平时的工作中僵尸进程是很难遇见的,僵尸进程其实进程死亡前的一个临界状态。我们还是通过例子来说明
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
pid_t pid = fork();
int status;
int ret;
if(pid == -1){
printf("create child process failed!\n");
return -1;
}else if(pid == 0){
printf("This is child process!, pid=%d\n",getpid());
pause();
}else{
printf("This is parent process!\n");
do{
ret = waitpid(pid, &status, WUNTRACED | WCONTINUED);
if(ret == -1){
printf("wait child process failed!\n");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("child process exited, status=%d!\n", WEXITSTATUS(status));
}else if (WIFSIGNALED(status)) {
printf("child process killed by signal %d!\n", WTERMSIG(status));
}else if (WIFSTOPPED(status)) {
printf("child process stopped by signal %d!\n", WSTOPSIG(status));
}else if (WIFCONTINUED(status)) {
printf("child process continued run!\n");
}
}while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
return 0;
}
我们将之前的fork文件改造了下,当子进程退出时父进程使用waitpid系统调用来获取子进程退出的原因。
我们来看下运行结果:
root@ubuntu:zhuxl$ ./a.out
This is parent process!
This is child process!, pid=106600
当运行后,子进程现在在pause处暂停着,而父进程使用waitpid正在等在子进程退出的状态。
这时候另外起了一个终端,再另外一个终端上输入kill -9 106600
root@ubuntu:~$ kill -9 106600
root@ubuntu:~$
root@ubuntu:zhuxl$ ./a.out
This is parent process!
This is child process!, pid=106600
child process killed by signal 9!
可以看到子进程收到了kill -9的信号然后退出,而且父进程也通过waitpid扑获了此退出信息。当然了我们的例子中还描述了正常退出,暂停,继续等信号。大家有兴趣可以试试。
可以通过kill -l查看所有的信号,然后给子进程发信号。
root@ubuntu:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
pid_t pid = fork();
int status;
int ret;
if(pid == -1){
printf("create child process failed!\n");
return -1;
}else if(pid == 0){
printf("This is child process!, pid=%d\n",getpid());
//pause();
}else{
printf("This is parent process!\n");
while(1);
#if 0
do{
ret = waitpid(pid, &status, WUNTRACED | WCONTINUED);
if(ret == -1){
printf("wait child process failed!\n");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("child process exited, status=%d!\n", WEXITSTATUS(status));
}else if (WIFSIGNALED(status)) {
printf("child process killed by signal %d!\n", WTERMSIG(status));
}else if (WIFSTOPPED(status)) {
printf("child process stopped by signal %d!\n", WSTOPSIG(status));
}else if (WIFCONTINUED(status)) {
printf("child process continued run!\n");
}
}while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
#endif
}
return 0;
}
我们将上面的代码进行修改,让父进程不再运行waitpid去等待子进程,而是一直while(1)循环住。
运行结果:
root@ubuntu:zhuxl$ ./a.out
This is parent process!
This is child process!, pid=106950
这时候子进程其实已经退出了,而父进程一直在while(1)着。这时候通过ps去查看父子进程的状态。
root 106949 99.8 0.0 4508 708 pts/0 R+ 19:59 7:37 ./a.out
root 106950 0.0 0.0 0 0 pts/0 Z+ 19:59 0:00 [a.out] <defunct>
这时候可以看到子进程的状态已经是Z+了,也就是僵尸进程了。可以看到子进程a.out已经盖上了棺材盖了。当然了也可以通过top命令来看下
top - 20:08:24 up 1 day, 10:07, 1 user, load average: 1.01, 0.89, 0.53
Tasks: 251 total, 2 running, 169 sleeping, 0 stopped, 1 zombie
%Cpu(s): 25.9 us, 0.7 sy, 0.0 ni, 73.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3049520 total, 1544560 free, 656260 used, 848700 buff/cache
KiB Swap: 2097148 total, 932648 free, 1164500 used. 2180604 avail Mem
可以看到有一个僵尸进程。
当僵尸进程产生之后,应该如何把僵尸进程从系统中干掉呢。
实践: 使用kill -9 命令尝试去杀死
root@ubuntu:~$ kill -9 106950 && ps -aux | grep "106950"
root 106950 0.0 0.0 0 0 pts/0 Z+ 19:59 0:00 [a.out] <defunct>
我们通过一套组合拳尝试了下,发下kill -9命令是对僵尸进程没反应。
那僵尸进程应该如何干掉呢,其实最简单的方法就是干掉僵尸进程的父进程就可以回收掉,走一波操作
root@ubuntu:~$ kill -9 106949 && ps -aux | grep "106950"
root 107214 0.0 0.0 16180 1148 pts/3 S+ 20:15 0:00 grep --color=auto 106950
当我们干掉僵尸进程的父进程后,僵尸进程也就退出了。
我们将产生僵尸进程的程序运行30份,这样系统中就产生30个僵尸进程了。
root@ubuntu:zhuxl$ ./a.out &
[27] 107433
root@ubuntu:zhuxl$ This is parent process!
This is child process!, pid=107434
root@ubuntu:zhuxl$ ./a.out &
[28] 107435
root@ubuntu:zhuxl$ This is parent process!
This is child process!, pid=107436
root@ubuntu:zhuxl$ ./a.out &
[29] 107437
root@ubuntu:zhuxl$ This is parent process!
This is child process!, pid=107438
。。。。。。
可以通过top命令查看
top - 20:22:17 up 1 day, 10:20, 1 user, load average: 28.92, 12.79, 5.23
Tasks: 310 total, 32 running, 169 sleeping, 0 stopped, 30 zombie
%Cpu(s): 5.4 us, 2.1 sy, 1.4 ni, 90.8 id, 0.2 wa, 0.0 hi, 0.2 si, 0.0 st
KiB Mem : 3049520 total, 1539440 free, 660764 used, 849316 buff/cache
KiB Swap: 2097148 total, 932648 free, 1164500 used. 2176104 avail Mem
通过top命令查看系统中现在已经有30个僵尸进程了。
再通过ps -aux查看系统中的僵尸进程
root 107428 0.0 0.0 0 0 pts/0 Z 20:19 0:00 [a.out] <defunct>
root 107429 12.4 0.0 4508 732 pts/0 R 20:19 0:29 ./a.out
root 107430 0.0 0.0 0 0 pts/0 Z 20:19 0:00 [a.out] <defunct>
root 107431 12.1 0.0 4508 712 pts/0 R 20:19 0:28 ./a.out
root 107432 0.0 0.0 0 0 pts/0 Z 20:19 0:00 [a.out] <defunct>
root 107433 12.3 0.0 4508 792 pts/0 R 20:20 0:28 ./a.out
root 107434 0.0 0.0 0 0 pts/0 Z 20:20 0:00 [a.out] <defunct>
root 107435 12.4 0.0 4508 764 pts/0 R 20:20 0:28 ./a.out
root 107436 0.0 0.0 0 0 pts/0 Z 20:20 0:00 [a.out] <defunct>
root 107437 12.4 0.0 4508 744 pts/0 R 20:20 0:28 ./a.out
root 107438 0.0 0.0 0 0 pts/0 Z 20:20 0:00 [a.out] <defunct>
root 107439 12.1 0.0 4508 744 pts/0 R 20:20 0:27 ./a.out
root 107440 0.0 0.0 0 0 pts/0 Z 20:20 0:00 [a.out] <defunct>
root 107447 12.4 0.0 4508 812 pts/0 R 20:20 0:27 ./a.out
root 107448 0.0 0.0 0 0 pts/0 Z 20:20 0:00 [a.out] <defunct>
root 107519 0.0 0.1 41416 3432 pts/3 R+ 20:23 0:00 ps -aux
。。。。。。。。
再杀死僵尸进程之前 看下系统的内存情况
root@ubuntu:~$ free -h
total used free shared buff/cache available
Mem: 2.9G 565M 1.7G 10M 648M 2.2G
Swap: 2.0G 0B 2.0G
我们使用killall a.out命令杀死所有的a.out进程,其中会杀死父进程,自然所有的僵尸进程就会消失的。这时候我们通过free -h命令查看,系统的内存
root@ubuntu:~$ free -h
total used free shared buff/cache available
Mem: 2.9G 563M 1.7G 10M 650M 2.2G
Swap: 2.0G 0B 2.0G
从上面的结论可以的出,僵尸进程其实是不占内存的。在一个进程变为僵尸进程之后,此进程占用的所有的系统资源全部会销毁掉的。只是将此进程设置为僵尸状态,为了跟踪此进程退出时的状态。