跳过正文

操作系统是特殊的软件程序

·169 字·1 分钟
Banson
作者
Banson

程序是什么 中,我们把软件程序简化为独占 CPU 的、有固定开始和结束时间的软件。这与你真正体验到的计算机有较大区别:

  • 真实的计算机可以根据用户的指令运行不同的程序。在 Windows 中,双击鼠标就会启动一个程序,在它运行结束后计算机并不会关闭,而是让你拥有继续运行别的程序的自由。
  • 真实的计算机可以同时运行好多程序,例如我在写下本篇文档时(Chrome 浏览器),同时在听音乐(网易云)。

要实现以上功能,单纯的一个 CPU 和一个程序是做不到的。缺失的部分就是本文介绍的对象:操作系统。你可能已经在别的地方听说过 系统 / 操作系统 / OS 这样的词,并且觉得很割裂:我们的主线章节对 CPU 的介绍中,完全没有给操作系统留位置。事实上,操作系统也只是一个软件程序而已。这个软件先于其他普通软件启动,并且具有运行和管理普通软件的功能。本文介绍操作系统的两个功能,分别对应上面两点:

  1. 加载和运行用户程序
  2. 进程管理

操作系统如何运行其他程序
#

我们在 CPU的基本原理 中已经知道,CPU 有一个指令计数器(Program counter, PC),它指向了当前执行的指令地址,所有的分支和跳转都是通过改变 PC 的值完成的。因此,“加载运行用户程序”这件事情听起来高大上,但无非就是 a. 操作系统把 PC 修改为用户程序第一条指令的地址;b. 用户程序执行完毕后,将 PC 改回跳转前的地址,即操作系统程序内的某条指令地址。

前面提到的“加载运行用户程序”的描述,是基于早期的实模式计算机而言的。在这种情况下,操作系统的工作相对简单:只需要负责将程序加载到内存的某个固定位置,并通过修改指令计数器(PC)来启动程序的运行。然而,在现代分页操作系统中,情况更加复杂:

  1. 操作系统首先为用户程序分配内存,并设置页表。 操作系统为每个用户程序创建一个独立的虚拟地址空间,并在页表中记录虚拟地址到物理地址的映射关系。
  2. 操作系统将 CPU 切换到用户态,设置指令计数器(PC)和页表基地址寄存器。 通过设置页表基地址寄存器,CPU 的内存管理单元(MMU)能够使用页表进行地址转换。当用户程序访问指令和数据时,MMU 自动将虚拟地址转换为物理地址,从而正确提取程序指令和数据。这样,用户程序可以在用户态下安全运行。
  3. 返回操作系统。 用户程序执行完毕后,通过系统调用(syscall)指令,将控制流交还给操作系统。

有关页表和虚拟内存的概念,我们已经在 操作系统内存管理 讨论过。接下来介绍上面提到的特权态 / 用户态、系统调用。

特权态与用户态
#

特权态(Privileged Mode)和用户态(User Mode)是 CPU 的两种运行模式,用于区分操作系统与用户程序的权限级别;一部分敏感指令被划分为特权指令,只能在特权态执行(例如设置页表基地址、修改中断向量,等等)。这种机制通过 CPU 中的一个标志寄存器实现,逻辑如下:

if (status_register != privileged && instruction_type == privileged) {
  refuse_to_execute
}

异常
#

异常(Exception)是 CPU 提供的硬件机制。当异常发生时,当前的执行流被打断,PC 跳转到一个提前设置好的地址(存放在一个特殊的寄存器中,我们称之为异常向量寄存器;其实叫中断向量寄存器的说法更普遍,但是容易把 Exception 和 Interrupt 概念搞混)。在这里我们只介绍 Exception 的一种:Syscall。(在本文中,Exception = {Interrupt, Trap, Fault}。Syscall 是 Trap 的一种。)

Syscall 是 CPU 提供的指令,因此是一个硬件机制。Syscall 指令可以在用户态执行,但是它的效果是切换到特权态,同时 PC 跳转到异常向量寄存器指定的地址。这个地址存放的是操作系统的程序,这样就完成了控制流从用户程序到操作系统的交接。因此,syscall 是用户和操作系统的交界面,也是软件和硬件的交界面。

CPU 上电时即处于特权态。操作系统是 CPU 上电后第一个运行的复杂程序,因此它可以设置异常向量寄存器,之后再切换到用户态运行其他程序。由于用户态无法修改异常向量寄存器,操作系统就这样保证每次 syscall 跳转到的都是自己的程序,从而确立了自己的管理地位。

操作系统如何同时执行许多程序
#

写给专业读者:这一节我们介绍时间片的概念。简化起见,假设 CPU 只支持串行。

想象一个公园门口有三个设施:门票售卖、冰激凌售卖、核酸检测。但很不巧,今天人手不足,工作人员只有一位。怎么样同时运行这三个设施呢?理所当然地,工作人员分身乏术,同一时刻只能服务一个窗口。

一个容易想到的方案是:让他在门票窗口工作一分钟,然后到冰激凌窗口工作一分钟,再到核酸检测窗口工作一分钟,如此循环往复,同时服务三列排队的游客。

操作系统正是实现了类似的方案。为了用一个 CPU 核心同时服务多个程序,引入了 分片 的概念。操作系统同时运行多个程序 $p_1, p_2, …, p_n$,当 $p_i$ 运行了一段固定时间(称为时间片)后,就切换到 $p_{i+1}$ 执行,如此循环往复。一个时间片的长度往往在几十毫秒的级别,因此在人类看来,这些程序就是同时执行的(实际上它们是以极高的频率轮流执行)。

每个运行的程序称之为一个 进程(Process)。进程从 A 切换到 B 时,有几件重要的工作要做:

  • 保存 A 的上下文,包括 CPU 的通用寄存器的值、PC 等,操作系统会把这些值从寄存器复制到属于 A 的内存数据结构中;
  • 将页表基址寄存器替换为 B 的页表基址,以便后续 B 运行时访问自己的程序和数据;
  • 恢复 B 上次切换时保存的上下文,即:将通用寄存器的值从 B 的相关内存数据结构中复制回寄存器,同时把上次保存的 PC 值复制回 PC;
  • 切换到用户态,B 的程序自然从刚才恢复的 PC 开始执行。

笼统地讲,每次进程切换都是在给进程 A 的 CPU 状态拍快照,然后把进程 B 上次拍的快照恢复到 CPU 中。

相关文章