操作系统:期末复习
Last updated on December 12, 2025 pm
本文为 SJTU-CS3601 操作系统课程的期末复习,主要复习内容为操作系统概述、内存管理、进程管理、文件管理。
Lecture 1: 操作系统的演化
- 操作系统的功能:
- 从硬件角度:管理硬件、对硬件进行抽象
- 从应用角度:服务应用、管理应用
操作系统内核架构
-
宏内核:内核所有模块运行在内核态,具备直接操作硬件的能力
- 优点:
- 拥有丰富的沉淀和积累
- 内核各模块间可以相互调用,性能高
- 缺点:
- 安全性与可靠性问题:模块之间没有很强的隔离机制
- 实时性支持:系统太复杂导致无法做最坏情况时延分析
- 系统过于庞大而阻碍创新
- 向上向下的扩展:很难去剪裁/扩展一个宏内核系统支持从 KB 级别到 TB 级别的场景
- 硬件异构性:很难长期支持一些定制化的方式去解决一些特定问题
- 功能安全:无法通过汽车安全完整性认证
- 信息安全:单点错误会导致整个系统出错
- 确定性时延:目前依然不确定是否能支持确定性时延
- 优点:
-
微内核:
- 设计原则:最小化内核功能
- 将内核功能拆分到用户态,成为独立的“服务”
- 内核仅保留极少功能,为这些服务提供通信
- 优点:
- 易于扩展:直接添加一个用户进程即可为操作系统增加服务
- 易于移植:大部分模块与底层硬件无关
- 更加可靠:在内核模式运行的代码量大大减少
- 更加安全:即使存在漏洞,服务与服务之间存在进程粒度隔离
- 更加健壮:单个模块出现问题不会影响到系统整体
- 缺点:
- 性能较差:内核中的模块交互由函数调用变成了进程间通信
- 生态欠缺:尚未形成像 Linux 一样具有广泛开发者的社区
- 重用问题:重用宏内核操作系统提供兼容性,带来新问题
- 设计原则:最小化内核功能
-
外核:不提供硬件抽象;不管理资源,只管理应用
- 应当由应用来尽可能地控制对硬件资源的抽象
- 将对硬件的抽象以库(LibOS)的形式提供,不同应用可使用不同的 LibOS
- 优点:
- OS 无抽象,能在理论上提供最优性能
- 应用对计算有更精确的实时等控制
- LibOS 在用户态更易调试,调试周期更短
- 缺点:
- 对计算资源的利用效率主要由应用决定
- 定制化过多,导致维护难度增加
-
多内核:通过多内核来管理异构多核设备
- 思路:
- 默认的状态是划分而不是共享
- 维持多份状态的 copy 而不是共享一份状态
- 显式的核间通信机制
- 设计:
- 在每个 core 上运行一个小内核
- OS 整体是一个分布式系统
- 应用程序依然运行在 OS 之上
- 思路:
Lecture 2: ARM 汇编基础
- 寄存器:
x1-x30是 31 个 64 位寄存器,w0-w30是其低位的 31 个 32 位寄存器
基础指令
- 寄存器之间的数据搬移:
mov dst, srcsrc:立即数或寄存器dst:寄存器
- 算术指令:

- 移位指令:

- 逻辑运算指令:

-
修改过的寄存器:对操作数进行移位或位扩展
- 无符号扩展:uxtb, uxth, uxtw (Zero-extend single-word / half-word / byte)
- 符号扩展:sxtb, sxth, sxtw (Sign-extend single-word / half-word / byte)
-
访存指令:


- 寻址模式:


分支指令
-
条件码:包含 N(Negative)、Z(Zero)、C(Carry)、V(Overflow)
- 设置:
- 带有 s 后缀的算术或逻辑运算指令(如
subs、adds) - 比较指令:
cmp(操作数之差)、cmn(操作数之和)、tst(操作数相与)cmp src1, src2:计算src1 - src2
- 带有 s 后缀的算术或逻辑运算指令(如
- 设置:
-
跳转条件:

-
跳转指令:
- 直接分支指令:以标签对应的地址作为跳转目标
- 无条件分支指令:
b \<label> - 有条件分支指令:
bcond \<label>,如beq、bne、ble
- 无条件分支指令:
- 间接分支指令:以寄存器中的地址作为跳转目标
br reg,如br x0
- 直接分支指令:以标签对应的地址作为跳转目标
-
for 循环翻译实例:

- while 循环翻译方法及实例:



函数调用
- 函数调用指令:
bl label(直接调用,调用函数)blr Rn(间接调用,调用函数指针)- 功能:将返回地址存储在链接寄存器 LR (即
x30寄存器),并跳转到被调用者的入口地址
- 函数返回指令:
ret(不区分直接调用与间接调用)- 功能:跳转到链接寄存器 LR 中的返回地址
- SP(Stack Pointer)寄存器:栈指针,指向栈顶(低地址)
- FP (Frame Pointer) 寄存器:帧指针,即
x29寄存器 - 标准的函数首尾操作:
1 | |
- 参数传递:
- 调用者使用
x0-x7寄存器传递前 8 个参数 - 第 8 个之后的参数,按声明顺序从右到左压到栈上,被调用者通过 SP + 偏移量访问
- 被调用者使用
x0寄存器传递返回值
- 调用者使用

- 寄存器保存:
- 调用者保存:
x9-x15- 调用者:在调用前按需进行保存,在返回后进行恢复
- 被调用者:可以随意使用
- 被调用者保存:
x19-x28- 被调用者:在使用前进行保存,在返回前进行恢复
- 调用者:这些寄存器的值在函数调用前后不会改变
- 调用者保存:

- 函数调用实例:

Lecture 3: ARM 汇编 - 系统 ISA
- 常见寄存器:

-
系统指令:
mrs/msr:从系统寄存器读取值/向系统寄存器写入值svc/eret:特权级切换和返回
-
特权级切换的时机:
- 同步异常:执行当前指令触发异常
- 第一类:用户程序主动发起系统调用(
svc指令) - 第二类:非主动,如用户程序意外访问空指针
- 第一类:用户程序主动发起系统调用(
- 异步异常:CPU 收到中断信号
- 从外设发来的中断,如屏幕点击、鼠标、收到网络包
- CPU 时钟中断,如定时器超时
- 同步异常:执行当前指令触发异常
特权级切换过程

-
处理器在切换过程中的任务:
- 将发生异常事件的指令地址保存在 ELR_EL1 中
- 将异常事件的原因保存在 ESR_EL1
- 将处理器的当前状态(即 PSTATE)保存在 SPSR_EL1
- 栈寄存器不再使用 SP_EL0(用户态栈寄存器),开始使用 SP_EL1
- 修改 PSTATE 寄存器中的特权级标志位,设置为内核态
- 找到异常处理函数的入口地址,并将该地址写入 PC,开始运行操作系统
-
操作系统在切换过程中的任务:将属于应用程序的 CPU 状态保存到内存中,用于之后恢复应用程序继续运行,包括:
- 通用寄存器
x0-x30 - 特殊寄存器,主要包括 PC、SP 和 PSTATE
- 系统寄存器,包括页表基地址寄存器等
- 通用寄存器
-
硬件操作的必要性:
- PC 寄存器的值必须由处理器保存:否则当操作系统开始执行时,PC 将被覆盖
- 栈的切换必须由硬件完成:否则操作系统有可能使用用户态的栈,导致安全问题
系统调用的优化
- vDSO:内核将一部分数据通过只读的形式共享给应用,允许应用直接读取
- Flex-SC:允许应用以“向某一块内存页写入请求”的方式发起系统调用,并通过轮询来等待系统调用完成
- 内核独占一个 CPU 核心,通过轮询来等待用户的请求,然后执行系统调用,并将返回值写入同一块内存页
- 如果只有一个核心,可以将轮询改成批处理,即应用程序一次发起多个系统调用请求,内核一次性将所有系统调用处理完
Lecture 4: 从应用视角看操作系统抽象
- 处理器上下文:
- 通用寄存器:所有(X0-X30)
- 特殊寄存器:SP_EL0 (栈寄存器)
- 系统寄存器:ELR_EL1(对应 PC), SPSR_EL1(对应 PSTATE)
常见的进程接口
-
exit():终止进程并带上一个 status 状态- 返回值:无返回值
- 语法:
void exit(int status);
-
fork():父进程创建新的子进程,调用一次返回两次- 返回值:子进程为 0,父进程为子进程 PID
- 语法:
pid_t fork(void);
1 | |
-
execve():加载和运行- 只调用一次,且永远不会返回(仅仅在运行报错的时候,返回调用程序)
- 语法:
int execve(const char *filename, const char *argv[], const char *envp[]);
-
waitpid():等待子进程终止后,内核回收子进程- 返回值:成功返回子进程 PID,出错返回 -1
- 语法:
pid_t waitpid(pid_t pid, int *status, int options);- pid>0 :等待集合中只有
pid子进程 - pid=-1:等待集合包括所有子进程
- options=0
- 挂起调用进程,等待集合中任意子进程终止
- 如果等待集合中有子进程在函数调用前已经终止,立刻返回
- 返回值是导致函数返回的终止子进程 pid
- 该终止子进程被内核回收
- status 指针带回被回收子线程的 exit 状态
- pid>0 :等待集合中只有
-
进程的终止:进程终止后,内核不会立刻销毁该进程,而是以终止态存在,等待父进程回收
- 僵尸进程:终止状态下还未被回收的进程
- 如果父进程在自己终止前没有回收僵尸子进程,内核会安排 init 进程回收这些子进程
- 僵尸进程:终止状态下还未被回收的进程
ELF 文件格式
- ELF 格式:可执行可链接格式

- ELF 头部:通常用于存元数据
- 节头部表:每一个节都有一个节头部(节头部表的一项)描述
- ELF 字符串表(.strtab):记录一系列 C 风格字符串,表示符号名或节名
- 用以调试的节:
- .debug:调试符号表,包括变量、typedef、C 源文件
- .line:C 源文件的行数与 .text 节中指令的映射
- 代码和数据节:
- .text:代码
- .rodata:只读数据,包括不可修改的常量数据
char *str = "apple"中的"apple"存放在.rodata段char str2[] = "apple"中的"apple"存放在栈上
- .data:初始化的全局变量和静态变量
- .bss:未初始化的全局变量和静态变量(不占文件空间,运行时分配内存)
Lecture 5: 内存地址翻译’
Lecture 6: 系统初始化
Lecture 7: 操作系统管理页表映射
Lecture 8: 物理内存管理
Lecture 9: 进程
-
进程控制块(PCB):用于表示进程,存储在内核态
- 保存的信息包括:虚拟地址空间、处理器上下文、内核栈
- 进程标识符 PID、退出状态、子进程列表、执行状态
-
进程的退出与等待:
- 进程的退出:进程退出时,其上下文结构和虚拟地址空间会被销毁,但 PCB 和内核栈保留(处于僵尸状态)
- 进程的等待:父进程会等待子进程退出,记录子进程的退出状态,回收 PCB 并销毁内核栈
-
进程的五种典型执行状态:
- 新生(new):刚调用
process_create - 就绪(ready):随时准备执行(但暂时没有执行)
- 运行(running):正在执行
- 僵尸(zombie):退出但未回收
- 终止(terminated):退出且被回收
- 新生(new):刚调用

进程的创建
进程创建的过程

-
一、PCB 相关初始化:PCB 及其包含的内容都需要创建及初始化
- 分配 PCB 本身的数据结构
- 初始化 PCB:虚拟内存
- 创建及初始化 vmspace 数据结构
- 分配一个物理页,作为顶级页表
- 内核栈:分配物理页,作为进程内核栈
-
二、可执行文件加载:可执行文件通常有固定的存储格式,以 ELF 为例
- 从程序头部表可以获取需要的段所在位置
- 通常只有代码段和数据段需要被加载(loadable)
- 加载即从 ELF 文件中映射到虚拟地址空间的过程
-
三、准备运行环境:在返回用户态运行前,还需为进程准备运行所需的环境
- 分配用户栈:分配物理内存并映射到虚拟地址空间
- 准备程序运行时的环境:将参数和环境变量放到栈上
-
四、处理器上下文初始化:最后才初始化处理器上下文,因为其包含的内容直到前序操作完成才确定
- SP:设置为用户栈的栈顶地址(用户栈分配后才确定地址)
- PC:设置 ELR_EL1 寄存器(加载 ELF 才知道程序入口地址)
- PSTATE:设置 SPSR_EL1 寄存器
- 大部分寄存器初始值可直接赋为 0
Linux 的进程创建
-
fork():- 实现:将父进程的 PCB 拷贝一份,包括虚拟内存、内核栈、处理器上下文等
- 优点:
- 接口非常简洁,(过去)实现简单
- 将进程创建和执行(
exec)解耦,提高了灵活度
- 缺点:
- 创建拷贝的复杂度与 PCB 复杂度相关,如今越来越复杂
- 完全拷贝过于粗暴(不如
clone) - 性能差、可扩展性差(不如
vfork和spawn) - 不可组合性 (如
fork()+pthread())
-
fork的替代接口:vfork:类似于fork,但让父子进程共享同一地址空间- 优点:连映射都不需要拷贝,性能更好
- 缺点:只能用在“
fork+exec”的场景中;共享地址空间存在安全问题
posix_spawn: 相当于fork+exec- 优点:可扩展性、性能较好
- 缺点:不如
fork灵活
clone:fork的进阶版,可以选择性地不拷贝内存- 优点:高度可控,可依照需求调整
- 缺点:接口比
fork复杂,选择性拷贝容易出错
进程切换
-
一、p0 进入内核态:由硬件完成部分寄存器保存
- PC 和 PSTATE 分别自动保存到 ELR_EL1 和 SPSR_EL1
-
二、p0 处理器上下文保存:将处理器中的寄存器值保存到处理器上下文对应的位置
-
三、由 p0 切换到 p1:
- 1. 虚拟地址空间切换:设置页表相关寄存器(TTBR0_EL1)
- 使用 PCB 中保存的页表基地址赋值给 TTBR0_EL1
- 2. 内核栈切换:设置内核中的栈寄存器 SP_EL1
- 使用 PCB 中保存的内核栈顶地址赋值给 SP_EL1
- 3. 进程上下文切换:设置
cur_proc为之后要执行的进程(p1)- 表明之后操作系统将以 p1 的身份运行
- 1. 虚拟地址空间切换:设置页表相关寄存器(TTBR0_EL1)
-
四、p1 处理器上下文恢复:从处理器上下文中加载各寄存器的值,放入对应寄存器中
-
五、p1 回到用户态:由硬件自动恢复部分寄存器
- 将 ELR_EL1 和 SPSR_EL1 中的值自动保存到 PC 和 PSTATE 中
参考资料
本文参考上海交通大学并行与分布式系统研究所(IPADS)操作系统课程 CS3601 华志超老师的 PPT 课件整理。
部分图片来源于上海交通大学程序语言与编译原理课程 CS2612 曹钦翔老师的讲义。