一、进程的概念、组成、特征
1、概念
概念:进程是一个“进程实体”运行的过程,是操作系统进行资源分配和调度的一个独立单位(注意,这个“进程实体”不能单纯理解为一个程序,因为一个程序的多次执行也对应不同的进程,比如多个微信号的微信打开)
这里尤其要注意进程的特点:
【动态性】:进程是动态的,它表示的是一个程序在运行的过程、状态
- 程序是静态的,就只是一个放在内存里的可执行文件.exe,一堆指令集合
- 进程是动态的,它是这个程序的一次执行过程(同一程序的多个执行就是多个进程)
;
【独立性】:是独立运行的,是独立获得资源的最小单位,即使同一个软件打开多个,那么这么多个被打开运行的软件依旧是独立的进程,互不影响
我们使用【Ctrl + Shift + Esc】或者右键“Win开始”图标,就能打开【任务管理器】
任务管理器就能看到我们正在运行的进程
2、进程的组成
1)PCB(Process Control Block)
那么思考,操作系统作为资源的管理者,它是怎么管理进程的、怎么区别该给哪个进程调度哪些资源?
跟我们前后端开发一样,任何用来区分信息的做法就是————设置身份识别【id】
那么操作系统一样,每创建一个进程,操作系统会给它创建一个唯一的、不重复的身份识别:【PID】(Process ID)
哪怕一个程序刚刚被关闭马上又被打开,它的PID仍然会改变
而改变的规则就只是每创建一个进程就在上一个进程PID基础上【PID++】,比如我刚刚打开qq,PID=233,然后马上关闭又马上再打开,这个qq的进程会变成PID=234
除了【PID】基本身份识别以外,操作系统还记录了进程这些信息:
1.基本进程描述信息:PID(进程ID)、UID(创建进程的该用户的ID)
根据基本进程信息,操作系统可以区别各个进程
2.记录分配了那些资源:分配多少内存、使用哪些文件、正在使用哪些I/O设备......
根据这些可实现操作系统对资源调度的管理
3.记录进程的运行情况:使用了多久CPU、网络流量使用情况、磁盘使用情况......
可用于操作系统对进程的控制、调度
;
那么这些信息都被存放到一个【数据结构】————PCB(Process Control Block)里,也叫“进程控制块”。操作系统需要用到的进程信息都放在这里。
2)程序段、数据段
进程的组成不止只有PCB,还有【程序段】和【数据段】
一个程序运行时,就是先把程序放到内存,然后开始运行
这时在运行前就要先创建记录这个进程所有信息的“数据结构块”【PCB】,然后紧接着创建【程序段】和【数据段】,将可执行程序里的所有指令放入【程序段】,将所需数据放入【数据段】
然后CPU会在进程中的【程序段】获取到一条一条“指令”,来操控各个资源运作
;
【数据段】则装运行过程产生的数据
一般来说可以把 “进程” 就理解为 “进程实体(进程映像)”,但是要仔细看这二者还是有区别的
“进程实体(进程映像)”可以理解是整个进程过程里的某一个动态片段的 “快照”,在这一时态里“进程实体(进程映像)”记录了静态的【PCB】【程序段】【数据段】信息
;
“进程”是动态的一个过程,由多个“进程实体(进程映像)”构成
另外记住:
- 1、PCB是给操作系统用的
- 2、程序段、数据段是给进程自己本身用的
3、进程的特点(了解即可)
二、进程的状态以及转换
1、进程的状态
进程分为五种状态:
- 创建态:程序刚刚从放内存准备运行,进程刚刚被创建中
- 就绪态:创建完毕,等待被操作系统交给CPU运行
- 运行态:交给了CPU,正在运行ing......
- 堵塞态:运行时当遇到某个“事件”,需要等待,那就先从CPU上退下变成“堵塞态”
- 终止态:完全运行完,结束这个进程的时候的状态
2、进程状态的转换
【创建态——>就绪态】
【创建态】就是:一个进程一开始先是一个程序【放入内存】开始运行,然后开始创建“进程实体(进程映像)”所需要的三大部分:【PCB】【程序段】【数据段】
;
【就绪态】就是:创建完之后就可以进入到就绪态了
;
可以理解为一个小银行,一个客户要办业务,先取号、准备好证件、想好自己要说什么,准备完就可以就绪准备下一个叫号到自己的时候过去办业务了
【就绪态——>运行态】
一般电脑都是只有1个CPU的,这些进程里的指令只能靠这个CPU来操作管控的时候才是一个进程正在开始运行,那么当CPU空闲时,操作系统就会让一个【就绪态】的进程上CPU上面开始运行,这时就是【运行态】
;
此时CPU已经被占用了,那其他的进程依旧保持【就绪态】
;
就好比这个小银行只有一个窗口,只有一个业务员,当你准备好了、上一个办业务的结束了业务了,那么窗口就空闲了,业务员就可以叫号让你来办业务,那么你就从就绪态进入到运行态,在这期间别的客人只能等
【运行态——>堵塞态】
【堵塞态——>就绪态】
【运行态——>堵塞态】:当一个【运行态】的进程执行某个指令遇到一个事件,比如要使用外部I/O设备打印机,而此时外部设备又在服务别的进程还没办法退出,那么这个进程就得等待,此时就会从CPU上退下来,变回【就绪态】,然后CPU开始处理别的【就绪态】的进程
;
【堵塞态——>就绪态】:那么当外部设备比如“打印机”又空闲了,刚刚【堵塞态】的进程就又可以变回【就绪态】,等待CPU上别的进程结束,在上CPU变成【运行态】
;
注意:【堵塞态】不能直接变成【运行态】,也不能从【就绪态】直接变【运行态】
;
可以理解为,假设你的业务是很复杂的,现在还需要借助经理来处理,但是现在经理还在忙(比如给朋友处理事情、骂保安.....),等了半天一个叼业务还没办完总不能一直让你占着茅坑不拉屎,那你就滚一边待着,让下一个客户来窗口办理业务;
等经理忙完了,你跟他大概解释一下情况,然后现在又准备就绪,等待窗口办理完上一个客户的业务,然后下一个又是你回去,办完你的业务
【终止态】
顾名思义,没啥好解释的,就是执行到exit指令,退出结束.....
【挂起态】
注意!!!!注意!!!!!注意!!!!!
以上这些状态都是在【内存】里的,不管是 “就绪态” 还是 “阻塞态”,他始终还是在内存,只不过是有没有被CPU处理而已
;
但是除了这几种状态,其实还有一种【挂起态】:
【挂起态】也可以细分为【就绪挂起态】和【阻塞挂起态】
;
当内存中的进程够多了,内存空间已经不够了的时候,操作系统会把一些不是那么重要的进程数据移到【外存】,有点类似被操作系统 “赶出来了”,被挂起的进程的PCB数据会被放到一个【挂起队列】,通过 “中级调度” 的某些策略来决定让哪些挂起的进程回到内存(这些知识点后面讲【调度管理】会讲到)
在【就绪态】“被赶出来” 就是【就绪挂起态】;在【阻塞态】“被赶出来” 就是【阻塞挂起态】
一定要区分,【阻塞态】还是在内存里,只是从CPU上下来了;【挂起态】是直接被赶出内存,放到外存先
可以理解成【阻塞态】只是你在这个银行窗口办业务时在等经理处理的时候,先从窗口下来在旁边等经理有空为止,但是还在银行里面;【挂起态】是这个银行已经爆满了,等着办理业务的人挤满了大厅,只好让一些业务不是那么紧急的人先出银行,在银行门口排队等着先
3、进程的组织方式
操作系统就像银行的引导员 或 叫号系统,他根据一个进程的状态来决定让一个进程该不该“办理业务”、“退出业务”......问题是操作系统是如何知道一个进程的状态的?
那是因为进程里的数据结构【PCB】里有一个【属性/字段:state】来记录进程的状态
那么操作系统为了方便管理,就会将各个进程的PCB组织起来,根据不同状态进行一个分类、排序,从而有序的管理进程工作
常见的有两种组织形式:【链式方式】、【索引方式】
【链式方式】
链式方式是最常用的方式,使用 指针 + 数组 的数据结构形式,执行指针指向【运行态】的PCB、就绪队列指针指向【就绪态】的PCB、堵塞指针指向【堵塞态】的PCB(很多操作系统还会根据堵塞的原因不同,又分成多个堵塞队列)
【索引方式】
不常用,知道即可,操作系统会给各种状态进程建立对应状态的【索引表】
然后执行指针指向【运行态】的PCB、就绪队列指针指向【就绪态】的索引表、堵塞指针指向【堵塞态】的索引表
表中的索引项指向各个PCB(因为单核计算机1个CPU,同一时刻只有一个运行态的进程,所以执行指针直接指向【运行态】的PCB)
三、进程控制
进程控制就是:操作系统到底是怎么实现各个进程的状态转换的?
1、“原语” 实现进程控制的流畅
操作系统对于进程的控制是使用 “原语” 使用的,它是一种特殊的程序,具有 “原子性” ,也就是运行时会 “一气呵成、连贯顺畅”
为什么要“一气呵成”?
2、【原语】的 “原子性” 是啥?
正常情况:CPU每执行完一条指令都会例行检查是否有中断信号需要处理,如果有,则暂停运行当前这段程序,转而执行相应的中断处理程序。
原子性:CPU执行了关中断指令之后,就不再例行检查中断信号,直到执行开中断指令之后才会恢复检查。
这样,【关中断、开中断】之间的这些指令序列就是不可被中断的,这就实现了“原子性”
注意:这两个【关闭中断】、【开启中断】指令是内核程序的【特权指令】,普通用户程序没有这些指令。
3、一个进程不同状态对应的不同类型原语
创建原语
撤销原语
阻塞原语 和 唤醒原语
切换原语
4、程序运行时【CPU】搭配【进程】的流程
涉及一点点计算机组成原理内容,可以参考我的文章:考研408《计算机组成原理》复习笔记,第一章计算机系统概述_计算机组成原理考研知识点总结-优快云博客
CPU中有很多【寄存器】用来暂存运行时需要的数据
那么【CPU】结合操作系统里的【进程】:
程序运行时,有的数据会频繁中途存在 ——> CPU里的PSW程序状态寄存器(运算器组件)、PC指令地址寄存器(控制器组件)、IR指令寄存器(控制器组件)、通用寄存器(运算器组件)......
也会用到 ——> 进程中的【数据段】
但是假设遇到堵塞状态,CPU会切换成别的进程(毕竟只有一个CPU),那么寄存器里的上一个进程的数据就会被覆盖,怎么办?
做法就是,把CPU里的寄存器的信息,备份到进程里的PCB(不是所有寄存器,只存PSW、PC和通用寄存器这些有用的)
当原来的进程再次投入运行时,可以通过PCB恢复它的运行环境
总结
四、进程的通信
进程间通信(Inter-Process Communication,IPC)是指两个进程之间产生数据交互。
比如微博分享一个链接到微信
但是各个进程之间是相互独立的!!!
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立
;
这是为了安全性考虑,你也不想在看着黄色APP的时候让APP共享你后台的相册里的照片吧....
那么就有3种通信共享方式:
1、共享存储
第一种方式是【共享存储】,操作系统会开辟一个【共享存储区】,供需要共享信息的两个进程进行数据传输,具体如何实现后面章节再讲,这里只需要知道有这么东西
共享存储还细分为两种方式:【基于数据结构的共享】和【基于存储器的共享】
1、基于数据结构的共享。:操作系统限定好了固定的存储大小,有多大的空间就存多少数据。这是低级的通信方式。
;
2、基于存储器的共享:操作系统划分出【共享存储区】之后,具体“怎么存?”、“存在什么位置?”这都是进程之间自己决定了,操作系统不干预。这是高级通信方式。
2、消息传递
进程间的数据交换以格式化的消息为单位。
进程通过操作系统提供的【发送消息】、【接收消息】两个原语进行数据交换。
消息传递又细分为2种方式:
【直接通信方式】
就是 “发送信息的进程” 使用【发送原语】,“指名道姓”指向要发送到的进程的Pid
然后操作系统就会将“发送信息的进程”的消息,放入操作系统内核里的 “被指定Pid的进程” 的PCD里的【消息队列】
然后“被指定Pid的进程”,也就是 “接收信息的进程” 就会使用 “接收原语”,去操作系统内核的自己的PCB里,把自己的消息队列里刚刚那个信息,取回自己的进程的PCB里
【间接通信方式】
不采用直接的“指名道姓”的方式传输信息,发送方只指明要把信息放哪个【信箱】
接收方也一样,取信息只取指明的某一个【信箱】里取信息
而且的多个进程可以往统一信箱发消息,多个进程也可以从同一个信箱取信息,当然也可以有多个信箱
3、管道通信方式
【管道通信】跟【共享存储】很像,都是由操作系统开启一块固定好存储大小空闲的区域,这个“管道”就是在内存里开辟的一个固定的内存缓冲区
但是!!它的数据结构跟【共享存储】完全不一样,要记得【共享存储】的数据放在什么位置,是不固定的,任意位置;但是【管道通信】的数据结构是一个 “循环队列”!!存储信息采用队列的“先进先出”的原则
【管道通信结构】
1)半双工通信:管道只能采用半双工通信,某一时间段内只能实现单向的传输
2)全双工通信
如果需要实现两个进程双向同时通信,就需要设置两个管道
然后注意:
- 各个进程要互斥的访问管道(这由操作系统完成)
- 管道写满时,写进程堵塞(直到读进程将信息取走,就可以唤醒写进程)
- 管道读空时,读进程堵塞(直到写进程写入数据,就可以唤醒读进程)
- 管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:
- ①一个管道允许多个写进程,一个读进程(2014年408真题高教社官方答案)
- ②允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据(Linux的方案)
;
【例题】:
写进程会把缓冲区写满,然后才让读进程读数据;当缓冲区里还有数据的时候,写进程不会忘里面写数据 (×)
【解释】:
写进程往管道写数据,即便管道没被写满,只要管道没空,读进程就可以从管道读数据
读进程从管道读数据,即便管道没被读空,只要管道没满,写进程就可以往管道写数据
【总结】