模型是指对于某个实际问题或客观事物、规律进行抽象后的一种形式化表达方式。- MBA 智库百科 - 模型

概念梳理

进程

  • 进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的独立单位。
  • 一个进程是某种类型活动的一个活动,它有程序、输入、输出以及状态。进程是对正在运行程序的一个抽象。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。一个程序可对应多个进程。
  • 系统资源以进程为单位分配,如内存、文件、每个进程拥有独立的地址空间。

进程表

进程表(process table),也称进程控制块(PCB),是由操作系统维护的,每个进程占用其中一个表项。该表项包含了操作系统对进程进行描述和控制的全部信息,从而保证该进程换出后再次启动时,就像从未中断过一样。

典型进程表表项的一些字段

segment

段定义( segment ) 是用来区分或者划分范围区域的意思。汇编语言的 segment 伪指令表示段定义的起始,ends 伪指令表示段定义的结束。段定义是一段连续的内存空间。

进程控制原语

原语

原语:完成某种特定功能的一段程序,具有不可分割性或不可中断性。即原语的执行必须是连续的,在执行过程中不允许被中断。也就是满足原子性

原子性操作,一组相关操作要么全部执行不可中断,要么就不执行。

  • 进程创建原语
  • 进程撤消原语
  • 阻塞原语
  • 唤醒原语
  • 挂起原语
  • 激活原语
  • 改变进程优先级

上下文切换

从一个进程切换到另一个进程需要一定的时间进行管理处理,包括保存寄存器的值和内存映射,更新不同的表格和列表、清除和重新调入内存高速缓存等。

进程间切换(process switch)又称上下文切换(context switch),它是一个存储和重建 CPU 状态的机制,要交换 CPU 上的进程时,必需先行存储当前进程的状态,然后再将进程状态读回 CPU 中。进程不运行时,将寄存器的值保存在进程控制块PCB中;当操作系统要运行一个新的进程时,会将PCB中的相关值送到对应的寄存器中。

地址空间

地址空间是内存中可供程序或进程使用的有效地址范围。也就是说,它是程序或进程可以访问的内存。存储器可以是物理的,也可以是虚拟的,用于执行指令和存储数据。

线程

线程,是进程中的一个运行实体,是 CPU 调度的基本单位。线程的下文环境是程序计数器等寄存器。线程拥有自己独立的栈和共享的堆。

线程引入解决的问题

  • 降低上下文切换开销,线程的创建、撤销花费时间少于进程
  • 多线程充分利用多核 CPU 的计算能力「内核线程」,在线程粒度进行阻塞而不是进程
  • 降低通信难度,同一进程内的线程共享内存和文件

进程和线程各自持有的资源

进程和线程各自持有的资源

多道程序设计中的进程模型

多道程序设计:允许多个程序同时进入内存并运行,其目的是为了提高系统效率。

多道程序设计中的进程模型

图例「来自《现代操作系统》」中多道程序设计计算机的内存中有 4 道程序,这 4 道程序被抽象为各自拥有自己控制流程的进程(每道程序都拥有自己的逻辑计数器)。图 c ,在任何一给定瞬间仅有一个进程真正在运行,进程运行时会将自己的逻辑程序计数器装入实际的物理程序计数器中,进程结束或暂停执行时,物理程序计数器将被保存在内存中该进程的逻辑程序计数器中。

常见线程模型

用户线程和内核线程之分

  • 用户线程,是完全建立在用户空间的线程库,用户线程的创建、调度、同步和销毁全由库函数在用户空间完成,不需要内核的帮助。因此这种线程是极其低消耗和高效的。但同一进程下创建的用户线程对 CPU 的竞争是以进程的维度参与的,这会导致该进程下的用户线程只能分时复用所属进程被分配的 CPU 时间片,所以无法很好的利用 CPU 多核运算的优势。一般情况下说的线程指的是用户线程。
  • 内核线程,由操作系统进行管理和调度,能够直接操作计算机的底层资源,线程切换时 CPU 需要切换到内核态。内核级线程可以很好利用多核 CPU 的并行计算额优势,开发人员可以通过系统调用的方式使用内核线程。
  • 用户空间管理线程,整个线程包放在用户空间,内核对线程包一无所知,内核管理的还是进程
    • 每个线程有用自己的线程表,线程在一个运行时系统上执行(运行时系统时一个管理线程的过程的集合,常用过程有 pthread_create、pthread_yield等)
    • 优点:
      • 保存线程状态的过程和调度程序都只是本地过程,效率比进行内核调用高
      • 线程切换不需要内核特权,线程调度非常快捷,调度算法可由应用程序特定
    • 缺点
      • 大多数系统调用都是阻塞的,我们需要避免被阻塞线程影响其他线程,此时阻塞系统调用如何实现?
      • 内核只将处理器分配给进程,同一进程中的两个线程不能同时运行于两个处理器上

用户空间管理线程

  • 在内核空间管理线程
    • 内核空间使用线程表记录所有的线程,线程的更新、线程创建、销毁通过系统调用实现
    • 线程阻塞时,内核可选择运行处于就绪态的线程(同一个进程中的线程或其它进程中的线程)
    • 问题:
      • 多线程进程创建新的进程时会遇到到底是拥有与原进程相同数量的线程还是一个线程的问题
      • 如果线程的创建、终止操作较为频繁,系统调用的开销还是不可忽视的

在内核空间实现线程

线程模型

混合模型,即内核线程与用户线程间多路复用。

一个进程对应一个内核线程

一个进程对应一个内核线程

此模型下(用户级线程模型),线程的创建、切换和同步等工作较为轻量与高效,依赖于编程语言实现。但同进程内的多线程无法很好的利用多核 CPU 优势,只能通过分时复用方式轮换执行。当进程内的任意线程阻塞,比如线程 A 请求 I/O 操作被阻塞,很可能导致整个进程范围内的阻塞,这是因为进程对应的内核线程因进程内的线程被阻塞而被剥夺 CPU 执行时间,导致整个进程丢失在 CPU 执行指令的机会。

进程中的每个线程都对应一个内核线程

进程中的每个线程都对应一个内核线程

此模型下(内核级线程模型),线程的调度和管理由操作系统内核负责,每次上下文切换都会从用户态切换到内核态,会产生不小的资源消耗,用户空间创建的线程数量受限于操作系统内核可创建内核线程的数量。但此模型下,多线程可充分利用多核 CPU 的并行计算能力,因为每个线程可以独立地被操作系统调度分配到 CPU 上执行指令,某个线程的阻塞不会影响到进程内其他线程工作的执行。

两级线程模型

此模型为用户级线程模型与内核级线程模型的混合,实现多个用户级线程与多个内核级线程的多路复用。此模型下,一个进程对应多个内核线程,有进程内的调度器决定进程内的线程如何与申请的内核线程对应。

两级线程模型

此线程模型可有效降低线程创建和管理的资源消耗,同时提供良好的并行能力。用户线程的调度和管理又进程的调度器负责,内核线程的调度和管理由操作系统负责。但此模型的线程的上下文信息的保存和恢复,栈空间的大小管理给开发人员带来的较大的技术挑战。

Go 中的 MPG 线程模型

GO 语言的 MPG 线程模型基于两级线程模型进行了改造,提高了线程调度的灵活度。如图:

MPG 线程模型

MPG 解释

  • M(Machine):一个 Machine 对应一个内核级线程,在 M 的生命周期内,它只会与一个内核线程进行绑定。
  • P(Processor):一个 Processor 表示执行 Go 代码片段所需的上下文环境,在运行时一个 M 只能绑定一个 P,M 和 P 的组合为 G 提供运行环境。在单个 Go 语言进程中,P 的最大数量决定了程序的并发规模。
  • G(Goroutine):Go 语言代码片段的封装(通常为一个方法,函数是 Go 的一等公民),一个待执行的任务,Go 协程。

协程(coroutine),一种用户态轻量级线程,不同于之前所说的线程,协程实现的是非抢占式调度(即由当前协程切换到其他协程由当前协程决定),协程是语言级别的。「协作式调度」

在实际运行过程中,M 和 P 的组合为 G 提供有效的运行环境,而多个可执行 G 将会顺序排成一个队列挂在某个 P 上面,等待调度和执行。M 和 P 会适时的组合和断开,以保证执行 G 队列能够得到及时运行。如下图:

Goroutine 阻塞

上图右半部分,当 M 对应的内核线程被唤醒时,M 会尝试为 G0 捕获一个 P 上下文,可能是从空闲的 P 列表中获取,如果获取不成功,M 会把 G0 放入调度器的可执行 G 队列中,等待其他 P 的查找。

参考

评论