CHS_03.2.1.4+进程控制
那什么是进程控制呢
进程控制的主要功能是对系统当中的所有进程实施有效的管理 它具有创建新进程 撤销已有进程 实现状进程 状态转换等功能
那其实说白了进程控制 它就是要实现进程的状态转化
比如说创建一个新进程 那不就是让一个进程从无到有到创建态再到就绪态
这是创建新进程所需要干的事情 那撤销一个已有进程不就是让进程进入终止态 然后最终把这个进程干掉的
一个过程嘛 所以其实所谓的进程控制就是要实现这些进程的状态转换
那么在进程的状态转换的时候 操作系统需要做一些什么事情呢 这就是这个小节当中我们要讨论的内容
知识总览
那刚才我们简要的了解了什么是进程控制 接下来我们会介绍怎么实现进程控制
如何实现进程控制?
需要用原语来实现 那这个一会会展开之后 我们会介绍几个进程控制相关的原语 它们分别需要实现哪些功能
那接下来我们要探讨的问题是怎么实现进程控制 刚才提到进程控制 也就是进程的状态转换
相应的处理是需要用原语来实现的 而原语这个概念我们在第一章当中
提到过操作系统的内核中有一些特殊的程序 它叫原语 原语 这种程序它的执行是具有原子性的
也就是说 这个程序运行是必须仪器合成的 中间不可以被中断 也就是说 实现进程的状态转换 这个事情中间的一系列操作必须一气呵成
那么接下来我们来考虑一下这样的问题 为什么进程控制或者说进程的状态转换这个过程需要一气呵成呢
如何实现进程控制?
我们结合上一小节当中学习到的知识 我们知道在pcb当中会有一个变量 用来表示进程当前所处的状态
比如说这个state变量 当它为一的时候 我们认为它是在就绪态 当它为二的时候在阻塞态
那么如果一个进程它处于就绪态 state等于一的话 那这个进程的pcb肯定是需要挂在就绪队列里的
而如果state等于二的话 那么这个进程的pcb就应该被挂在阻塞队列里
那么 接下来我们来考虑这样一个事情 我们知道处于阻塞队列的这些进程 他肯定是在等待某种事件的发生
那假设现在这个进程二也就是pcb二所对应的那个进程 它所等待的事件已经发生了 那么 在这种情况下 这个进程是不是应该从阻塞态转换为就绪态呢
所以 操作系统当中的内核程序就需要把这个进程的状态从阻塞态变为就绪态
那进行状态转换的这个过程 他至少需要做这样的两件事情 第一件事要把pcb当中state这个变量
从二变为一第二件事 他还需要把这个pcb二从阻塞队列当中宅出去 然后挂到就绪队列当中
所以 操作系统再让这个进程的状态发生转换的过程当中 他至少需要干这样的两件事
如何实现原语的"原子性"?
那么 接下来我们来考虑这样一个事情 假设现在state一已经被他设为了一
而在完成了这一步之后 突然又检测到了一个中断信号 那么 既然检查到了中断信号
那系统肯定需要对这个中断进行处理 而在这个时候 pcb 当中state等于一也就是说
从state这个变量来看 这个进程它的状态是就绪态 但是从他所处的对列来看 这个pcb二此时又还是在这个阻塞对列当中
所以这就导致了pcb二当中的这个变量所表示的状态和
pcb二他所处的这个对列 这两个信息对不上了 所以说 如果操作系统让这个进程的状态转换的这个中间处理的步骤
不能一气呵成的话 就有可能会出现我们在这个地方所看到的这种啊某些关键的数据结构信息不统一的情况
那这些数据结构是非常重要的 这有可能会影响到操作系统 进行后续的别的一些工作 可能会让系统出错
所以讲到这儿 大家应该就可以明白为什么进程的状态转换 或者说进程控制的过程需要一气呵成了
那刚好源于这种特殊的内核程序 它具有一气呵成不可被中断的这种美好的性质
所以我们可以用原语这种特殊的程序来实现一气呵成这样的事情
那么 接下来我们要探讨的问题是 为什么原语这种特殊的程序 它可以一气呵成不被中断呢
如何实现原语的"原子性"?
其实它的这种原子性是用两个特权指令 关中断和开中断这两个指令来实现的
我们来看一下这两个指令的作用 那假设这是一个正在运行的内核程序
那cpu会依次执行这些指令 并且根据第一章的讲解 我们知道 cpu每执行完一条指令之后
他都会例行的检查是否有中断信号需要处理 那如果说他在执行了指令二之后 cpu发现此时有一个中断信号
那在这种情况下 cpu就会暂停执行当前的这个程序 转而执行一个处理中断的程序 那等这个中断处理完成之后 他才会再回到原来这个程序 继续往下执行
那这是我们之前认识到的情况 就是cpu每执行完一条指令 他都会检查是否有外部中断信号需要处理
那接下来我们再来看一下如果执行了关中断指令会发生什么情况 假设此时cpu正在依次的执行这些指令
然后 当他执行了关中段这条特权指令之后 cpu就不再例行检查中断信号了
所以接下来cpu会继续往下执行 那如果说此时他执行了指令a的 这个过程当中有一个外部中断信号到来了
但是此时他并不会像之前一样例行的检查是否有终端信号 而是会继续往下处理
一直到cpu执行了开中断指令之后 他才会恢复以前的那种习惯
也就是 每执行完一条指令 那就会检查一下此时是否有外部中断信号需要处理
所以当他执行了开中断指令之后 他会发现 哎 之前有一个中断信号我还没有处理
所以在这个时候 cpu才会转向执行终端处理程序 所以从刚才这个例子当中我们就可以看到
在关中段和开中段这两条指令中间的这一系列的指令序列
他们的执行肯定是不可被中断的 这样的话就实现了我们开篇提到的所谓的原子性
这段指令序列的执行肯定是一气呵成的 他中间不可能再被中断 所以这是关中断指令和开中断指令的一个
特殊的作用 那显然这两个指令他们肯定是特权指令 那接下我们来思考一个问题
如果这两个特权指令允许普通的用户程序使用的话 会发生什么情况呢
那是不是就意味着我可以在我的程序开头就植入一个关中段之力 然后一直到我的程序末尾才
在执行开中断指令 这样的话 只要我的程序上cpu运行了 那我的程序肯定会一直霸占着cpu 而不会被中断
那显然这种情况是不应该让他发生的 所以关中断指令和开中断指令他们是特权指令
只能让内核程序使用 而不能让普通的用户程序使用好的 那么到目前为止 我们知道了两个事情
第一进程控制 或者说进程的状态转化这个事情必须一气呵成 而想要做到一气呵成 我们可以用原语这种特殊的程序来实现
而原语的实现需要由开中段指令和关中段指令来配合着完成
创建原语
那接下来我们要讨论的问题是进程控制相关的这些原语 或者说相关的这些特殊的程序 他们在背后需要完成一些什么事情呢
首先来看第一个原语 这个原语是用于实现进程的创建的 如果操作系统要创建一个进程 那么它就必须使用创建原语
那么这个创建原语 他会干这样的几件事情 首先是要申请一个空白的pcb
因为pcb是进程存在的唯一标志嘛 所以要创建一个进程当然是需要创建一个和他相对应的pcb
另外呢 还会给这个进程分配他所需要的资源 比如说像内存空间等等 然后还会把这个pcb的内容进行一些初始化的工作
比如说分配pid设置pid等等 最后他还会把这个pcb插入到就绪队列
所以说 创建原语让一个进程从创建态进入到了就绪态 把它放到了就绪队列里
那有一些比较典型的事件会引起操作系统使用创建原语创建一个进程
比如说当一个用户登录的时候 操作系统会给这个用户建立一个与他对应的用户管理进程或者用户通信进程等等
或者发生作业调度的时候也会创建一个进程 根据去年的反馈 很多同学
不知道作业到底是什么 其实作业就是此时还放在外存里的那些
还没有投入运行的程序 所以所谓的作业调度就是指从外存当中挑选一个程序 让他把它放入内存 让它开始运行
那我们知道 当一个程序要开始运行的时候 肯定是需要创建和他相对应的进程的 所以当发生作业调度的时候
就需要使用到这个创建原语 另外 呢 有的时候 一个进程可能向操作系统提出某些请求 然后操作系统会
专门建立一个进程来处理这个请求 还有的时候 一个进程也可以主动的请求创建一个子进程
总之 发生这些事件的时候 都会引起系统创建一个新的进程 也就是说 它会使用到这个创建原语
那接下来要看的是撤销原语 撤销原语是要终止一个进程的时候使用的
撤销原语
使用了撤销原语之后 就可以让一个进程从某一种状态
转向终止态 最终这个进程从系统中彻底消失 那撤销原语需要做这样的一些事情
首先 既然要撤销一个进程 那肯定需要找到这个进程相应的pcb 那如果说这个进程此时正在运行的话
那就需要立即剥夺他的cpu使用权 然后把cpu分配给其他进程 同时操作系统在杀死一个进程的时候还会杀死所有他的子进程
并且这个进程被撤销之后 他之前所占有的那些资源应该归还给
他的父进程 最后的 最后还需要把这个进程的pcb从系统中删除 那这样的话 这个进程就彻底的
完蛋了 有的同学可能不理解子进程父进程这样的概念 我们来看一个很实际的例子
这是我现在正在录视频的时候使用的电脑 这个界面显示的是此时我的电脑当中
正在运行的各种进程 这个界面中的这种显示方式其实就反映了这些进程之间的父子关系
可以看到 这儿有一个pid为零的进程 它是最祖先的一个祖先进程
然后 这个祖先进程 他创建了一个pid为一的进程 这个进程叫做l
其实他就是我们在第三章会学习到的 所谓的装入程序 或者叫装入进程 在开机了之后 我们启动的 所有的别的那些进程其实都是这个
装入进程来启动的 所以别的那些进程都是这个装入进程的子进程
那除了他之外 其他的那些进程也可以创建自己的子进程来完成相应的一系列工作
比如说访达这个进程 他就创建了这几个他自己的子进程 那这样的设计方法有什么优点呢
我们可以想一下 刚开始系统中几乎所有的资源都是这个进程所拥有的
比如说我的电脑里八gb的内存全部是他所拥有的 那之后当他在建立自己的这些子进程的时候
他可以按照这些子进程的需要 把自己手里的那些资源再分配给他手底下的这些进程
比如说他本来手里有八gb的内存 那他把其中的50MB分配给了这个进程
把54 5兆字节分配给了这个进程 而当他的这些子进程终止了之后
那当然是需要把自己手里的这些资源还给他们的父进程 也就是上面的这个进程
所以回到刚才我们提到的这个地方 相信大家就更容易理解了 其实我们的操作系统当中的各个进程 他们之间的关系是一种树形的结构
系统中的零号进程和一号进程是最祖先的两个进程 然后这两个进程又依次创建了他们的子进程
各个进程又可以在创建各自的子进程 所以这些进程之间的关系其实是一种树性的结构 不过这个并不是我们操作系统这一门课要考察的重点 只是为了让大家能够更深入的理解这提到的这两个特性
希望可以把大家绕晕 那我们回到正题继续往下 很多事件有可能会引起一个进程的终止
比如说一个进程自己请求终止 也就是他使用了exit这个系统调用
那这种情况要操作系统在背后就会需要使用到这个撤销原语来把这个进程撤销掉
另外呢 如果一个进程做了一些非法的事件 比如说整述出10或者非法使用特权指令 这种情况也会被操作系统强行的撤销 强行把它干掉
还有一种情况 有时候是用户会选择杀掉一个进程 比如说我们在使用windows电脑的时候 经常出现卡死的情况 那这种情况下 很多同学喜欢用这样的方式打开任务管理器 然后结束掉某一个卡死的进程
那这种就是外界干预的情况 那接下来我们要看的是阻塞原语和唤醒原语
阻塞原语和唤醒原语
啊 这个小节的内容确实比较多 但是其实我们这些原语 他到底要干什么
这些事情我们并不需要死劲背 我们只需要理解他背后的过程就可以 考试的时候不可能让你默写这些东西的
所以我们着重以理解为主 大家不需要刻意的记忆 那回到这儿 有的时候 一个进程可能会从运行态进入到阻塞态
那在这种情况下 操作系统就会在背后执行一个阻塞原语来实现这个状态的转换
阻塞一个进程需要做的事情比较简单 首先是找到这个进程对应的pcb
然后需要保护进程运行的现场 什么叫保护进程运行的现场 这个我们一会再解释 这又是一个比较庞大的话题
另外呢 系统还需要把pcb当中的状态信息设置为阻塞态 然后然后让这个进程下处理机
并且把它插入到相应的等待对列当中 那经过上个小节的学习 我们知道
一个进程需要被阻塞 那肯定是因为他主动请求要等待某一个事件的发生
而如果这个进程 他所等待的事件发生了 之后这个进程就会被唤醒 也就是说 操作系统会让这个进程的状态从阻塞塔又回到就绪塔
那在这个时候就会需要使用到唤醒原语 唤醒原语需要做这样的几个事情 首先要找到他的pcb 然后把pcb从等待对列当中移除 然后把它设置为就绪态
并且把pcb插入到就绪队列当中 等待被调度这两个原语做的这些事情
相信都不难理解 那需要注意的是一个进程因为什么事情被阻塞 就应该被什么事情给唤醒
所以唤醒原语和阻塞原语 他们必须是成对使用的
那接下来我们再来认识最后一个源语 叫做切换原语
进程的切换
切换原语会让此时正在处于运行态的进程下处理机让他回到就绪堆列
并且从就绪队列当中选择一个处于就绪态的进程 让他上处理机运行 所以切换原语会让两个进程的
状态发生改变 那切换原语需要做这样的一些事情 首先是需要把进程的运行环境信息存到pcb当中
什么叫进程的运行环境信息呢 这点涉及到一些硬件的知识 我们一会再展开细聊
另外呢 他还会把进程的pcb移到相应的队列 比如说 让这个下处理机的进程的pcb回到继续队列当中
另外呢 他还会挑选了一个进程 让他上处理及运行 并且更新他的pcb的内容
同时 他还会从这个进程的pcb当中恢复这个进程所需要的运行环境
那什么叫保存运行环境 什么叫恢复运行环境 这是比较难理解的地方
接下来我们得深入探讨一下这个问题 那在之前的学习中 我们认识到了一个程序的运行需要经历这样一系列的流程 程序运行之前需要把它相应的这些指令把它放入到内存当中 然后cpu从内存中读取这些一条一条的指令并且执行
知识滚雪球:程序是如何运行的?
但是接下来我们要拓展一个更深层的细节 cpu在执行这些指令的过程中需要进行一系列的运算
那么 cpu当中会设置很多的寄存器来存放这些指令 这些程序在运行过程当中所需要的某些数据
总之 寄存器就是cpu里边用于存放数据的一些地方 那cpu当中会有各种各样的寄存器
比如说我们之前提到过psw就是程序状态字寄存器 cpu的状态 内核态还是用户态
这个状态信息就是保存在psw这个寄存器当中的 当然除了cpu状态信息之外 psw中还会保存别的一些信息
那这我们就不展开 这是计算机组成原理里边需要学习的地方 另外 cpu中还会有一个比较关键的
寄存器叫做pc 也就是程序计数器寄存器 这个寄存器里边存放的是
接下来需要执行的指令 它的地址是多少 那这点我们一会结合实力就很好理解了
另外 cpu当中还会有一个指定寄存器 这个寄存器当中存放的是当前cpu正在执行的那条指令
还有呢 cpu中还会有一些其他的通用的寄存器 可以用来存放一些别的必要的信息等等等等
总之 cpu当中会有一系列的寄存器 我们这只列举了几个操作系统这门课当中大家需要
稍微的了解一下的寄存器 那接下来我们来分析一下这样的一些指令序列的执行在背后发生了什么样的事情
我这自己胡乱写了四条指令 这四条指令所完成的事情就是定义了一个叫做x的变量
并且实现了x加加的操作 那假设此时cpu正在执行的是指令 一
那么 他会把指令一的内容读到IR 也就是指令寄存器当中 并且程序计数器这个寄存器当中会存放
接下来他应该执行的那条指令 也就是指令二的地址 那 此时cpu执行指令一他发现指令一是让他往内存的某一个地方写入一个变量x的值
那cpu执行指定一的时候 就会往内存的某一个地方写入变量x的这个
直也就是一那执行完指令一之后 cpu就会开始执行下一条指令
而从pc这个寄存器当中 他就知道下一条要执行的指令应该是指令二所以接下来他会取出指令二把指令二的内容放在
IR 也就是指灵机存器当中 同时pc的内容也更新为再下一条指令
那指令二是让cpu把变量x的值 把它放到某一个通用寄存器当中
所以 cpu会从内存中取出这个x变量的值 把它放到通用寄存器当中 于是这个寄存器的内容就变成了一
那这样的话 就执行完了指令二那再接下来cpu又要执行再下一条指令 所以它会取出指令三
然后pc的内容 同样的也会更新 那指令三是让他把寄存器当中的这个数据进行加一的操作
所以 这个通用寄存期中的值就会从一变成二再接下来cpu又会执行再下一条指令
那么 此时执行的这条指令指令四是让他把这个通用寄存器当中的内容
把它写回到变量x所存放的这个位置当中 所以执行了指令四之后
就会把内存当中x的值从一变成了二所以可以看到我们执行x加加这样的操作其实在背后
cpu是执行了一系列的 更基本的那些指令才完成了这个事情 并且从刚才讲的这个过程当中我们会发现
这些指令顺序执行的过程当中 有很多中间结果是放在这些寄存器当中的
比如说x加加 这个操作刚开始其实只是把它放在了通用寄存器里 而并没有写回内存
但是需要注意的是 这些寄存器并不是这个进程所独属的
如果其他进程上cpu运行的话 那么这些寄存器也会被其他进程所使用
那这会发生什么情况呢 我们再把这个故事从头捋一遍 现在这个cpu 他要依次的执行这些指令
那刚开始执行指令 一指令二指令三当他执行了指令三之后
寄存器里的这个值变成了二而此时如果说他要转向执行另一个进程 会发生什么情况呢
刚才我们说到 如果另一个进程上cpu运行的话 那么另一个进程也会使用到这些寄存器
所以 另一个进程在上cpu运行的时候 有可能会把前一个进程在寄存器当中保留的这些中间结果给覆盖掉
比如说它覆盖成了这个鬼样子 那我们之前的这个进程不是执行到了指令三吗
因为它的前三条指令执行的那些中间结果都已经被覆盖了 所以这个进程已经没有办法再往下执行了
所以为了解决这个问题 可以采取这样的策略 当一个进程他要下处理机的时候
可以把它之前运行的这个运行环境的信息把它保存在自己的pcb当中
当然 这个pcb当中并不需要把所有的寄存器信息都保存下来 只需要保存一些必要的信息就可以了
比如说 psw pc还有这个通用寄存器 那这个进程执行了前三条指令之后 它的运行环境是这个样子的
我们把它放到了pcb当中 接下来才可以切换成别的进程 那接下来别的进程在使用cpu的时候 可能会往这些寄存器当中写各种各样的数据 总之之前那个进程的数据有可能会被覆盖
但是 当之前的这个进程需要重新回到cpu运行的时候 操作系统就可以根据之前保存下来的这些信息来恢复它的运行环境了
那把他的运行环境恢复之后 cpu就知道接下来他要执行的是指令四
并且此时通用寄存器当中存放的数值是二所以既然接下来要执行的是指令四那cpu就会根据pc的这个指向
把指令四的内容取到IR这个计算器当中 然后让pc直向下调指令
同时 cpu开始解析这条指令时到底是要干什么 他发现指令四是 让他
把寄存器当中的内容写回到x存放的位置 所以接下来他就可以把二这个内容写回到x的这个位置 于是x加加 这个操作就真正的被完成了
总之呢 这个地方讲了这么多的内容 想让大家知道的就是什么叫做进程的运行环境
其实所谓的运行环境 或者说进程上下文 它就是进程在运行过程当中
寄存器里存储的那些中间结果 当一个进程需要下处理机的时候 需要把它的这个运行环境把它存到自己的pcb当中
而当一个进程需要重新回到cpu运行的时候 就可以从pcb当中恢复他之前的这个运行环境 让他继续往下执行了
所以 保存进程的运行环境和恢复进程的运行环境 这是实现进程并发执行的一个很关键的一个技术
知识回顾
那这个小节的干货比较多 我们讲了一些涉及底层硬件的一些知识
那在操作系统这门课当中 硬件相关的知识我们不需要去深究 只是为了让大家理解其中的某一些很关键的操作系统概念
又不得不提一些硬件的知识 毕竟操作系统是最接近硬件的一层 软件嘛所以中间扯这么多 也是为了让大家能够更好更深入的理解
那大家需要注意一下原语这个概念 它使用关中段和开中段来实现
他的执行必须一气呵成 不可中断 之后 我们介绍了一些进程控制相关的言语 各个源于他中间做了各自的事情 但是这些都不需要死记硬背
其实无论是哪一个控制原语 他所要做的无非就是这么三件事
第一就是更新pcb当中的一些信息
第二是把pcb插入到合适的对列 第三向进程创建和进程终止的时候 有可能还需要分配和回收这个进程的资源
那更新pcb的信息主要是修改这个进程的状态 也就是state那个变量 或者就是往pcb当中保存
进程的运行环境 或者从pcb恢复进程的运行环境 总之 我们了解了这些控制源于背后做的事情
能够帮助我们更好的理解进程管理 处理机管理这一系列的知识
但是这些内容确实不需要死心背 所以虽然这个小节看起来内容很多 但是更多的都是理解性的东西 大家不需要花时间去记忆
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习