进程
进程的概念、组成、特征
进程的概念
程序:是静态的,就是个存放在磁盘里的可执行文件,就是一系列的指令集合。
进程(Process):是动态的,是程序的一次执行过程
进程的组成——PCB
操作系统是这些进程的管理者,它要怎么区分各个进程
当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的“身份证号”—— PID(Process ID,进程ID)
操作系统要记录 PID、进程所属用户 ID(UID),还要记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些 I / O 设备、正在使用哪些文件),还要记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况等),这些信息都被保存在一个数据结构 P C D PCD PCD 中,即进程控制块
操作系统需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放在 P C B PCB PCB中
P C B PCB PCB 是进程存在的唯一标志,当进程被创建时,操作系统为其创建 P C B PCB PCB ,当进程结束时,会收回其 P C B PCB PCB
进程的组成——程序段、数据段
P C B PCB PCB 是给操作系统用的
程序段、数据段是给进程自己用的
一个进程由 P C B PCB PCB、程序段、数据段组成
进程是系统进行资源分配的基本单位
进程的特征
动态性 | 进程是程序的一次执行过程,是动态地产生、变化和消亡的 |
---|---|
并发性 | 内存中有多个进程实体,各进程可并发执行 |
独立性 | 进程是能独立运行、获独立获得资源的基本单位 |
异步性 | 各进程按各自独立的、不可预知的速度向前推进,操作系统要提供“进程同步机制”来解决异步问题 |
结构性 | 每个进程都会配置一个PCB。结构上看,进程由程序段、数据段、PCB组成 |
进程的状态与转换
进程的状态
进程正在被创建时,它的状态是“创建态”,在这个阶段操作系统会为进程分配资源、初始化 P C B PCB PCB
当进程创建完成后,便进入“就绪态”,处于就绪态的进程已经具备运行条件,但由于没有空闲CPU,就暂时不能运行
系统中可能会有很多个进程都处于就绪态,当CPU空闲时,操作系统就会选择一个就绪进程,让它上处理机运行,如果一个进程此时在CPU上运行,那么这个进程处于“运行态”,CPU会执行该进程对应的程序
假设现在CPU正在执行一个进程的指令,这个指令中有一条指令发出系统调用,请求使用打印机资源,而此时打印机正在为别的进程服务,所以现在这个打印机无法分配给这个进程,所以这个进程接下来的指令就无法继续执行,既然它无法继续执行,所以我们不能让它继续占用CPU资源。在进程运行的过程中,可能会请求等待某个事件的发生(如等待某种系统资源的分配,或者等待其他进程响应),在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下CPU,并让它进入“阻塞态”,当CPU空闲时,又会选择另一个“就绪态”进程上CPU运行。如果说打印机的服务结束,打印机就会空闲下来,所以当打印机空闲下来的时候,它就可以分配给刚才请求打印机资源的那个进程,所以当操作系统把打印机这个资源分配给这个进程的时候,这个进程等待的事件就发生了,操作系统会把这个进程从阻塞态回到就绪态
一个进程可以执行 exit 系统调用,请求操作系统终止该进程,此时该进程就会进入“终止态”,操作系统会让该进程下CPU,并回收内存空间等资源,最后还要回收该进程的 P C B PCB PCB ,当终止进程的工作完成之后,这个进程就彻底消失了
![进程五种状态的变迁](https://i-blog.csdnimg.cn/blog_migrate/902f0924c1d2adb303119fd16e1dcf38.png)
NULL --> 创建态:一个新进程被创建时的第一个状态
创建态 --> 就绪态:当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪态,这个过程是很快的
就绪态 --> 运行态:处于就绪态的进程被操作系统的进程调度器选中后,就分配给 CPU 正式运行该进程
运行态 --> 终止态:当进程已经运行完成或出错时,会被操作系统作结束状态处理
运行态 --> 就绪态:处于运行态的进程在运行过程中,由于分配给它的运行时间片用完或处理机被抢占,操作系统会把该进程变为就绪态,接着从就绪态选中另外一个进程运行
运行态 --> 阻塞态:当进程请求某个事件且必须等待时,例如请求 I/O 事件
阻塞态 --> 就绪态:当进程要等待的事件完成时,它从阻塞态变到就绪态
阻塞态 --> 就绪态不是进程自身能控制的,是一种被动行为
运行态 --> 阻塞态是一种进程自身做出的主动行为
注意:不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出这种请求)
进程PCB中,会有一个变量state来表示进程的当前状态
如果有大量处于阻塞状态的进程,进程可能会占用着物理内存空间,显然不是我们所希望的,毕竟物理内存空间是有限的,被阻塞状态的进程占用着物理内存就一种浪费物理内存的行为。所以,在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。
![虚拟内存管理-换入换出](https://i-blog.csdnimg.cn/blog_migrate/377782671454b5e615f13635f8e2409d.png)
那么,就需要一个新的状态,来描述进程没有占用实际的物理内存空间的情况,这个状态就是挂起状态。这跟阻塞状态是不一样,阻塞状态是等待某个事件的返回。
挂起态分为两种:
- 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行
- 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现
这两种挂起状态加上前面的五种状态,就变成了七种状态变迁
导致进程挂起的原因不只是因为进程所使用的内存空间不在物理内存,还包括如下情况:
- 通过 sleep 让进程间歇性挂起,其工作原理是设置一个定时器,到期后唤醒进程。
- 用户希望挂起一个程序的执行,比如在 Linux 中用
Ctrl+Z
挂起进程;
进程的组织——链接方式
链式方式:操作系统会管理一系列的队列,每个队列指向相应状态的进程 P C B PCB PCB ,操作系统持有指向各个队列的指针
索引方式:操作系统会给各种状态的进程创建索引表,索引表的表项会指向相应的 P C B PCB PCB ,操作系统持有指向各个索引表的指针
进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能
简单理解:进程控制就是要实现进程的状态转换
如何实现进程控制
用原语
原语:是一种特殊的程序。它的执行具有原语性。也就是说,这段程序的运行必须一气呵成,不可中断
为何进程控制要一气呵成
假设 P C B PCB PCB 中的变量 state 表示进程当前所处状态,1表示就绪态,2表示阻塞态…
如果一个进程的 state = 1的话,那么它就得挂在就绪队列里,如果 state = 2的话,那么它就得挂在阻塞队列里
假设此时处于阻塞队列的进程A等待的事情发生,则操作系统中,分则进程控制的内核程序至少需要做这两件事:
- 将进程A的 P C B PCB PCB 的 state 设为1
- 将 P C B PCB PCB 从阻塞队列放到就绪队列
假设现在 P C B 1 PCB1 PCB1 的 state 已经被设为1了,而现在突然检测到一个中短信号,既然检测到中断,那么系统就要对这个中断进行处理,而此时 P C B 1 PCB1 PCB1 的 state = 1,也就是说 P C B 1 PCB1 PCB1 的状态应该是就绪态,而 P C B 1 PCB1 PCB1 还处于阻塞队列中,所以就导致了 P C B 1 PCB1 PCB1 表示的状态和 P C B 1 PCB1 PCB1 所处的队列对不上。所以操作系统i让进程的状态转换这个过程中间处理的步骤不能一气呵成的话就有可能出现上述问题
如何实现原语的“原子性”
原语的执行具有原子性,即执行过程一气呵成,期间不允许被中断,可以用“开中断指令”和“关中断指令”这两个特权指令实现原子性
正常情况:CPU每执行完一条指令都会例行检查是否有中断信号需要处理,如果有,则暂停运行当前这段程序,转而执行相应的中断处理程序
CPU执行了关中断指令后,就不会例行检查中断信号,直到执行开中断指令后才会恢复检查。这样,关中断、开中断之间的这些指令序列就是不可被中断的,这就实现了“原子性”
进程的上下文切换
CPU中会设置很多“寄存器”,用来存放程序运行过程中所需的某些数据,总之,寄存器就是CPU中用于存数据的地方
CPU中会有各种各样的寄存器:
- PSW:程序状态字寄存器
- PC:程序计数器,存放下一条指令的地址
- IR:指令寄存器,存放当前正在执行的指令
- 通用寄存器:其他一些必要信息
当一个进程要切换时,可以把它之前**运行环境的信息(一些必要的寄存器信息)**保存在自己的 P C B PCB PCB 当中,接下来才可以切换成别的进程,当原来的进程再次投入运行时,可以通过PCB恢复它的运行环境
进程的上下文(运行环境信息):进程在运行环境当中,寄存器里存储的中间结果。
各个进程之间是共享 CPU 资源的,在不同的时候进程之间需要切换,让不同的进程可以在 CPU 执行,那么这个一个进程切换到另一个进程运行,称为进程的上下文切换。
进程是由内核管理和调度的,所以进程的切换只能发生在内核态。
所以,进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。
大家需要注意,进程的上下文开销是很关键的,我们希望它的开销越小越好,这样可以使得进程可以把更多时间花费在执行程序上,而不是耗费在上下文切换。