进程状态
前言
本文详细讲解了进程的状态,包括进程状态的查看方式和僵尸进程、孤儿进程两种特别的状态。如要了解进程的概念、机制等,可以参考进程概念。
3、进程状态
在程序运行的时候,如果遇到一个scanf等语句,进程会暂停知道输入相应的数据,才继续运行,由此可见进程需要有不同的状态(例如运行、阻塞、挂起等),不然进程无法按照预期正常执行。
3.1状态
-
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
-
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))
-
D磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
-
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
-
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
*运行队列
进程需要执行的时候,会被加入到运行队列中,并由调度器对队列进行调度,在CPU中执行运行的进程,无论是在运行中的还是在运行队列中的进程都是在R运行状态。示意图如下:
3.2进程状态查看
- ps -l 列出与本次登录有关的进程信息;
- ps -aux 查询内存中进程信息;
- ps -aux | grep + 程序名字 查询该程序进程的详细信息;
- top 查看内存中进程的动态信息;
- kill -9 + pid 杀死进程。
举例如下图:
其中,在使用ps -l命令时,注意到几个信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID: 代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI: 代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
3.3僵尸进程(Z状态)
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
下面是一个僵尸进程例子:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
pid_t ret = fork();
if(ret == 0){
printf("child process exit\n");
exit(0);
}
else{
while(1){
}
}
return 0;
可以复制一个当前会话便于观察进程信息,下图一为上面代码运行效果,下图二为运行中的进程信息,可以看到由于子进程代码中有exit(0)而提前退出,而父进程一直等待子进程的反馈未果,因而子进程处于z状态。想要结束程序可以使用Ctrl + c 退出或使用kill命令。
进程一般退出的时候,如果父进程没有主动回收子进程信息,子进程会一直让自己处于Z状态,进程的相关资源尤其是
task_struct
结构体不能被释放
僵尸进程的危害
- 进程的退出状态必须被维持下去。父进程如果一直不读取,那子进程就一直处于Z状态
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话 说,Z状态一直不退出,PCB一直都要维护
- 那一个父进程创建了很多子进程,就是不回收,会造成内存资源的浪费。因为数据结构 对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间的,不会受会造成内存泄漏
3.4孤儿进程
- 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
- 父进程先退出,子进程就称之为“孤儿进程”
- 孤儿进程被1号systemd进程”领养“,当然要有systemd进程回收。
下面是一个孤儿进程的例子。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
pid_t ret = fork();
if(ret == 0){
for(int i=0;i<60;i++){
printf("chile process %d \n",getpid());
sleep(1);
}
}
else{
for(int i=0;i<8;i++){
printf("father process %d\n",getpid());
sleep(1);
}
exit(0);
}
return 0;
}
可以看到父进程提前退出,子进程继续执行,如果观察进程信息会发现子进程在父进程提前退出后它的PPID变成了1。使用ps ajx | grep systemd
会发现PID是1,即1号进程就是操作系统本身。我们把这种子进程称为孤儿进程。
为什么孤儿进程的PPID会变成1?
因为子进程将来需要被释放,原来的父进程提前退出,因此子进程被系统进程”领养“,在结束后进程后释放掉子进程。