进程概念

  • 进程是能分配给处理器并由处理器执行的实体,由一组执行的指令、一个当前状态和一组相关系统资源所表示的活动单元,

  • 其两个基本元素是程序代码和与代码关联的数据集。

  • 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 有两个维护问题:

    1. 例程(如中断处理例程)中的错误可能会改变 PCB,进而破坏系统对受影响进程的管理能力;
    2. PCB 结构或语义中的设计变化可能会影响到 OS 中的许多模块;
  • 解决思路:

    • OS 中所有例程都通过一个处理例程来解决,即处理例程的任务只是保护 PCB,且是读写 PCB 的唯一仲裁程序

进程控制

执行模式

  • 两种执行模式

    • 与操作系统关联的处理器执行模式:特权模式(或称,控制模式、内核模式、系统模式);
    • 与用户程序关联的处理器执行模式:非特权模式
  • 处理器怎么知道正在什么模式运行

    • PSW 状态字中存在一个指示执行模式的位
    • 用户调用 OS 服务或中断进而触发系统例程执行时,置为内核模式;
    • 当系统服务返回用户进程时,置为用户模式。
  • 如何设置模式?(以 IA-64 结构、Linux 系统为例)

    • 处理器中 PSR (Processor Status Register)有一个 2 bit 字段 CPL,置为 0 时是最高特权级,3 是最低特权级
    • 中断发生时,CPL 置零;
    • 中断处理程序的最后一个指令会使处理器在返回时恢复中断程序的 PSR。

进程创建

  1. 分配唯一的进程标识符 PID;
  2. 为进程分配空间:包含进程映像中所有元素;
  3. 初始化进程控制块
    • 处理进程标识 ID 和其它相关 ID(父进程 PID、用户UID);
    • 初始化处理器状态信息;
    • 初始化进程控制信息;
    • 优先级默认最低,除非显式请求;
    • 初创时不拥有任何资源;
  4. 添加到对应状态的调度队列:将调度队列维护为链表,新进程放在就绪、就绪/挂起链表中;
  5. 维护其它数据结构;

进程切换

  • 什么事件触发了进程切换

    1. 中断
      • 时钟中断
      • IO 中断
      • 内存失效——缺页中断
    2. 陷阱
      • 运算除 0
      • 用户设置的异常检查
    3. 系统调用
      • IO 请求
  • 模式切换和进程切换有什么区别

    • 模式切换指的是处理器运行指令的权限是否在用户态和内核态切换;
      • 模式切换可在不改变运行态进程的状态时出现,此时保存上下文及恢复的开销很小;
    • 进程切换指当前进程是否运行结束、时间片耗尽等,需要下一个就绪态进程占据 CPU。
  • 保存的上下文有什么

    • 中断处理程序可能改变的所有信息,如 PSW;
    • 恢复被中断程序时需要的所有信息,如 PC;
  • ==保存和恢复上下文都由硬件实现==

  • 出现中断时,处理器会进行什么工作?——模式切换

    1. PC 置于中断处理程序开始处;
    2. 用户模式切换到内核模式,以便中断处理代码直接运行特权指令
    3. 处理器处于中断处理程序第一条指令的取指阶段,在此之前保存被中断进程的上下文到其 PCB 中

完整的进程切换步骤

  1. 保存处理器上下文;
  2. 更新当前处于运行态进程的进程控制块,包括进程状态、退出运行态原因、记账信息等;
  3. 将该进程的 PCB 移动到相应队列(就绪、阻塞、就绪/挂起);
  4. 选择另一个就绪态进程开始执行,更新对应 PCB,如运行状态信息;
  5. 更新内存管理数据结构,取决于管理地址转换的方式;
  6. 载入 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 请求发出时,系统执行如下操作(所有操作都在父进程的内核模式下完成):

    1. 在进程表中为新进程分配一个空项;
    2. 为子进程分配一个唯一进程标识符;
    3. 复制父进程的进程映像,但共享内存除外;
    4. 增加父进程所拥有文件的计数器,反应另一个进程也拥有这些文件的事实;
    5. 将子进程置为就绪态;
    6. 将子进程的 ID 返回给父进程,将 0 值 返回给子进程;
  • 接下来内核可以选择的去向是(三选一):

    • ==停留在父进程中==,控制权返回到用户模式下父进程调用 fork 的位置;
    • ==处理器控制权交给子进程==,子进程开始执行,执行点与父进程相同,都是 fork 调用开始处;
    • ==控制权转交给另一个进程==,原父进程、子进程都处于就绪态;

线程

  • 引入线程前,进程拥有资源所有、调度执行之权

    • 资源所有权:对存放进程映像的虚拟地址空间有控制、所有权;
    • 调度执行权:多进程的执行过程会交替进行,进程是被操作系统调度和分派的实体
  • 引入线程,就是为了拆分调度执行权,更加细分地利用进程申请的资源,提高效率。

单线程 vs. 多线程

  • 多线程环境中,进程成为了资源分配单元和保护单元,其包括:

    1. 容纳进程映像的虚拟地址空间;
    2. 对处理器、其它进程、文件和 IO 资源的受保护访问;
  • 进程中包含若干线程,每个线程包括

    1. 线程运行状态;(TCB)
    2. 未运行时保存的线程上下文,线程视为进程内运行的独立程序计数器;
    3. 一个执行栈;
    4. 每个线程用于保存局部变量的静态存储空间;
    5. 与进程内其它线程共享的内存和资源访问;

  • 线程优点

    1. 在已有进程中创建一个新线程的时间,远少于创建一个全新进程的时间;
    2. 终止线程花费时间更少;
    3. 同一进程内切换线程时间,比切换进程少得多;
    4. 提高了不同执行程序间通信的效率,==同一进程间多个线程共享内存和文件,无须调用内核就可以互相通信==。
  • 单用户多处理器系统中使用线程的例子

    1. 前台与后台工作:如程序在前一条命令完成前提示输入下一条命令;
    2. 异步处理
    3. 模块化程序结构
    4. 使 IO 与处理器并行

线程功能

线程状态

  • 线程执行状态

    • 运行态
    • 就绪态
    • 阻塞态
  • 与线程状态改变相关的基本操作

    • 派生:新线程有自己的寄存器上下文和栈空间;
    • 阻塞
    • 解除阻塞
    • 结束

一个线程被阻塞,是否会导致整个进程被阻塞?——后文 ULT 与 KLT 会作解释。

线程同步

  • 同一进程内的线程共享资源,因此需要同步线程的活动,以便互不干扰且不破坏数据结构。

线程分类

用户级线程

  • 方案描述:
    • 管理线程的所有工作都由用户空间的应用程序完成,==线程对内核透明==;
    • 线程库是管理 ULT 的例程包,提供创建销毁线程、线程通信、线程调度、线程保存与恢复上下文的功能;
    • 应用程序通常以单个线程开始,后续其它线程通过调用线程库派生获得;

  • ULT 的优点

    1. 所有线程管理数据结构都在一个进程的用户地址空间中,切换线程不需要内核模式特权,==节省了两次模式切换的开销==;
    2. 线程调度策略==可以根据用户程序需要量身定做==,而不必扰乱底层 OS 的调度程序;
    3. ULT ==由高级编程语言直接提供的线程库进行操控==,可移植性强,也不必对底层内核进行修改;
  • ULT 的缺点

    1. ==一个线程发生阻塞时,会同时阻塞进程中其它所有线程==(如上图 b)
    2. 多线程应用==无法利用多处理器技术的性能==,一个进程中只有一个线程可以获取处理器,即便在进程内实现多道程序设计;
  • 解决方案

    1. (不可取)将应用程序由多线程改写为多进程;
    2. 套管 jacketing,将产生阻塞的系统调用转化为一个非阻塞的系统调用。
      • 如,替代直接调用系统级 IO 的例程,而是调用应用级 IO 套管例程,该例程检查 IO 设备是否忙,若忙阻塞请求线程并把控制权转移;

内核级线程

  • 方案描述:

    • ==应用级没有线程管理代码==,只有内核线程开放的 API;
    • 内核为进程及其每个线程维护上下文,==调度由内核基于线程完成==;
  • KLT 如何克服 ULT 的缺点

    • KLT 中内核可以将同一进程的==多个线程调度到多个处理器==中,
    • 并且进程的的一个线程阻塞时,可以调度其它线程运行
  • KLT 的缺点

    • 线程切换也要涉及内核特权级的切换,模式切换会带来巨大开销;

混合 ULT 与 KLT

  • 线程创建、调度、同步都在用户空间进行,只是将需要系统调用的 ULT 映射到 KLT 上;
  • 同一应用程序中的多个线程可在多个处理器上并行执行;

进程与线程间数量关系

多核与多线程性能

  • Amdahl 定律

表示可并行的代码占比。

  • 即使很小的串行代码比,也会显著降低多核处理器下的性能表现;
  • 处理器越多,耗费在调度、通信、缓存上的开销就越多,性能反而下降;