@TOC
1. 阻塞
进程因为等待某种条件就绪,而导致的一种不推进的状态
1. 举例
- 有时候电脑卡,是因为开启了太多软件,为什么启动太多程序会卡呢?
- 启动了太多的程序相当于启动了太多的进程,操作系统在用你的cpu调度时,调度不过来了,当前正在调度的在运行,没有调度的相当于卡了
- 阻塞就是进程卡住了
2. 为什么要阻塞?
- 进程要通过等待的方式,等具体的资源被别人使用完成后,再被自己使用
- 阻塞:进程等待某种资源就绪的过程
- 以硬件为例,资源可以看作磁盘、网卡、显卡等外设, 比如去银行存钱,存钱申请的单子没了,直接去存,工作人员就会说因为存钱的单子没有就绪所以不能存,再去等一会,等有单子了再来存
- 没有继续执行存钱的行为,当前所处状态为阻塞状态
3.操作系统层面上如何理解进程等待某种资源就绪呢?
资源
操作系统对于磁盘、网卡、显卡等 资源通过 先描述,在组织进行管理,把设备用结构体描述起来,再用链表组织起来 管理的本质具体解释点击这里
进程
存在大量的进程,操作系统要进行管理,也要先描述,在组织,就存在了大量的task_struct的结构体,每一个进程都是由task_struct定义出来的对象
- 将dev结构体内添加一个队列指针,若进程需要在设备资源等待,则把task_struct链接到设备资源队列的尾部
4. 总结
阻塞:阻塞就是不被调度 一定是因为当前进程等待某种资源就绪 一定是进程task_struct结构体需要在某种操作系统管理的资源下进行排队
2.挂起
- 当进程被CPU调度时进行下载,由于网断了,导致下载终止,从而使进程链接到网卡设备队列的尾部,造成阻塞 从而使CPU调用其他进程
- 若系统中内存的资源特别紧张,把占有内存的并且闲置的数据和代码,交换到磁盘当中,把内存中这部分的代码和数据释放,当该进程等待的资源就绪时,再把存入磁盘的数据和代码换入内存中,并把进程放入CPU中运行
- 把代码和数据暂时性由操作系统交换到磁盘时,此时的进程称之为挂起状态
3.Linux进程状态
- Linux源码当中进程所对应的状态
task_struct 是一个结构体,内部会包含各种属性,就有状态,通过0、1、2等数字找到对应的进程状态
1. R状态
进程只要是R状态,就一定是在CPU运行吗?
不一定
- 每个进程当中有自己的运行队列,只要该进程在队列中进行排队,运行的进程就会在运行的队列中排队,CPU调度进程时,从队列中挑选指定的进程运行就可以了,这种运行状态就叫做R状态
- R状态不直接代表进程在运行,代表该进程在运行队列中排队
证明当前进程运行状态
生成程序
创建makefile并输入如下内容
创建test.c并输入如下内容
make产生可执行程序mytest,./mytest执行可执行程序
查看进程
赋值SSH渠道生成终端2
在保证终端1中的mytest运行的情况下,在终端2中输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep
发现此时进程状态为S+ (+号后面会说) S称为休眠状态,不是R状态
test.c代码修改如下
在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
- 发现进程状态由S+变为R+
- printf循环打印,就代表要频繁访问显示器设备,循环中的printf本质就是向外设打印消息,当CPU执行printf代码时,频繁打印外设不一定就绪,进程可能在外设中排队,等资源就绪时,再把进程放入CPU上把结果写入外设中
- 说明第一次查到的S状态是阻塞状态的一种,是以休眠状态进行阻塞的
2. S休眠状态——可中断休眠
本质是一种阻塞状态
修改test.c代码如下
- ./mytest运行,并输入10 10
在保证终端1中的mytest运行的情况下,在终端2中再次输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep
- 进程状态为S,进程没有被调度,等键盘资源 即在键盘中输入数据
- 在终端1中
ctrl c
,终止mytest运行,S状态被终止
3.D休眠状态 ——不可中断休眠
想要往磁盘写入100MB的数据,由于磁盘写入数据很慢,所以进程把自己设置成阻塞状态,若内存资源特别紧张,操作系统就想要把这个阻塞状态的进程干掉,可是此时磁盘依旧还在写入数据,这样做就会使磁盘写入数据失败,最终就会使100MB的数据丢失
- 若该进程的休眠状态为D状态,使操作系统无法干掉进程,就能解决这个问题
4.T状态——暂停状态
使用kill命令,向指定的进程发信号
- 9号信号SIGKILL 表示干掉 进程
- 18号信号 SIGCONT 表示让当前进程继续运行
- 19号信号 SIGSTOP 表示暂停一个进程
暂停进程
test.c内容如下
保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
,并输入 kill-19+PID值
,终端1自动停止运行
再次在终端2中查询进程,发现进程状态由S+变为T
继续进程
若想让进程继续运行,在终端2中使用 kill -18 +PID值
带加号的问题
- 在暂停进程,又继续进程后,当前进程状态 为S,不带加号了
- 在终端1使用ctrl c,程序不停止了
- 进程状态带+,进程是在前台运行的,可以使用ctrl c 终止
- 进程状态不带+,进程是在后台运行的,可以正常执行shell指令,但在后台继续还会执行自己的代码
- 此时若想终止进程,使用
kill -9 +PID值
干掉进程
5. X状态(死亡状态)&&Z状态(僵尸状态)
- X死亡状态只是一个返回状态,你不会在任务列表里看到这个状态,所以这里这是举例时提及,但不会验证
- 我们创建进程,是为了进程帮我们办事,同时也关心结果,而main函数的返回值是进程的退出码
查看当前进程退出码
用于判定进程结果是否正确 echo $?
修改test.c的内容如下
使用make生成可执行程序mytest,./mytest执行可执行程序
- 说明mytest进程的退出码是0,进程结果正确
对于Z状态的理解
如果一个进程退出了,立马X状态,立马退出,你作为父进程,有没有机会拿到退出结果呢?
- linux当进程退出的时候,一般进程不会立即彻底退出,而是要维持一个状态叫做Z状态,也叫做僵尸状态
- 方便后续父进程读取子进程退出的退出结果
如何让我们看到僵尸状态呢?
- 子进程退出,但是不要回收子进程
举例
- 假设你在某一天看到路上有一个人躺着,你报了警,警察来了后,先封锁现场,再来人确认躺者是否死亡,法医确认这个人的死因,然后通知家属等一系列事情才能进行
- 被120、法医检查时,这个人所处的状态就叫僵尸状态
- 为了方便别人从他的残留信息中甄别他死亡的原因是什么
验证Z状态
修改test.c内容如下
保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
- 当父子进程都运行时,两者进程状态都为S+
- 当使用
kill - 9 +PID值
将子进程干掉后,再次使用指令查询进程,发现子进程为僵尸状态,父进程为S+
僵尸状态危害
- 在父子进程中,若父进程一直不读取,子进程就会处于Z状态
- 在父子进程中,若子进程一直处于僵尸状态(即退出子进程,但不回收子进程),就会在内存中一直保留该进程
- 若父进程创建多个子进程,就是不回收,就会造成内存资源的浪费