Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1614755
  • 博文数量: 441
  • 博客积分: 20087
  • 博客等级: 上将
  • 技术积分: 3562
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-19 15:35
文章分类

全部博文(441)

文章存档

2014年(1)

2012年(1)

2011年(8)

2010年(16)

2009年(15)

2008年(152)

2007年(178)

2006年(70)

分类: Java

2008-04-26 08:35:39

在linux中你可能进程听到有僵尸进程,那么究竟什么是僵尸进程,他又是怎样产生的呢?下面我们通过1个例子来说明一下。

我们知道退出一个进程用系统调用exit, 但是这并不意味着该进程马上就消失了,事实上它还留下了一个被称为僵尸进程(Zombie)的数据结构。在Linux进程的5种状态中,僵尸进程是非常特 殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收 集,除此之外,僵尸进程不再占有任何内存空间。从这点来看,僵尸进程虽然有一个很酷的名字,但它的影响力远远抵不上那些真正的僵尸兄弟,真正的僵尸总能令 人感到恐怖,而僵尸进程却除了留下一些供人凭吊的信息,对系统毫无作用。

也许读者们还对这个新概念比较好奇,那就让我们来看一眼Linux里的僵尸进程究竟长什么样子。

当一个进程已退出,但其父进程还没有调用系统调用wait(稍后介绍)对其进行收集之前的这段时间里,它会一直保持僵尸状态,利用这个特点,我们来写一个简单的小程序:

下载:
  1. #include <sys/types.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. /*
  5.     下面的程序展示了僵尸进程
  6.     进程关系:
  7.     主进程(main) ,主进程创建了父进程之后,就退出了
  8.         |
  9.         |-父进程  该父进程打印信息之后,一直循环 while(1)
  10.             |
  11.             |-子进程  该子进程创建之后打印就退出,成为僵死进程,因为他的父进程还在运行
  12. */
  13.  
  14. int main()
  15. {
  16.     /*主进程创建子进程*/
  17.     pid_t child = fork();
  18.  
  19.     /*在子进程中*/
  20.     if ( child == 0 ) 
  21.     {
  22.         pid_t pid;
  23.         char *message;
  24.         int n;
  25.        
  26.         printf("fork program starting\n");
  27.         /*子进程再创建子进程*/
  28.         pid = fork();
  29.         switch( pid )
  30.         {
  31.         case -1:
  32.             perror("fork failed.\n");       
  33.             exit(1);
  34.             break;
  35.         case 0:
  36.             message = "This is the son.\n";
  37.             n = 2;
  38.             break;
  39.         default:
  40.             message = "This is father.\n";
  41.             n = 5;
  42.             break;
  43.         }
  44.         /*父子进程都打印输出*/
  45.         for (; n > 0; n-- )
  46.         {
  47.             puts(message);
  48.             sleep(1);
  49.         }
  50.         if ( pid == 0) /*子进程退出, 成为僵死进程*/
  51.             exit(0);
  52.         else   /*父进程一直循环*/
  53.             while(1);
  54.       }
  55.       else if ( child > 0) /*在主进程中,主进程退出,剩下了1个子进程和子进程的子进程*/
  56.             exit(0);
  57. }

在这个程序中有三个进程,主进程,新进程(父进程)和他的子进程。 主进程创建了新进程(父进程)之后立刻就退出了,他的资源会被init进程回收,现在只剩下了新进程(父进程)和它创建的子进程,而这个新进程(父进程) 创建的子进程输出了一些信息之后就立刻就退出了,就只剩下了新进程(父进程),可是这个父进程并没有调用wait来回收它创建的子进程(虽然此时他的子进 程已经退出了,但是仍在在进程表中占有一席之地,这个需要父进程用wait来回收),而此时此刻,这个父进程仍在死循环中(while(1);),于是他 的子进程便成为僵尸进程, 我们用 ps aux就可以看到下面的东西:

USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1923 29.0 0.1 2520 308 pts/4 R 00:41 0:09 ./zombie.o
root 1924 0.0 0.0 0 0 pts/4 Z 00:41 0:00 [zombie.o]

第2行的Z以及都表明这([zombie.o])是一个僵尸进程, 它的父进程./zombie.o一直在Running(R),
此时的僵尸进程我们已经无法用kill将其杀掉,要想将其杀掉,有两个方法,一个是重启机器,另一个就是变通的方法,杀掉它的父进程,当我们杀掉了它的父 进程之后,这个僵尸的父进程就会变为init(所有的进程,当它的父进程被杀掉之后,它父进程的父进程就会接管它,以此类推,直到最后由init来接管 它),而init进程是一个系统进程,它每隔一段时间就会用wait来回收这些没有父母的僵尸进程。在这里,当你用kill 1923杀掉它的父进程之后,你再用ps 命令就看不到它了。

我们就linux的发展来看一看僵尸进程:我们知道,Linux和UNIX总有着剪不断理还乱的亲缘关系,僵尸进程的概念也是从UNIX上继承来 的,而UNIX的先驱们设计这个东西并非是因为闲来无聊想烦烦其他的程序员。僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,首先,这个进程是 怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程强迫退出的?其次,这个进程占用的总系统CPU时间和总用户CPU时间分别是多少?发生页错误 的数目和收到信号的数目。这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与之相关的信息都立刻归于无形,而此时程序员或系统管理 员需要用到,就只好干瞪眼了。

那么,我们如何收集这些信息,并终结这些僵尸进程呢?就要靠我们下面要讲到的waitpid调用和wait调用。这两者的作用都是收集僵尸进程留下的信息,同时使这个进程彻底消失。

所以,防止僵尸进程的方法就是要记得调用系统调用wait及时的将其回收。如果你不及时回收,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。关于wait系统调用的详细介绍请看。

阅读(1241) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~