操作系统 Focused Reading 0 Categories / 0 Tags / 3.4k Words
Operating System Note

操作系统

操作系统学习笔记

2026.08.17 Updated 2026.08.18 3.4k Words

这一页从程序如何被 CPU 执行开始,逐步进入进程、内存、并发与设备管理。理解 CPU 的工作模型,是理解操作系统其余部分的起点。

01. 当我们谈论 CPU 时,大脑里应该有什么?

CPU 是程序的执行引擎,操作系统则负责为不同程序分配 CPU、内存和设备。即使不需要设计 CPU,理解它如何读取并执行指令,仍是理解虚拟内存、进程切换和系统调用的基础。

先建立一张全景图

1
2
3
4
5
6
7
8
9
磁盘上的可执行文件
|
| 操作系统加载
v
内存中的代码与数据 <----> CPU 缓存 <----> CPU 核心
|
| 取指 -> 译码 -> 执行
v
寄存器:RIP、通用寄存器、RSP、RFLAGS、CRx

程序启动后,机器指令和数据会被加载到内存。CPU 根据当前的指令地址取出下一条指令,译码后执行,再进入下一轮。缓存会加快访问,但从程序员和操作系统的视角看,指令与数据都位于进程的虚拟地址空间中。

1. CPU 到底在做什么

CPU 的基本循环可以概括为:取指、译码、执行、更新下一条指令地址

  1. 取指:根据 RIP 指向的地址取得下一条机器指令。
  2. 译码:识别这条指令需要做什么,例如加法、比较、读写内存或跳转。
  3. 执行:由执行单元完成计算、访存或跳转,并更新寄存器和状态位。
  4. 继续执行:通常 RIP 顺序前进;遇到分支、函数调用或返回时,则跳到新的地址。

我们写的 C/C++、Rust 或 Python 源码不会被 CPU 直接执行。它们需要经过编译器、解释器或运行时,最终变成 CPU 能理解的机器指令。常见的 0x... 只是机器指令或数据的十六进制表示。

2. 寄存器:CPU 内部最快的工作区

寄存器是位于 CPU 核心内部的少量高速存储单元,用来保存正在计算的数据、地址和执行状态。以下名称以 x86-64 为例;不同架构名称不同,但角色相似。

类型 代表寄存器 在程序运行中的作用
通用寄存器 RAXRBXRCXRDX 保存整数、地址、临时变量和计算结果;按常见调用约定,RAX 常保存函数返回值。
指令指针 RIP 指向下一条将要执行的指令,是程序执行流的“当前位置”。修改它就能实现分支、循环、函数调用和返回。
栈指针 RSP 指向当前调用栈的栈顶,用于局部变量、返回地址和函数调用现场。
帧指针 RBP 常被编译器用来定位当前栈帧的基准位置;优化后的代码可能不使用它。
状态寄存器 RFLAGS 记录计算结果和控制状态,供条件跳转、调试和中断控制使用。

可以把寄存器理解为 CPU 手边的草稿纸。内存容量更大,但访问成本通常更高;CPU 会尽量把频繁使用的数据放在寄存器和缓存中。

3. RIP 如何决定程序往哪里走

RIP 常被称为指令指针。它保存的是下一条指令的地址

  • 顺序执行时,CPU 执行完一条指令后,让 RIP 前进到下一条。
  • if-else、循环等条件分支,会根据比较结果决定是否改写 RIP
  • 调用函数时,CPU 跳到函数入口;函数返回时,再回到调用点之后的地址。

因此,程序的控制流本质上就是 RIP 在不同代码地址之间移动。调试器查看调用栈、单步执行或设置断点,本质上都与控制执行流有关。

4. 状态寄存器:让 CPU 能够“根据结果做决定”

RFLAGS 中包含许多标志位。其中最常见的是:

标志位 含义 常见用途
ZF(Zero Flag) 运算结果是否为 0 判断相等、条件跳转。
CF(Carry Flag) 无符号运算是否产生进位或借位 多精度整数运算、无符号比较。
OF(Overflow Flag) 有符号运算结果是否溢出 有符号数运算检查。
TF(Trap Flag) 启用单步陷阱 调试器可以借此逐条观察程序。
IF(Interrupt Flag) 是否响应可屏蔽外部中断 影响 CPU 是否接收普通硬件中断;不影响异常和不可屏蔽中断。

例如,比较两数后,CPU 会设置相应标志位;紧随其后的条件跳转指令再读取这些标志位,从而决定是否跳转。

5. 控制寄存器:虚拟内存和权限机制的开关

控制寄存器主要由内核使用,普通用户态程序不能随意读写。仍以 x86 为例:

寄存器 核心作用
CR0 控制 CPU 的基础工作模式;其中的分页相关位参与启用分页机制。
CR2 发生页错误时,保存导致错误的线性(虚拟)地址。
CR3 保存当前地址空间顶级页表的物理地址,是虚拟地址翻译的入口。
CR4 控制 PAE 等扩展特性,并启用部分高级处理器能力。

这组寄存器把 CPU 与操作系统的内存管理紧密连接起来:进程切换时,内核可能切换 CR3,从而让同一个虚拟地址在不同进程中映射到不同的物理内存。

6. 指令从哪里来:冯·诺依曼模型

现代计算机仍可用冯·诺依曼模型来理解:输入设备提供数据,内存保存正在使用的程序和数据,CPU 负责运算与控制,输出设备呈现结果。

  • 磁盘:长期保存可执行文件和数据。
  • 操作系统与加载器:创建进程的虚拟地址空间,把程序需要的内容映射或加载进内存。
  • 内存与缓存:提供 CPU 执行期间需要访问的代码和数据;缓存由硬件自动管理,用来降低访问延迟。
  • CPU:从当前地址空间取指并执行。

所以,“程序在磁盘里”不等于“CPU 正在执行它”。只有程序被加载为进程,并获得 CPU 执行时间后,其中的指令才会真正运行。

7. 一个核心只能运行一个执行流,多任务是如何出现的

从操作系统调度的视角看,一个逻辑 CPU 在某一时刻只运行一个线程的执行上下文。时间片到期或发生中断时,内核会保存当前线程的寄存器状态,再恢复另一个线程的状态,然后从对方的 RIP 继续执行。

1
2
3
4
5
6
线程 A 运行一段时间
-> 时钟中断进入内核
-> 保存 A 的 RIP、RSP、通用寄存器等上下文
-> 调度器选择线程 B
-> 恢复 B 的上下文并返回用户态
线程 B 从上次停下的位置继续运行

这就是上下文切换。CPU 通常不会在每条指令后切换任务,而是让一个线程连续运行一小段时间。高速轮转让人感觉多个程序同时执行;真正的多核 CPU 则可以让多个逻辑 CPU 并行运行不同线程。现代处理器还有流水线、乱序执行和超线程等机制,但它们不改变操作系统需要保存与恢复执行上下文这一事实。

小结:理解 CPU 时应抓住的六件事

  1. CPU 的基本职责是不断执行机器指令。
  2. RIP 决定下一条指令在哪里,控制流就是它的跳转轨迹。
  3. 通用寄存器和 RSP 保存当前计算与调用现场。
  4. RFLAGS 记录运算和控制状态,CR0CR4 参与内存与处理器特性的控制。
  5. 程序先从磁盘进入进程的地址空间,才能被 CPU 执行。
  6. 多任务依赖调度器和上下文切换;进程、线程和虚拟内存将在后续章节展开。

学习导图

一、程序运行原理篇

  • 说到CPU时你的大脑里应该有什么?
  • 内存的本质是什么?
  • 编程语言是怎么一回事?
  • 到底该怎样理解堆区和栈区?
  • 没有操作系统程序能运行起来吗?
  • 程序是如何运行起来的?
  • 为什么用C语言编写操作系统?
  • 进程和程序之间有什么区别?

二、操作系统实现篇

  • 操作系统是如何实现的?
  • 操作系统是如何启动的?
  • CPU权限管理的王者之争
  • 到底什么是用户态、内核态?
  • 什么是系统调用?
  • 为什么需要系统调用?
  • 系统调用时操作系统中发生了什么?
  • 操作系统中的代码是如何运行的?
  • 系统调用完成后怎样返回用户态?
  • 实践篇:观察内核调用栈
  • 实践篇:用strace观察系统调用的秘密

三、进程管理篇

  • 为什么操作系统中需要进程这个概念?
  • 操作系统是怎样同时运行多个程序的?
  • 进程切换是怎样实现的?
  • 进程切换与函数调用的区别是什么?
  • 为什么进程会有各种状态信息?
  • 操作系统是怎样调度进程的?
  • 进程的生命周期
  • 进程与操作系统的CPU争夺之战
  • 假如让你来为进程创建设计API
  • 为什么Linux需要fork与exec两个系统调用来创建进程?
  • 操作系统是如何进一步创建出进程的?
  • 进程是如何退出的?
  • 进程间通信是如何实现的?
  • 有了进程为什么还需要线程?
  • 程序员该如何理解线程?
  • 线程是如何实现的?
  • 进程和线程之间到底有什么区别?
  • 有了线程为什么还需要协程?
  • 如何利用多线程来优化程序性能?
  • 实践篇:多进程编程
  • 实践篇:多进程多线程在浏览器中的应用

四、内存管理篇

  • 黑客帝国与内存背后的真相
  • 内存分配到底在解决什么问题?
  • 程序能运行起来需要分配哪些内存?
  • 操作系统如何为进程分配内存的?
  • 虚拟地址空间的诞生
  • 操作系统如何为进程分配内存之段式内存管理
  • 操作系统如何为进程分配内存之页式内存管理
  • 页式内存管理会有哪些问题?
  • 操作系统如何加速页表的读取?
  • 操作系统如何解决页表内存占用过多的问题?
  • 为什么虚拟内存能节省内存?
  • 为什么物理内存只有1G程序员却可以申请出4G内存?
  • 上帝视角:操作系统是如何管理内存的?
  • 实践篇:如何利用内存池优化程序性能?
  • 实践篇:申请4G内存实际会消耗多少物理内存?
  • 实践篇:进程在内存中是什么样子的?

五、中断管理篇

  • 到底什么是中断?
  • 操作系统在什么情况下开始运行?
  • 操作系统是如何处理中断的?
  • 程序是如何读取到网络数据的?
  • 程序是如何读取到磁盘数据的?

六、并发篇

  • 并发问题的根源是什么?
  • 实践篇:实际动手感受一下并发问题
  • 怎样解决并发问题?
  • 一山不容二虎:锁是如何实现的?
  • 有自旋锁为什么还需要互斥锁?
  • 闻风而动:如何实现线程的同步等待机制?
  • 到底什么是信号量?
  • 信号量是怎么实现的?
  • 实践篇:用信号量来解决互斥问题
  • 实践篇:用信号量来解决同步等待问题
  • 实践篇:生产者消费者问题

七、设备管理篇

  • 一切皆为文件到底是什么意思?
  • 操作系统是如何管理设备的?
  • 磁盘是如何工作的?
  • 文件这个概念在操作系统是如何实现的?
  • 文件系统是如何实现的?
  • 文件夹在操作系统中是如何实现的?
  • 操作系统是如何打开、读写以及创建文件的?
  • 操作系统是如何加速文件读写的?