僵尸进程
来源:互联网 发布: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
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- 僵尸进程
- HTML/XML转义字符对照表
- hrbust 2190 矩阵快速幂模板
- Oracle Linux的系统配置和限制
- 移动端web开发技巧
- js调用手机输入法搜索事件
- 僵尸进程
- RocketMQ 迈入50万TPS消息俱乐部的优化工作
- 欢迎使用CSDN-markdown编辑器
- Android Butterknife使用
- java基础改学C++(三)函数:内置函数,函数模板,默认参数函数
- PHP实现登录功能DEMO
- scala--快速了解Breeze
- 用Java Serialization实现任意文件网络传输
- Log调试的技巧