文章目录
- 前言
- 一、阻塞
- 二、挂起
- 三、运行R
- 四、休眠D
- 五、四个重要概念
- 总结
前言
上篇内容大家看的云里雾里,这实在是正常不过,因为例如 写实拷贝 等一些概念的深层原理我还没有讲解,大家不用紧张,我们继续往下学习就行!!!
在本篇开始之前,我们先来回顾一下有关 进程 的一些知识点:
- OS管理的本质是先描述,再组织
- OS并非直接管理进程 ,而是管理 进程 的 PCB(task_struct)
- PCB 中有着进程的各种信息,包括:PID、PPID、进程状态等
- 我们可以通过函数 getpid() 获取当前进程的 PID
- 进程 间存在父子关系,可以通过 fork() 主动创建 子进程
- 父子进程 相互独立,共享一份代码时,具有 写时拷贝 机制
一、阻塞
先来看看进程状态图
- 阻塞 就是 进程 因等待某种条件就绪,而导致的一种不推进状态
- 通俗来说,阻塞 就是 进程 卡住了,原因就是缺少资源
比如在我们日常生活中,常常发生堵车,这就是因为 道路资源 不够用了,车辆这个 进程 就需要原地等待
那么 进程 需要什么资源呢?
- 比如 磁盘、网卡、显卡 等各种外设
- 假设你现在想在 steam 上下载游戏,当你点击下载按钮后提示磁盘空间不足,此时是无法运行 steam下载 这个进程的,因为此 进程 需要等待足够大的 磁盘资源
- 此时我们就称此 进程 为 阻塞 状态
一言以蔽之,进程阻塞就是不被调度,原因是 进程 的 task_struct 结构体需要在某种被 OS 管理的资源下排队
二、挂起
理解 进程阻塞 后,理解 进程挂起 就比较轻松了
其定义为:当 CPU 资源紧张时,将 进程 交换至 磁盘 中挂起,此时内存中只有 PCB,所以 挂起 可以看作一种特殊的阻塞状态
比如在我们日常生活中,一边走路一边玩手机很危险,所以此时我们会将玩手机这个 进程挂起 ,即把手机揣进兜里,然后 专心执行走路 这个 进程
进程是何种状态,取决于此进程的PCB在哪里排队!!! 本质无外乎链表的增、删、查、改!!!
三、运行R
一个程序在运行就表示该 进程 处于 运行 状态,那么事实真的如此吗?
可以看到当前的进程状态为 睡眠 S+
注: + 表示当前进程在前台运行中
原因在于:
- 运行了,但我们很难捕捉到
- 根据冯诺依曼体系, printf 也是将内存里面去写的,相当于显示器也有自己的缓存,printf 也有自己的 I/O,因为 I/O 的速度很慢,所以大部分时间都在 I/O, 所以我们进程大部分都在等待状态
四、休眠D
存在一种特殊睡眠状态 休眠 D,休眠 又被称为不可中断休眠,顾名思义,休眠 D 状态下的 进程 是无法终止的,kill 指令和 OS都无能为力,只能默默等待 进程阻塞 结束,拿到资源了,进程 才会停止 休眠 D 状态
倘若存在 休眠 D 进程长时间运行,那么此时就表示系统离宕机不远了
我来画个示意图来解释一下为什么会有这么一个特殊的进程状态,
情景是 进程A 想将一整天银行的交易记录(100w条)永久保存到磁盘里面,进程A把磁盘叫出来,说:我这边有100w条数据,请你帮我保存到你那边去呗,而磁盘相对于内存来说是非常慢的,而这个时候进程A就只能阻塞了,就被链入到磁盘等待队列 struct device* devices 中了,而状态变为S(假设,事实上是D,后面就知道为什么不能了),这个时候进程A就翘脚嗑瓜子了,让其他进程运行,但是这个时候,OS急冲冲地跑过来,说:我们内存资源已经不足了,你再占用资源我自己都要寄了,覆巢之下,岂有完卵!,我只好把你干掉了。这个时候磁盘发现写到第80w的时候发现这个时候磁盘也要慢了,只好告诉进程A:赶快告诉用户,写入失败了,赶快返回了错误码。但是这个时候进程A已经被OS干掉了,磁盘只好把数据丢弃了,也就造成了数据的丢失
数据丢失导致银行老板把进程A、OS、磁盘叫到办公室,磁盘委屈道:我就是个臭跑腿的,我已经将数据写入失败的可能告诉你了,我跟进程B,进程C都是这样干的,这不怪我!进程A说:这不怪我,这怪OS把我给干掉了,人在家中坐,锅从天上来,OS说:这不怪我,我本来就是管理者,我只是在履行保护内存资源的职责!
很明显,明察秋毫的我们发现了其实是银行的问题,定义了一个新的状态D深度睡眠,用于当 OS 和 磁盘 进行 I/O阻塞 的时候,这就是D状态的由来
五、四个重要概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便有了优先级。
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
并行: 多个进程在多个CPU下分别同时进行运行,这称之为并行。
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
CPU执行进程代码,不是把进程代码执行完毕,才开始执行下一个,而是给每一个进程预分配一个 时间片 ,基于 时间片 ,进行调度轮转(单CPU下)
总结
这篇有点水,下篇争取来个实的!