很多人学操作系统的时候,感觉自己背了很多零碎概念:什么内核态和用户态、什么进程线程、什么分页分段、什么调度算法,背的时候都懂,一到面试就散。
其实操作系统的题目真正的主线只有一条,那就是:
操作系统本质上是在解决三件事:怎么安全地使用硬件,怎么高效地组织执行,怎么合理地管理内存与资源。 你只要沿着这三条线去讲,整个操作系统就不会出现问题。
所以,这篇内容,其实是在回答四个连续问题:
- 程序怎么安全地访问系统资源? 操作系统通过用户态和内核态做权限隔离,通过系统调用提供安全入口;在 IO 场景里,为了减少数据搬运和上下文切换,又引出了零拷贝这条性能优化主线。
- 多个执行流怎么被组织和协调? 操作系统要组织程序执行,于是有了进程、线程、协程,也有了线程状态、PCB、进程通信机制这些配套结构。
- 有限的内存怎么被抽象和复用? 为了解决内存有限和地址隔离问题,操作系统演化出了分页、分段、虚拟内存、地址映射这一整套内存管理体系。
- 有限的 CPU 怎么被合理分配? 最后,为了让多个任务合理共享 CPU,又有了生产者消费者模型代表的同步控制问题,以及批处理、交互式、实时系统下不同目标导向的调度算法。
操作系统的三大态
面试官一上来很喜欢问内核态、用户态、系统调用。本质上是在问你:为什么操作系统一定要做权限隔离?
因为应用程序是不可信的。假设每个进程都可以直接访问网卡、磁盘、内存页表,任何一个程序写错地址,整个系统就会被拖死。所以操作系统把执行级别拆成了两个世界:
- 用户态:业务代码大部分时间待的地方,权限受限,不能直接执行特权指令。
- 内核态:操作系统内核待的地方,可以管理进程、内存、文件系统和硬件设备。
那用户程序如果真的要读文件、发网络请求怎么办?答案就是系统调用。系统调用本质上是用户态向内核态申请服务的标准入口。
相关阅读:
这条线继续往下,马上就会引出另一个经典问题:一次 IO 到底为什么慢?
因为很多时候慢的不是“算”,而是“搬”。数据从磁盘到内存、从内核缓冲区到用户缓冲区、再从用户缓冲区到网卡缓冲区,这里面既有数据拷贝,也有用户态和内核态来回切换。
所以所谓零拷贝,本质上并不是一字面意义上的“完全不拷贝”,而是尽量减少 CPU 参与的数据搬运次数,以及减少不必要的上下文切换。常见的几个关键词你要能连起来:
- DMA:负责让设备和内存直接搬数据,减轻 CPU 负担。
- mmap:把文件映射进进程地址空间,减少一次从内核到用户的显式拷贝。
- sendfile:适合“文件直接发网络”的场景,绕开用户态中转。
相关阅读:
操作系统是怎么组织“执行中的程序”的?
接下来要讲的是进程、线程、协程。这一块的核心不是背区别,而是先回答一句话:
为什么会从进程演化出线程,又为什么在线程之上还会有协程?
进程是资源分配的基本单位,它的隔离性最强,但切换和通信成本也最高。线程共享进程的地址空间和打开文件,能把“执行流”拆得更轻,所以它成了 CPU 调度的基本单位。再往后,线程切换和锁竞争依然有成本,于是又有了协程,让应用自己在用户态做调度。
所以你可以这么理解:
- 进程解决的是隔离和资源拥有权。
- 线程解决的是同一进程内的并发执行。
- 协程解决的是“线程太重、调度太贵”的问题。
相关阅读:
讲完执行单元,面试官通常会继续追问两个配套问题。
第一个是:线程有哪些状态?
这个问题不是要你死记状态名,而是看你能不能理解线程为什么会停下来。线程可能是在等 CPU,也可能是在等锁,也可能是在等 IO,也可能是主动 sleep。真正要抓住的是“状态变化背后的原因”。
相关阅读:
第二个是:并发、并行、同步、异步、阻塞、非阻塞这些词到底有什么区别?
这组词非常容易混,但其实它们根本不是一个维度:
- 并发和并行,说的是任务怎么被执行。
- 同步和异步,说的是你拿结果的方式。
- 阻塞和非阻塞,说的是调用方线程会不会被挂住。
真正厉害的回答,不是一个个背定义,而是知道:同步不等于阻塞,异步也不天然等于非阻塞。
相关阅读:
还有一个经常被忽略但很基础的点,叫 PCB(进程控制块)。为什么说创建进程、撤销进程,本质上都是在操作 PCB?
因为对操作系统来说,进程不是一段抽象代码,而是一组被记录下来的运行信息:进程状态、寄存器现场、内存映射、打开文件、调度信息,这些都装在 PCB 里。没有 PCB,操作系统就没法感知和管理这个进程。
相关阅读:
既然进程要并发运行,就一定会涉及“进程之间怎么交换信息”。所以这一段自然会落到 IPC,也就是进程通信机制。
- 低成本本机通信:管道、命名管道、共享内存。
- 需要同步互斥配合的通信:共享内存 + 信号量。
- 跨机器或者更通用的通信:消息队列、Socket。
共享内存最快,但同步问题最重;Socket 最通用,但成本更高。
相关阅读:
内存管理
操作系统的内存管理,本质上是在做四件事:给进程分配内存;回收不用的内存;做地址转换和隔离;尽量提升内存利用率。
最基础的三种管理方式是:
- 页式管理:固定大小切分,便于管理,利用率高。
- 段式管理:按逻辑意义切分,比如代码段、数据段,更符合程序结构。
- 段页式管理:先分段,再分页,兼顾逻辑和管理效率。
相关阅读:
这里面最容易被单独拿出来问的一题,是分页和分段到底有什么区别。一定要抓住两个关键词:
- 分页是为了操作系统管理方便,页面本身没有业务语义。
- 分段是为了符合程序逻辑结构,段是有含义的。
再往下一层,就是页表和段表。页表负责把虚拟页号映射到物理块号,段表负责记录每一段的基址和长度。也就是说,分页和分段不只是“切法不同”,连地址转换的组织方式都不一样。
相关阅读:
而真正把整个内存管理体系串起来的,是虚拟内存。
虚拟内存为什么重要?因为它解决了一个根本矛盾:程序需要的地址空间,往往大于机器当下真正可用的物理内存。
操作系统给每个进程看到的是一整块连续的虚拟地址空间,至于哪些页真正在内存里,哪些页暂时在外存里,交给底层去动态换入换出。这背后利用的是局部性原理,也就是程序在某段时间内只会高频访问一小部分数据。
相关阅读:
虚拟内存一旦讲清楚,虚拟地址和物理地址就顺理成章了。程序员写代码时操作的是虚拟地址,不是物理地址。虚拟地址经过 MMU 和页表映射后,才落到真正的物理内存位置。这样做的价值非常大:
- 每个进程的地址空间相互隔离。
- 程序不用关心真实物理地址细节。
- 操作系统可以灵活做换页、保护和共享。
相关阅读:
生产者-消费者模型
生产者消费者模型是几乎把并发控制的核心元素都放进来了:共享缓冲区,互斥访问,条件同步,资源计数。
典型写法里会有三个信号量:
mutex:保证临界区互斥。items:表示当前已有多少个可消费资源。space:表示当前还有多少个空位可生产。
这个题最容易体现你是否真正理解并发控制,因为你不只是要知道“上锁”,还要知道为什么先等资源、再进临界区,否则会把线程白白锁死在里面。
相关阅读:
调度算法: CPU的运行逻辑
这一段就是调度算法。很多人一看到 FCFS、SJF、时间片轮转就开始背,但如果你抓不住“系统目标”,这些算法就永远记不牢。
先看批处理系统。这类系统几乎不强调交互体验,更关心的是吞吐量和周转时间。所以常见算法是:
- FCFS:先来先服务,公平但容易让短任务吃亏。
- SJF:短作业优先,整体效率高,但长作业可能饿死。
- SRTN:最短剩余时间优先,是 SJF 的抢占式版本,更激进地优先短任务。
相关阅读:
再看交互式系统。这类系统最重要的是用户感受到的响应速度,所以常见算法会变成:
- 时间片轮转:大家轮流用 CPU,强调响应公平。
- 优先级调度:高优任务先跑,但要防止低优任务长期饿死。
- 多级反馈队列:把短任务优先、长任务下沉、优先级动态调整结合到一起。
相关阅读:
最后是实时系统。这类系统和前两类最大的区别是,它不是追求“平均更快”,而是追求“必须按时”。
- 硬实时:错过截止时间就是失败,比如飞控、汽车安全控制。
- 软实时:偶尔超时可以接受,但整体要尽量稳定,比如音视频播放、在线会议。
相关阅读: