僵尸与孤儿(Linux进程的状态)
R状态 | 要么在运行,要么在运行队列里等待运行 |
---|---|
S状态 | 浅度睡眠 – 可中断睡眠(类似于阻塞状态) |
D状态 | 深度睡眠 – 不可中断睡眠(无论如何都kill不掉) |
T状态(暂停) | 程序暂停,从前台转向后台 |
t状态(追踪) | 用gdb调试的时候,停在断点处 |
Z状态(僵尸) | 看正文 |
X状态(孤儿) | 看正文 |
左图将死未死的僵尸,并不是僵尸状态
右图的死亡后,浏览器记录还没有删掉(生前的记录还有保存),是僵尸状态
僵尸进程:
- 子进程与父进程运行时,子进程先退出了(此时子进程的声明周期已经终止,子进程的状态需要维持,他们被保存在了task_struct结构体中)
- 如果此时父进程不接收子进程的返回状态,那么子进程就会维持僵尸状态,等待父进程的接收(帮助自己删除浏览器记录)
- 等到父进程接收后,子进程就会解除僵尸状态,然后就真真正正死亡了
Linux下的Z状态
完整代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<stdlib.h>
5
6 int main()
7 {
8 size_t pid=fork();
9 if(pid==0)
10 {
11 int n=5;
12 while(n--)//5秒后子进程退出,父进程还在跑
13 {
14 printf("i am child poccess,pid:%d,ppid:%d\n",getpid(),getppid());
15 sleep(1);
16 }
17 }
18 else
19 {
20
21 while(1)//父进程死循环,不退出,也不接收子进程的返回信息
22 {
23 printf("i am father poccess,pid:%d,ppid:%d\n",getpid(),getppid());
24 sleep(1);
25 //wait();
26 //printf("wait complet\n");
27 }
28 }
29 return 0;
30 }
上述代码结果:
父子进程开始时一起运行,5秒之后,子进程退出,父进程还在运行,此时父进程没有接收子进程的返回信息。子进程进入僵尸状态
一个程序的进程结束后,状态信息还没有被接收,此时进入僵尸状态。而这个僵尸状态会一直维持到该进程的状态信息被接收
这就会导致
- 占用空间,内存泄露
- 由于操作系统提供给用户的进程数量是有限的,大量的僵尸进程可能会导致新的进程无法运行
先给出一个概念:任何进程的结束状态都是僵尸状态
参照C语言:任何函数结束的时候都有一个返回值(或者运行中途出错了,要保留出错信息告诉调用自己的函数),此时就可以理解为他们进入了僵尸状态(程序已经运行完了,等待有人来接收我的返回值)。所以有时候你在Linux上看不到一些程序运行完后的僵尸状态,是因为操作系统读取的太快了。
调用 **wait()
, waitpid()
**函数
pid_t wait(int *status);
//等待任意一个子进程,成功返回子进程pid,失败返回<0
pid_t waitpid(pid_t pid, int *status, int options);
//返回值同上,参数pid==-1时可以回收任意子进程,>0时指定回收指定pid的子进程;
//status用来接收退出信息(退出码和信号);
//options为0是阻塞等待
调用这个函数来回收子进程的资源和获取子进程的退出信息,然后子进程就会退出僵尸状态
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 size_t pid=fork();
9 if(pid==0)
10 {
11 int n=5;
12 while(n--)
13 {
14 printf("i am child poccess,pid:%d,ppid:%d\n",getpid(),getppid());
15 sleep(1);
16 }
17 }
18 else
19 {
20
21 while(1)
22 {
23 printf("i am father poccess,pid:%d,ppid:%d\n",getpid(),getppid());
24 sleep(1);
25 pid_t rpid=wait(NULL);//调用wait,来等待子进程,此时父进程不会再往后运行,直到子进程返回
26 if(rpid>0) printf("wait complet\n");
27 }
28 }
29 return 0;
30 }
运行结果:
子进程退出的一瞬间就被父进程接收了,所以本来的两个进程变成了一个进程
孤儿进程:
- 当其失去了父进程,而子进程还在跑,那么此时子进程就变成了孤儿进程
- Linux下的孤儿进程会被init进程(pid=0)回收
- 变成孤儿的进程会由前台进程变成后台进程
Linux下的X状态
完整代码:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5
6 int main()
7 {
8 size_t pid=fork();
9 if(pid==0)
10 {
11 while(1)//子进程一直运行,不退出
12 {
13 printf("i am child process,pid:%d,ppid:%d\n",getpid(),getppid());
14 sleep(1);
15 }
16 }
17 else
18 {
19 int n=10;
20 while(n--)//父进程运行10秒后退出
21 {
22 printf("i am father process,pid:%d,ppid:%d\n",getpid(),getppid());
23 sleep(1);
24 }
25 printf("father process out\n\n");
26 }
27 return 0;
28 }
上图中为何显示不出X?详见如下
由上述代码可以看出,子进程的ppid在其父进程被干掉后立马转变成为1。
这是因为子进程变成孤儿的瞬间,就被init进程接收了,并且变成后台进程,所以S+变成S(此时ctrl+c杀不死,只能用kill来杀掉)
由于init进程帮我们接手了孤儿进程,所以孤儿进程就等于换了个父进程,所以并没有什么危害
可以用 kill -19 pid
命令来暂停进程
这种状态常见于gdb调试,可以用gdb调试一个代码,运行到断点处就会显示t状态
这种是最常见的状态,分为 S+
(前台运行)和 S
(后台运行)
但是为什么我的程序分明正在运行,他却显示的是S状态,如图:
这是因为我的函数要访问硬件设备(要向屏幕打印输出),而访问硬件设备的速度是很慢的,这个时候程序在等待向硬件设备写入,所以是S+状态。
注意:R状态并和不意味着程序正在运行,他也可能正处于运行队列里,等待被运行
如何查看R状态呢?只需要对上图进行一点修改
这样就不会访问硬件设备,无需等待,就会显示R+状态
这种状态十分罕见,如果你碰到了,那么恭喜你,你的电脑也差不多挂掉了
个时候程序在等待硬件设备写入,所以是S+状态。