精华内容
下载资源
问答
  • 僵尸进程

    2020-02-15 17:59:34
    今天我们重点来分析下什么是僵尸进程僵尸进程是如何产生的,以及僵尸进程如何灭掉,僵尸进程产生的原理。 先来看一个在之前遇到的例子: Tasks: 779 total, 6 running, 753 sleeping, 0 stopped, 20 zombie ...

    之前在第一节进程的基本概念中讲到进程的状态的时候,提到进程的状态中存在一个僵尸状态。今天我们重点来分析下什么是僵尸进程,僵尸进程是如何产生的,以及僵尸进程如何灭掉,僵尸进程产生的原理。

     

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

    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占用是在内核态

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

     

    进程的正常退出

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

    #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

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

     

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,970
精华内容 16,388
关键字:

僵尸进程