tittle:关于僵尸进程和孤儿进程
进程的几种状态
- R状态 进程要么在运行要么在运行队列中。
- S状态 睡眠状态,进程在等待某事件完成(可被中断)
- D状态 不可中断的睡眠状态(通常其在第等待IO结的束)
- T状态 被停止的进程 (发送信号SIGSTOP停止进程 SIGCONT让进程继续运行)
- X状态 进程已死 已被回收 kernel你的do_exit函数返回的状态。
- Z状态 僵尸进程。。。
僵尸进程的产生
“僵尸”进程是什么?通常情况下,僵尸进程的成因是因为该进程已经执行完毕,但是该进程的父进程却无法完整的将该进程结束掉(如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,即未接受其退出状态信息,那么它就一直保持僵尸状态),而造成该进程一直存在于内存中。
一个进程在调用exit命令结束自己的生命的时候,其实
它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)所以僵尸进程需要父进程彻底结束他。
查看僵尸进程
那么如何查看一个进程是否为僵尸进程呢?
ps:将某个时间点的进程运行状态选取下来
ps aux //查看系统所有的进程数据
-A:所有的进程均显示出来
-a:不与terminal有关的所有进程
-u:有效用户相关的进程
-x:通常与a一起使用,可以列出较完整的信息
-l:较长、较详细地将该PID的信息列出
当你获知它是一个僵尸进程后,那么你该如何干掉它呢,那么首先就得了解一下进程的管理。
程序之间的相互管理,是通过给予一个信号来进行管理的
查看信号(signal):
1、man 7 signal
2、kill -l
这里显示的是一些操作进程所需要的信号及其编码。
通常情况下,我们只需记住几个特别重要的信号即可。
- 1:启动被终止的进程,可让该PID重新读取自己的配置文件
- 9:强制中段一个进程,如果该进程运行到一半(如vim)会产生.filename.swap的半产品文件
- 15:正常结束一个进程
- 18:继续运行该进程
- 19:暂停一个进程
kill -9 (+进程的pid)可以杀死这个进程
模拟一个僵尸进程
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<errno.h>
5
6
7 void FunTest()
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(0 == pid)
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 system("ps -o pid,ppid,state,tty,command");
25 printf("father process is exiting\n");
26 }
27
28 int main()
29 {
30 FunTest();
31 return 0;
32 }
执行结果:43645成为僵尸进程
模拟实现多个僵尸进程
父进程循环创建子进程,子进程退出,造成多个僵尸进程。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<errno.h>
5
6
7 void FunTest()
8 {
9 pid_t pid;
10 int n = 3;
11 while(n){
12 n--;
13 pid = fork();
14
15 if(pid < 0)
16 {
17 perror("fork error");
18 exit(1);
19 }
20 else if(0 == pid)
21 {
22 printf("I am the child process.I am exiting\n");
23 exit(0);
24 }
25 else{
26 sleep(1);
27 continue;
28 }
29
30 }
31 printf("I am father process .I will sleep two second\n");
32 sleep(2);
33 system("ps -o pid,ppid,state,tty,command");
34 printf("father process is exting\n");
35
36 }
37
38 int main()
39 {
40 FunTest();
41 return 0;
42 }
执行结果:43963 43964 43965三个僵尸进程
僵尸进程解决办法
通过信号量机制
子进程退出时向父进程发出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
8 static void sig_child (int signo)
9 {
10 pid_t pid;
11 int status;
12 //deal Jiangshi process
13 while((pid == waitpid(-1,&status,WNOHANG)) > 0)
14 {
15 printf("child %d terminated\n",pid);
16 }
17 }
18
19 int main()
20 {
21 pid_t pid;
22 pid = fork();
23 signal(SIGCHLD,sig_child);
24 if(pid < 0)
25 {
26 perror("fork error");
27 exit(1);
28 }
29 else if(0 == pid)
30 {
31 printf("I am the child process. I am exiting\n");
32 exit(0);
33 }
34 printf("I am the father process. I will sleep two seconds\n");
35 sleep(2);
36 system("ps -o pid,ppid,state,tty,command");
37 printf("father process is exiting\n");
38 return 0;
运行结果:僵尸进程被父进程成功回收。
关于孤儿进程
一个父进程退出后,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<errno.h>
5
6 int main()
7 {
8 int pid = fork();
9 if(-1 == pid)
10 {
11 perror("fork error");
12 }
13 else if(0 == pid)
14 {
15 printf("child is %d,father is %d\n",getpid(),getppid());
16 sleep(2);
17 }
18 else{
19 printf("father is %d,child is %d\n",getppid(),getpid());
20 printf("father process is exuted\n");
21 }
22 return 0;
23 }
僵尸进程危害场景:
例如有个进程,它定期的产生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短,但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问,这样系统运行上一段时间之后,系统中就会存在很多的僵死进程。
倘若用ps命令查看的话,就会看到很多状态为Z的进程。 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。
那我们该如何消灭系统中大量的僵死进程呢?答案就是把产生大量僵死进程的那个元凶杀掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。杀掉元凶进程之后,它产生的僵死进程就变成了孤儿进程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了