僵尸进程

来源:互联网 发布:2016年交通事故数据 编辑:程序博客网 时间:2024/06/05 22:53

僵尸进程

在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init(id号为1) 来接管他,成为他的父进程

1.解释

一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)

2.产生

一个进程在调用exit命令结束自己的生命的时候,其实
它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

查看

查看僵尸进程,利用命令ps,可以看到有标记为Z的进程就是僵尸进程

ps -o pid,ppid,state,tty,command

3.进程处理

它需要它的父进程来为它收尸,如果它的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态; 存在的问题:如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程,系统的性能可能会受到影响。如果这时父进程结束了,那么init进程会自动接手这个子进程,为它收尸,它还是能被清除的。4、子进程结束后为什么要进入僵尸状态? 因为父进程可能要取得子进程的退出状态等信息。5、僵尸状态是每个子进程必经的状态吗? 是的。 任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 * 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
6、如何查看僵尸进程: $ ps -el 其中,有标记为Z的进程就是僵尸进程 S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;Z代表僵死状态;T代表停止或跟踪状态

4.如何避免

⒈父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
⒉ 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
⒊ 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
⒋ 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。

5.模拟实现僵尸进程

测试一:让子进程调用exit(0),让父进程等待2s,让子进程先结束。查看此时的进程状态:

1 #include<stdio.h>                                                          2 #include<stdlib.h>  3 #include<errno.h>  4 #include<unistd.h>  5   6   7 int main()  8 {  9         pid_t pid; 10         pid=fork(); 11  12         if(pid<0) 13         { 14                 perror("fork error"); 15                exit(1);     16         } 17         else if(pid==0) 18         { 19                 printf("I am the child process.I am exiting\n"); 20                 exit(0); 21         } 22         printf("I am father process.I will sleep two seconds\n"); 23         sleep(2);//等待子进程先退出 24          //输出进程信息 25         system("ps -o pid,ppid,state,tty,command"); 26         printf("father process is exiting .\n"); 27         return 0; 28 }        

这里写图片描述

测试二

父进程循环创建子进程,子进程退出,造成多个僵尸进程。

  1 #include<stdio.h>                                                             2 #include<stdlib.h>  3 #include<errno.h>  4 #include<unistd.h>  5 int main()  6 {  7         pid_t pid;  8   9         while(1)  10         { 11                 pid=fork(); 12  13                 if(pid<0) 14                 { 15                         perror("fork error"); 16                 } 17                 else if(pid==0) 18                 {19                         printf("I am a child process.\n I am exiting.\n"); 20                         exit(0); 21                  22                 } 23                 else 24                 { 25                         sleep(20); 26                         continue; 27                 } 28         } 29         return 0;

这里写图片描述

6.僵尸进程解决办法

(1)通过信号量机制

子进程退出时向父进程发出SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程

1 #include<stdio.h>  2 #include<stdlib.h>  3 #include<errno.h>  4 #include<unistd.h>  5 #include<signal.h>                                                            6 #include<sys/wait.h>  7 static void sig_child(int signo);  8   9 int main() 10 { 11         pid_t pid; 12         pid=fork(); 13         //创建第一个进程 14         signal(SIGCHLD,sig_child);//****新加行 15  16         if(pid<0) 17         { 18                 perror("fork error"); 19             exit(1);     20         }//第一个子进程 21         else if(pid==0) 22         { 23                 printf("I am the child process.I am exiting\n"); 24                 exit(0); 25         } 26         printf("I am father process.I will sleep two seconds\n"); 27         sleep(2); 28  29         system("ps -o pid,ppid,state,tty,command"); 30         printf("father process is exiting .\n"); 31         return 0; 32 }33  34  35 static void sig_child(int signo) 36 { 37  38         pid_t pid; 39         int stat; 40         //chulijianshijincheng 41  42         while((pid=waitpid(-1,&stat,WNOHANG))>0) 43         { 44                 printf("child %d terminated\n",pid); 45         } 46  47 }     

这里写图片描述

(2)fork两次

原理是将子进程成为孤儿进程,从而其父进程变为init进程,通过init进程处理僵尸进程,测试程序如下:

1 #include<stdio.h>                                                                                                            2 #include<stdlib.h>  3 #include<errno.h>  4 #include<unistd.h>  5 #include<sys/wait.h>  6   7 int main()  8 {  9         pid_t pid; 10         pid=fork(); 11  12         if(pid<0) 13         { 14                 perror("fork error"); 15             exit(1);     16         } 17         else if(pid==0) 18         { 19                 //zijinchengzaichuangjianzijincheng 20                 printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid()); 21                 pid=fork();//子进程再创建 22  23                 if(pid<0) 24                 { 25                         perror("fork error:\n"); 26                         exit(1); 27                  28                 } 29                 else if(pid>0)29                 else if(pid>0) 30                 { 31                         printf("first process is exited.\n"); 32                         exit(0); 33                  34                 } 35                 sleep(3); 36                //睡眠3秒,保证第一个子进程退出,这样第二子进程的父亲就是init进程 37                 printf("I am the second process. pid:%d\tppid:%d\n",getpid(),getppid()); 38                 exit(0); 39         } 40         //父进程处理第一个子进程退出 41         if(waitpid(pid,NULL,0)!=pid) 42         { 43                 perror("waitepid error:"); 44                 exit(1); 45         } 46         exit(0); 47         return 0; 48 }     

这里写图片描述
参考文章:http://www.cnblogs.com/Anker/p/3271773.html

0 0
原创粉丝点击