目录
1.进程排队
硬件的队列
进程排队
2.进程的三大状态
什么是状态
运行状态
阻塞状态
挂起状态
3.Linux系统中的进程状态
4.僵尸状态
5.孤儿进程
1.进程排队
硬件的队列
计算机是由很多硬件组成的,操作系统为了管理这些硬件,通常需要为这些硬件创建数据结构,比如:
在这个数据结构中,有一个 task_struct* q 的东西,这个就是硬件的队列。每个硬件都有属于自己的一个队列;对于CPU来说,我们称它的队列为运行队列;对于键盘、显示器等设备来说,我们称它们的队列为阻塞队列。
进程排队
我们的计算机系统中的进程往往不止一个,但是大多数硬件只有一个,这些进程往往需要使用硬件,这就导致了使用同一个硬件的进程之间必须进行排队。
那么如何进行排队呢?我们可以这样理解。task_struct对象是可以通过双链表的形式组织起来的,task_struct* q 指向一个 task_struct 对象,这样就天然的形成了一个队列。
操作系统就可以 从描述硬件的数据结构中拿到 q 变量,再通过q变量拿到所有需要使用该硬件资源的进程了。
2.进程的三大状态
什么是状态
状态其实就是task_struct中的一个整形变量:
当进程处于不同硬件的队列中,status就对应不同的值,进程就处于不同的状态。
运行状态
当我们的进程准备就绪,需要运行的时候,操作系统就会改变该进程的task_struct中的prev和next指针,让这两个指针和CPU运行队列中的结点进行连接,并将该进程中的状态设置为运行状态;这样,一个进程就处于运行状态了。
阻塞状态
一个进程在CPU上运行的时候,不可能一直处于运行状态,因为,当代计算机都有一个时间片的概念。也就是说,一个进程占据CPU的最大时间为时间片的大小,当时间片结束之后,不管运行是否结束,都应该从CPU上剥离下来,给其他的进程使用CPU资源。
而且,有些进程在运行的过程中需要获取软硬件资源,比如说一个程序中有一条输入语句,当程序运行到该语句的时候,就需要等待用户输入,也就是等待资源就绪。
我们假设输入设备是键盘,当资源没有就绪的时候,操作系统就会把该进程的PCB链入等待的资源提供的队列中,也就是键盘的队列,并且将该进程的状态设置为阻塞状态:
- 目前我们就知道了:状态的变化,会引起进程的PCB对象被链入不同硬件的队列中。
挂起状态
挂起状态有一个前提:计算机资源已经比较吃紧了。
比如说:一个程序被加载到内存中形成进程之后,该进程可能在等待某种资源而处于阻塞状态,这个时候,该进程不会被CPU运行,但是内存中却要保存该进程的PCB以及该进程的代码和数据;如果计算机中的资源已经比较吃紧了,也就是说计算机中的进程比较多,已经逼近崩溃的边缘了,操作系统为了不让系统崩溃,就会对该进程的代码和数据进行换出(换出到磁盘的swap分区中),以此来缓解计算机内存的压力,等到系统压力缓解之后,或者该进程的资源准备就绪之后,再对该进程的代码和数据进行换入。
当一个进程的代码和数据被换出到磁盘的swap分区中,操作系统会将该进程的 task_struct 中的进程状态修改为挂起状态,此时的进程就处于挂起状态了。
3.Linux系统中的进程状态
Linux2.6内核源代码中关于进程状态的表示:
- R:运行状态(running)并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S:睡眠状态(sleeping)意味着进程在等待事件完成,也就是软硬件资源就绪(这里的睡眠有时候也叫做可中断睡眠)。
- D:磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态,在这个状态的进程通常会等待IO的结束。
- T:停止状态(stopped)可以通过发送 SIGSTOP 信号给进程来停止当前进程,这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
- X:死亡状态(dead)这个状态只是一个返回状态,我们查不到这个状态。
- Z:僵尸状态,当一个进程退出后,但是它的父进程还没有读取它的退出信息的时候,该进程就处于僵尸状态。
4.僵尸状态
什么是僵尸状态?僵尸状态是进程的一种比较特殊的状态,当进程退出之后,该进程的父进程没有读取到进程的退出信息时,该进程就会处于僵尸状态。在Linux系统中,僵尸状态用Z表示。
为什么要有Z状态呢?我们创建一个进程,是希望这个进程完成某项工作,那我们肯定想要知道子进程把工作完成的怎么样,所以,子进程一定会产生一些结果数据保存在PCB当中,让父进程来读取,只有父进程读取之后,我们才能知道子进程把工作完成的怎么样了。也就是说,进程退出之后,要保持Z状态来让父进程读取子进程的退出信息。
僵尸状态的危害?一个进程只有被父进程读取到退出信息之后才会完全退出,如果父进程一直不读取子进程的退出信息,那么子进程就会一直处于僵尸状态。也就是说,进程不会完全退出,既然不会完全退出,操作系统就要一直维护进程的PCB对象;PCB对象是要占据内存空间的,如果父进程有很多个子进程,父进程一直不读取子进程的退出信息,那么就有大量的PCB需要维护,内存中的这些空间被子进程的PCB对象所占据,自己不用,别人也用不了,就会造成内存泄漏问题。
如何避免僵尸状态的危害呢?在子进程运行结束之后,应该立即读取子进程的退出信息。
- 读取子进程的退出信息可以使用waitpid等系统调用接口。
5.孤儿进程
如果一个进程创建子进程之后,父进程先退出会发生什么呢?如果父进程先退出,该子进程不就没有父进程了吗?那就不会有进程来读取子进程的退出信息了,子进程不就会一直处于僵尸状态吗?
其实不然,当父进程先退出之后,子进程就会变成孤儿进程,孤儿进程会被系统的init进程,也就是pid为1的进程所领养,当子进程退出的时候,init进程就会立即读取子进程的退出信息,防止子进程变成僵尸状态;所以,我们并不需要担心父进程比子进程先退出的问题。