Linux中进程的六种状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
R运行状态(running)
并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列。
问题:如果只有一个CPU,可不可以存在多个R状态的进程。 现场试一下:用fork创建子进程,试一下
可以看到,是可以存在多个运行状态的。 进程是R状态,不代表正在运行,代表可被调度。换句话说,进程只有是R状态才可被调度,其他状态要先转为R状态,才能被OS调度。
S睡眠状态(sleeping)
意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。 代码实现一下:
休眠状态是在等待某种条件就绪,在休眠状态,可被操作系统杀死,也叫浅度睡眠。
处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。
由上图看到,一般情况下,进程列表中的绝大多数进程都处于S状态(除非机器的负载很高)。毕竟CPU就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来。
D磁盘休眠状态(Disk sleep)
有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
可是为什么有了S状态,又来一个D状态呢?有什么作用呢? 举例说明:
T停止状态(stopped)
可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
Z僵尸状态(Zombies)
代码实现一下僵尸状态: 子进程5秒结束,父进程死循环,不读取子进程结束信息。
5秒之后,子进程变为僵尸状态。
僵尸进程是什么
是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返回代码时,就会产生僵死(尸)进程, 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
为什么要有僵尸进程
因为我们必须得保证一个进程跑完,启动这个进程的父进程或是操作系统必须得知道这个进程退出时,把我们交代得任务完成得怎么样了,成功还是失败了。必须要知道子进程得运行结果。当子进程退出的时候,它的信息不会立即释放,会存在PCB中,没有人读取,这个状态不会被释放掉,这个状态就是僵尸状态。
僵尸进程的危害
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!
因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空 间!内存泄漏?是的!
X死亡状态(dead)
这个状态只是一个返回状态,你不会在任务列表里看到这个状态。当父进程读取子进程的返回结果时,子进程立刻释放资源。死亡状态是非常短暂的,几乎不可能通过ps命令捕捉到。
孤儿进程
所谓孤儿进程,故名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。
孤儿进程会被init进程(1号进程)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害. 代码实现一下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id == 0){
printf("I am child, pid : %d\n", getpid());
sleep(10);
}else{
printf("I am parent, pid: %d\n", getpid());
sleep(3);
exit(0);
}
return 0;
}
由图可以看出,当父进程结束时,父进程由4416变为1号进程,子进程4417被1号进程领养。
|