前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >僵尸进程

僵尸进程

作者头像
DragonKingZhu
发布2020-03-24 16:11:26
1.3K0
发布2020-03-24 16:11:26
举报
文章被收录于专栏:Linux内核深入分析

先来看一个在之前遇到的例子:

代码语言:javascript
复制
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的数据中可以看出一下几点:

  • 其中已经有20个僵尸进程了
  • 测试过程中肯定出现了内存不足的情况,可以看到swap已经有1.2G使用了。说明在测试中有内存换到swap分区的操作。
  • 此机器是8核的cpu,可以看到cpu达到了100%,而其中绝大多数的cpu占用是在内核态

当然了,我们本节重点分析僵尸进程产生的原因,僵尸进程是怎么产生的。

进程的正常退出

在平时的工作中僵尸进程是很难遇见的,僵尸进程其实进程死亡前的一个临界状态。我们还是通过例子来说明

代码语言:javascript
复制
#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系统调用来获取子进程退出的原因。

我们来看下运行结果:

代码语言:javascript
复制
root@ubuntu:zhuxl$ ./a.out
This is parent process!
This is child process!, pid=106600

当运行后,子进程现在在pause处暂停着,而父进程使用waitpid正在等在子进程退出的状态。

这时候另外起了一个终端,再另外一个终端上输入kill -9 106600

代码语言:javascript
复制
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查看所有的信号,然后给子进程发信号。

代码语言:javascript
复制
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

僵尸进程的产生

代码语言:javascript
复制
#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)循环住。

运行结果:

代码语言:javascript
复制
root@ubuntu:zhuxl$ ./a.out
This is parent process!
This is child process!, pid=106950

这时候子进程其实已经退出了,而父进程一直在while(1)着。这时候通过ps去查看父子进程的状态。

代码语言:javascript
复制
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命令来看下

代码语言:javascript
复制
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 命令尝试去杀死

代码语言:javascript
复制
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命令是对僵尸进程没反应。

那僵尸进程应该如何干掉呢,其实最简单的方法就是干掉僵尸进程的父进程就可以回收掉,走一波操作

代码语言:javascript
复制
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个僵尸进程了。

代码语言:javascript
复制
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命令查看

代码语言:javascript
复制
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查看系统中的僵尸进程

代码语言:javascript
复制
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
 
。。。。。。。。

再杀死僵尸进程之前 看下系统的内存情况

代码语言:javascript
复制
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命令查看,系统的内存

代码语言:javascript
复制
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

从上面的结论可以的出,僵尸进程其实是不占内存的。在一个进程变为僵尸进程之后,此进程占用的所有的系统资源全部会销毁掉的。只是将此进程设置为僵尸状态,为了跟踪此进程退出时的状态。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程的正常退出
  • 僵尸进程的产生
  • 僵尸进程如何灭掉
  • 僵尸进程到底占不占内存
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档