进程概念
-
进程是能分配给处理器并由处理器执行的实体,由一组执行的指令、一个当前状态和一组相关系统资源所表示的活动单元,
-
其两个基本元素是程序代码和与代码关联的数据集。
-
PCB 表征进程执行的任意时刻的所有信息:
- PID:
- State:
- Priority
- Program counter:
- Memory pointers:
- Content data:
- I/O status info:
- Accounting info:
进程状态
进程状态模型
两状态模型 (running or not)
五状态模型 (ready? running:blocked)
六状态模型 (add suspend)
七状态模型 (suspend read/suspend wait)
进程描述
OS控制结构
- 内存管理——内存表
- 设备管理——I/O 表
- 文件管理——文件表
- 进程管理——基本进程表——进程映像
进程控制结构
- 进程的物理表示——进程映像:程序、数据、栈、属性集;
- 虚存分为内核空间和用户程序空间;
- 内核空间中为每个进程维护独特的数据结构,主要是页表、内核栈等;
- 用户空间中可以为进程在运行时动态分配空间、有栈维护函数调用……
- OS 想要管理好进程,需要知道进程的位置和属性信息:
- 进程位置取决于内存管理方案;
- 进程属性由 PCB 管理,分为标识信息、处理器状态信息、进程控制信息;
- 进程控制块包含 OS 所需的进程所有信息;
进程控制块的保护问题
-
PCB 有两个维护问题:
- 例程(如中断处理例程)中的错误可能会改变 PCB,进而破坏系统对受影响进程的管理能力;
- PCB 结构或语义中的设计变化可能会影响到 OS 中的许多模块;
-
解决思路:
- OS 中所有例程都通过一个处理例程来解决,即处理例程的任务只是保护 PCB,且是读写 PCB 的唯一仲裁程序;
进程控制
执行模式
-
两种执行模式:
- 与操作系统关联的处理器执行模式:特权模式(或称,控制模式、内核模式、系统模式);
- 与用户程序关联的处理器执行模式:非特权模式;
-
处理器怎么知道正在什么模式运行?
- PSW 状态字中存在一个指示执行模式的位
- 用户调用 OS 服务或中断进而触发系统例程执行时,置为内核模式;
- 当系统服务返回用户进程时,置为用户模式。
-
如何设置模式?(以 IA-64 结构、Linux 系统为例)
- 处理器中 PSR (Processor Status Register)有一个 2 bit 字段 CPL,置为 0 时是最高特权级,3 是最低特权级;
- 中断发生时,CPL 置零;
- 中断处理程序的最后一个指令会使处理器在返回时恢复中断程序的 PSR。
进程创建
- 分配唯一的进程标识符 PID;
- 为进程分配空间:包含进程映像中所有元素;
- 初始化进程控制块:
- 处理进程标识 ID 和其它相关 ID(父进程 PID、用户UID);
- 初始化处理器状态信息;
- 初始化进程控制信息;
- 优先级默认最低,除非显式请求;
- 初创时不拥有任何资源;
- 添加到对应状态的调度队列:将调度队列维护为链表,新进程放在就绪、就绪/挂起链表中;
- 维护其它数据结构;
进程切换
-
什么事件触发了进程切换?
- 中断
- 时钟中断
- IO 中断
- 内存失效——缺页中断
- 陷阱
- 运算除 0
- 用户设置的异常检查
- 系统调用
- IO 请求
- 中断
-
模式切换和进程切换有什么区别?
- 模式切换指的是处理器运行指令的权限是否在用户态和内核态切换;
- 模式切换可在不改变运行态进程的状态时出现,此时保存上下文及恢复的开销很小;
- 进程切换指当前进程是否运行结束、时间片耗尽等,需要下一个就绪态进程占据 CPU。
- 模式切换指的是处理器运行指令的权限是否在用户态和内核态切换;
-
保存的上下文有什么?
- 中断处理程序可能改变的所有信息,如 PSW;
- 恢复被中断程序时需要的所有信息,如 PC;
-
==保存和恢复上下文都由硬件实现==
-
出现中断时,处理器会进行什么工作?——模式切换
- PC 置于中断处理程序开始处;
- 用户模式切换到内核模式,以便中断处理代码直接运行特权指令
- 处理器处于中断处理程序第一条指令的取指阶段,在此之前保存被中断进程的上下文到其 PCB 中
完整的进程切换步骤
- 保存处理器上下文;
- 更新当前处于运行态进程的进程控制块,包括进程状态、退出运行态原因、记账信息等;
- 将该进程的 PCB 移动到相应队列(就绪、阻塞、就绪/挂起);
- 选择另一个就绪态进程开始执行,更新对应 PCB,如运行状态信息;
- 更新内存管理数据结构,取决于管理地址转换的方式;
- 载入 PC 和其它寄存器先前的值,恢复上下文到切换前;
要实现进程切换,操作系统对控制的数据结构做什么改动? 上一问题的 2、3、4、5
OS 执行的设计方法
- OS 与普通计算机软件同样运行在处理器上,也是一个程序;
- OS 会频繁地释放控制权,并依赖于处理器来恢复控制权;
无进程内核
内核在所有进程的外部执行,二者分离,如图 a
-
运行中进程遇到中断或 syscall,会保存上下文并转移控制权到内核;
-
OS 本身具有控制过程调用和返回的内存区域与系统栈;
-
OS 可以执行任何预期的功能,并恢复被中断进程的上下文,恢复中断用户进程的执行;
-
OS 也可以保存进程的模式上下文,并继续调度和分派另一个进程,这取决于中断的原因和当前的情况;
-
此方案中,进程只适用于用户程序,OS 代码是在特权模式下单独运行的实体。
在用户进程内执行
-
在用户进程的上下文中执行所有 OS 软件,此时 OS 是用户调用的一组例程,在用户进程的环境内执行并实现各种功能,如图 b
-
任何时刻 OS 都管理着 n 个进程映像,这里的进程映像包括内核程序的代码、数据、栈区域:
-
发生中断、陷阱或系统调用时,
- 该方案中处理器置于内核模式,控制权交给 OS,此前保存了模式上下文,并切换到 OS 例程。
- 注意,此时仍然处于当前用户进程内,只是切换了模式,而不是切换了进程。
- OS 执行完操作后,切换模式以在当前进程内恢复中断前的流程。
-
该方案最大的优点是,
- ==不论中断还是恢复,都不会产生进程切换的巨大开销==。如要进行进程切换,则要调用进程切换例程作专门处理。
基于进程的 OS
-
第三种方案是将 OS 作为一组系统例程实现,内核功能被组织成独立的进程,如图 c
-
优点:
- ==鼓励使用模块化 OS 的程序设计原理,精简了模块间接口==;
- 对非关键 OS 功能使用独立进程实现,比如用以提供资源利用率的监视,因而适合微内核结构;
- 最后在多处理器和分布式环境中性能较好。
Unix SVR4 的进程管理
进程状态转换(9 状态)
进程创建 fork
-
fork 请求发出时,系统执行如下操作(所有操作都在父进程的内核模式下完成):
- 在进程表中为新进程分配一个空项;
- 为子进程分配一个唯一进程标识符;
- 复制父进程的进程映像,但共享内存除外;
- 增加父进程所拥有文件的计数器,反应另一个进程也拥有这些文件的事实;
- 将子进程置为就绪态;
- 将子进程的 ID 返回给父进程,将 0 值 返回给子进程;
-
接下来内核可以选择的去向是(三选一):
- ==停留在父进程中==,控制权返回到用户模式下父进程调用 fork 的位置;
- ==处理器控制权交给子进程==,子进程开始执行,执行点与父进程相同,都是 fork 调用开始处;
- ==控制权转交给另一个进程==,原父进程、子进程都处于就绪态;
线程
-
引入线程前,进程拥有资源所有、调度执行之权:
- 资源所有权:对存放进程映像的虚拟地址空间有控制、所有权;
- 调度执行权:多进程的执行过程会交替进行,进程是被操作系统调度和分派的实体
-
引入线程,就是为了拆分调度执行权,更加细分地利用进程申请的资源,提高效率。
单线程 vs. 多线程
-
多线程环境中,进程成为了资源分配单元和保护单元,其包括:
- 容纳进程映像的虚拟地址空间;
- 对处理器、其它进程、文件和 IO 资源的受保护访问;
-
进程中包含若干线程,每个线程包括:
- 线程运行状态;(TCB)
- 未运行时保存的线程上下文,线程视为进程内运行的独立程序计数器;
- 一个执行栈;
- 每个线程用于保存局部变量的静态存储空间;
- 与进程内其它线程共享的内存和资源访问;
-
线程优点:
- 在已有进程中创建一个新线程的时间,远少于创建一个全新进程的时间;
- 终止线程花费时间更少;
- 同一进程内切换线程时间,比切换进程少得多;
- 提高了不同执行程序间通信的效率,==同一进程间多个线程共享内存和文件,无须调用内核就可以互相通信==。
-
单用户多处理器系统中使用线程的例子:
- 前台与后台工作:如程序在前一条命令完成前提示输入下一条命令;
- 异步处理
- 模块化程序结构
- 使 IO 与处理器并行
线程功能
线程状态
-
线程执行状态:
- 运行态
- 就绪态
- 阻塞态
-
与线程状态改变相关的基本操作:
- 派生:新线程有自己的寄存器上下文和栈空间;
- 阻塞
- 解除阻塞
- 结束
一个线程被阻塞,是否会导致整个进程被阻塞?——后文 ULT 与 KLT 会作解释。
线程同步
- 同一进程内的线程共享资源,因此需要同步线程的活动,以便互不干扰且不破坏数据结构。
线程分类
用户级线程
- 方案描述:
- 管理线程的所有工作都由用户空间的应用程序完成,==线程对内核透明==;
- 线程库是管理 ULT 的例程包,提供创建销毁线程、线程通信、线程调度、线程保存与恢复上下文的功能;
- 应用程序通常以单个线程开始,后续其它线程通过调用线程库派生获得;
-
ULT 的优点:
- 所有线程管理数据结构都在一个进程的用户地址空间中,切换线程不需要内核模式特权,==节省了两次模式切换的开销==;
- 线程调度策略==可以根据用户程序需要量身定做==,而不必扰乱底层 OS 的调度程序;
- ULT ==由高级编程语言直接提供的线程库进行操控==,可移植性强,也不必对底层内核进行修改;
-
ULT 的缺点:
- ==一个线程发生阻塞时,会同时阻塞进程中其它所有线程==(如上图 b)
- 多线程应用==无法利用多处理器技术的性能==,一个进程中只有一个线程可以获取处理器,即便在进程内实现多道程序设计;
-
解决方案:
- (不可取)将应用程序由多线程改写为多进程;
- 套管 jacketing,将产生阻塞的系统调用转化为一个非阻塞的系统调用。
- 如,替代直接调用系统级 IO 的例程,而是调用应用级 IO 套管例程,该例程检查 IO 设备是否忙,若忙阻塞请求线程并把控制权转移;
内核级线程
-
方案描述:
- ==应用级没有线程管理代码==,只有内核线程开放的 API;
- 内核为进程及其每个线程维护上下文,==调度由内核基于线程完成==;
-
KLT 如何克服 ULT 的缺点?
- KLT 中内核可以将同一进程的==多个线程调度到多个处理器==中,
- 并且进程的的一个线程阻塞时,可以调度其它线程运行;
-
KLT 的缺点:
- 线程切换也要涉及内核特权级的切换,模式切换会带来巨大开销;
混合 ULT 与 KLT
- 线程创建、调度、同步都在用户空间进行,只是将需要系统调用的 ULT 映射到 KLT 上;
- 同一应用程序中的多个线程可在多个处理器上并行执行;
进程与线程间数量关系
多核与多线程性能
- Amdahl 定律:
表示可并行的代码占比。
- 即使很小的串行代码比,也会显著降低多核处理器下的性能表现;
- 处理器越多,耗费在调度、通信、缓存上的开销就越多,性能反而下降;