操作系统基础:CPU调度流程 / 中断 / 系统调用 / 上下文切换

一句话:操作系统是个”大管家”,管 CPU 给谁用、管内存怎么分、管设备怎么访问、管文件怎么存。这一篇先把 CPU 相关的底层机制讲清楚。

一、CPU 调度全流程——从进程产生到执行结束

整体流程


graph TD

    A[程序在硬盘上] --> B[双击运行]

    B --> C[创建进程]

    C --> D[就绪状态<br/>排队等CPU]

    D -->|调度器选中| E[运行状态<br/>CPU在执行]

    E -->|时间片用完| D

    E -->|等IO/等资源| F[阻塞状态<br/>等待中]

    F -->|IO完成/资源就绪| D

    E -->|执行完毕| G[终止]

每个步骤详细说

步骤 1:程序变成进程

 
程序 = 硬盘上的 .exe 文件(死的)
 
进程 = 跑起来的程序(活的)
 
Chrome.exe 在硬盘里躺了几年,你双击它那一刻:
 
  ① 操作系统把代码从硬盘加载到内存
 
  ② 分配 PCB(进程控制块)——进程的"身份证"
 
  ③ 分配内存空间、栈、堆
 
  ④ 把进程扔进就绪队列
 
  → 进程诞生了,开始等 CPU
 

PCB(Process Control Block)——进程的身份证


graph TD

    subgraph PCB 进程控制块

        P1[PID 进程号唯一]

        P2[状态 就绪/运行/阻塞]

        P3[程序计数器 PC<br/>代码执行到哪一行]

        P4[CPU 寄存器值<br/>所有变量的当前值]

        P5[内存限制]

        P6[打开的文件列表]

        P7[优先级]

    end

就像你入职时公司给你建的人事档案——你是谁、在做什么、有什么权限,全记在里面。

步骤 2:调度器选谁上 CPU

 
就绪队列里好几个进程排队等着用 CPU:
 
[Chrome] [VS Code] [微信] [音乐]
 
调度器(Dispatcher)根据调度算法选一个:
 
  FCFS → 先来的先上
 
  RR   → 每人轮流,一人一个时间片
 
  MLFQ → 优先级高的先上
 
被选中的 → 从就绪变成运行
 

步骤 3:上下文切换——换人的瞬间

 
正在跑 Chrome(进程A) → 时间片到了 → 换 VS Code(进程B)
 
切换时发生的事:
 
  ① 把进程A的"现场"全部记住——寄存器里现在有什么值?
 
     程序跑到哪一行了?→ 存到 PCB_A
 
  ② 把进程B的"现场"从 PCB_B 里恢复——上次跑哪了?
 
  ③ 开始跑进程B
 
这个过程叫 上下文切换(Context Switch)
 
 
类比:你在写作业,你妈叫你吃饭,吃完再回来写
 
  你在写数学第 10 题(正在跑进程A)
 
  → 你妈喊"吃饭了!"(中断)
 
  → 你把笔放好,记住写到第 10 题的第 3 小问了(保存上下文)
 
  → 去吃饭(切到进程B)
 
  → 吃完回来,看一眼:哦第 10 题第 3 小问,继续写(恢复上下文)
 
  如果你吃到一半忘了写到哪了,就得重新想——那就是切换成本。
 
 
上下文切换很贵:
 
  每次切换要保存/加载一堆寄存器
 
  还要刷新 TLB(地址映射缓存)
 
  大概 1-10 微秒
 
看起来很短?但切换频繁就累加很多。
 
类比:你写 5 分钟作业就去吃口饭再回来写——永远进入不了状态。
 
        写 1 小时再吃饭——效率高。
 
        这就是时间片不能太小的原因。
 

步骤 4:三种状态切换

 
① 就绪 → 运行:排到队了,上 CPU
 
② 运行 → 就绪:时间片到了/被更高优先级的抢了,下去重新排队
 
③ 运行 → 阻塞:等硬盘读数据/等网络/等用户打字——先睡一觉
 
④ 阻塞 → 就绪:等的东西好了,回来排队
 
⑤ 运行 → 终止:执行完了,再见
 
 
类比:去政务大厅办事
 
就绪 = 取了号,在座位上等着叫号
 
运行 = 正在窗口办业务
 
阻塞 = 材料不齐,回去补材料(不在大厅里等了)
 
        补好了回来重新取号 → 就绪
 
终止 = 办完了,走人
 

中断的分类全景图


mindmap

  root((中断))

    外中断(来自CPU外部)

      可屏蔽中断:键盘、网卡、硬盘、时钟

      不可屏蔽中断:内存校验错、断电

    内中断(CPU自己产生的异常)

      故障 Fault:缺页、除零、段错误

      陷阱 Trap:系统调用、断点

      终止 Abort:内存坏了,救不了

最大的分法就两个维度:


graph TD

    subgraph 中断分类

        A[按信号来源] --> B[外中断<br/>CPU外部设备]

        A --> C[内中断<br/>CPU自己产生]

        D[按能否忽略] --> E[可屏蔽中断<br/>CPU可以选择忽略]

        D --> F[不可屏蔽中断<br/>CPU必须立刻处理]

    end

键盘中断——最直观的例子,一步步看

你按了一个 ‘A’ 键,CPU 那边发生了什么:

 
① 你按下 'A'
 
② 键盘控制器检测到:有人按了!
 
③ 键盘控制器发一个电信号给"中断控制器"
 
④ 中断控制器转发给 CPU
 
⑤ CPU 执行完当前正在执行的那条指令(不能半截停)
 
⑥ CPU 发现:哦,有中断
 
⑦ CPU 赶紧把当前进程的所有信息存起来(寄存器、PC)
 
⑧ CPU 查"中断向量表"——找到键盘中断该调哪个函数
 
⑨ CPU 跳过去执行键盘处理函数
 
⑩ 函数从键盘读扫描码,转成 'A',放进输入缓冲区
 
⑪ 处理完了
 
⑫ CPU 恢复之前存起来的进程信息
 
⑬ 继续跑原来的代码——好像什么都没发生过
 
 
对你的程序来说:它根本不知道这期间被中断过。
 
对你自己来说:你按了 A,屏幕上出现了 A,感觉就是一瞬间。
 
对 CPU 来说:它干了十几个步骤。
 

类比:你正在写作业,你妈喊你吃饭

 
你正在算一道数学题(CPU 跑进程)
 
你妈喊:"吃饭了!"(中断)
 
你停下笔,记住算到哪一步了(保存上下文)
 
你放下笔去吃饭(处理中断)
 
吃完回来,看刚才记的位置,继续算(恢复上下文)
 
从数学题的角度——它不知道你被叫去吃饭了,它只"感觉"你一直在算。
 

内中断(异常 Exception)——CPU 自己搞出来的

外中断是”外部设备来找 CPU”,内中断是”CPU 自己跑着跑着出了问题”。

三种内中断

类型啥时候发生还能救吗例子
故障(Fault)指令执行到一半✅ 修好了重试这条指令缺页异常、除零
陷阱(Trap)指令执行完了✅ 继续下一条系统调用、断点
终止(Abort)严重错误❌ 没救了内存物理损坏

Trap(陷阱)——“我故意的,你帮我干件事”

和 Fault 的核心区别:

 
Fault:重新执行当前指令("刚才那条没跑成,你再跑一次")
 
Trap:继续执行下一条指令("刚才那条跑完了,跑下一条")
 

最经典的例子:系统调用

 
你的程序调 read() → 想从文件读数据
 
但你的程序在用户态——不能直接访问硬盘。
 
必须通过系统调用,让操作系统帮它读。
 
流程:
 
① 程序把"我想 read"的指令和参数放到寄存器
 
② 执行一条特殊的指令:syscall
 
③ 这条指令触发 Trap → CPU 切换到内核态
 
④ 操作系统拿到控制权 → 根据参数找到内核里的 read 函数
 
⑤ 内核真的去硬盘上读了数据
 
⑥ 数据读回来了
 
⑦ CPU 切回用户态
 
⑧ 你的程序继续执行 read 后面的下一行代码
 
 
为什么叫"陷阱"(Trap)?
 
  你故意跳进去的——目的是让 OS 帮你办事,办完再出来。
 
类比:
 
  你在办公室写报告(用户态)
 
  → 需要盖章(需要 OS 帮忙)
 
  → 你走进领导办公室(Trap 进内核态)
 
  → 领导帮你盖章(内核执行)
 
  → 你回自己工位(返回用户态)
 
  → 继续写报告(执行下一条指令)
 

第二个例子:调试器的断点

 
你在 IDE 里给某行代码设了个断点。
 
编译器在这行代码前偷偷插了一条 int 3 指令。
 
程序跑到这里:
 
→ 执行到 int 3 → 触发 Trap
 
→ 调试器(比如 VS Code 的 debugger)拿到控制权
 
→ 程序停住了,你可以看变量、单步执行
 
→ 你点"继续" → 调试器恢复程序 → 执行下一行
 

中断优先级——多个中断同时来了怎么办?

 
想象一下:
 
CPU 正在处理网卡中断(收到数据包)
 
这时候键盘被按下了
 
接着时钟中断也到了
 
三个中断一起来 → CPU 先处理谁?
 

中断有优先级,高的先处理。

 
大致优先级(从高到低):
 
最高:不可屏蔽中断(内存坏了)—— 致命问题
 
  ↑   硬件故障(断电)
 
  ↑   时钟中断 —— 时间管理,关系到调度
 
  ↑   硬盘中断 —— IO 完成
 
最低:键盘/鼠标 —— 慢速设备,最不着急
 

高优先级可以打断低优先级的处理过程,反过来不行。

 
CPU 在处理键盘中断 → 时钟中断来了(优先级更高)
 
  → CPU 暂停键盘处理 → 处理时钟 → 回来继续处理键盘
 
CPU 在处理时钟中断 → 键盘中断来了(优先级更低)
 
  → CPU 说:"键盘你先等着,我处理完时钟再说"
 

这叫 中断嵌套——高优先级中断可以插队。


sequenceDiagram

    participant CPU

    participant Key as 键盘中断(低)

    participant Clock as 时钟中断(高)

    Note over CPU: 正在跑正常代码

    Key->>CPU: 键盘按下了!

    Note over CPU: 开始处理键盘

    Clock->>CPU: 时钟到了!(优先级更高)

    Note over CPU: 暂停键盘,先处理时钟

    Note over CPU: 时钟处理完

    Note over CPU: 回来继续处理键盘

    Key->>CPU: 键盘处理完了

    Note over CPU: 恢复跑正常代码

 
类比:你在接一个不太重要的电话(键盘中断)
 
      → 突然另一个重要电话打进来了(时钟中断,高优先级)
 
      → 你对第一个说"稍等" → 接第二个 → 讲完 → 继续第一个
 

中断处理函数的规矩——为什么不能在里面干太多活

中断处理函数(也叫 ISR,Interrupt Service Routine)有几个硬性规定:

规矩 1:必须尽快结束

 
中断处理期间,其他中断可能被关掉了——你处理得太慢,其他设备就得不到响应。
 
就像你接了一个重要电话,然后跟对方聊了半小时——这半小时里别人打不进来。
 

规矩 2:不能阻塞、不能睡大觉

 
你在中断处理函数里调 sleep()?你在想啥呢?
 
中断上下文不属于任何进程,你阻塞了没人能救你。
 

规矩 3:急事急办,不急的事等会儿办

 
所以中断处理被拆成两半:
 
上半部(Top Half):
 
  立刻要做的事,不能等。
 
  比如:从网卡里把收到的数据包复制到内存缓冲区。
 
  为啥?因为网卡的缓冲区很小,你不赶紧取走,下一个包来就把上一个覆盖了。
 
  类比:外卖到了 → 你赶紧去门口把外卖拿进来放桌上。
 
        不能让人家在门口一直等着。
 
下半部(Bottom Half):
 
  不那么急的事,等会儿再做。
 
  比如:处理刚才收到的那堆数据包——是 HTTP 请求还是视频流?
 
  类比:外卖放桌上了 → 你先把这集电视剧看完 → 再看外卖。
 
        反正外卖已经在桌上了,跑不掉。
 

graph TD

    A[网卡收到数据包] -->|触发中断| B[上半部:立刻把数据从网卡复制到内存]

    B --> C[恢复原来的进程]

    C --> D[下半部(等会儿再做):处理分析数据]

    style B fill:#ff6b6b,color:#fff

    style D fill:#96ceb4

Linux 里的下半部机制:

 
下半部的实现方式:
 
  SoftIRQ(软中断) → 比如网卡数据处理
 
  Tasklet → 类似 SoftIRQ,但更简单
 
  Work Queue → 可以阻塞,最慢
 

中断全过程——从按键到显示,完整串一遍

从你按键盘上的 ‘A’ 到屏幕上出现 ‘A’,经过的所有步骤:

 
① 你按了 'A'
 
② 键盘控制器检测到按键
 
③ 键盘控制器发中断信号到中断控制器
 
④ 中断控制器判断优先级(键盘的优先级低)
 
⑤ 中断控制器把信号转发给 CPU
 
⑥ CPU 执行完当前这条指令(保证不半截停)
 
⑦ CPU 检查到:有中断来了
 
⑧ CPU 保存当前进程的上下文(寄存器、PC 全部存起来)
 
⑨ CPU 暂时关中断(防止处理键盘时又被其他中断打断)
 
⑩ CPU 查中断向量表:键盘中断号对应的处理函数在哪?
 
⑪ CPU 跳到键盘处理函数
 
⑫ 处理函数从键盘端口读扫描码,转成 ASCII 码 'A'
 
⑬ 把 'A' 放到系统的输入缓冲区
 
⑭ 处理完了,CPU 开中断(允许其他中断了)
 
⑮ 恢复之前保存的进程上下文
 
⑯ 继续跑原来的代码——它完全不知道刚才被中断过
 
⑰ 调度器下次运行时,发现"键盘有输入了"
 
⑱ 把 'A' 交给当前的前台程序(比如记事本)
 
⑲ 记事本收到 'A' → 显示到屏幕上
 

从你按 ‘A’ 到看到它,十几个步骤,大约几毫秒。

中断总结


graph TD

    A[硬件事件发生] --> B{来自 CPU 外部还是内部?}

    B -->|外部| C[外中断]

    B -->|内部| D[内中断]

    C --> E[可屏蔽中断<br/>键盘/网卡/硬盘]

    C --> F[不可屏蔽中断<br/>内存校验错]

    D --> G[Fault 故障<br/>缺页/除零]

    D --> H[Trap 陷阱<br/>系统调用/断点]

    D --> I[Abort 终止<br/>硬件损坏]

二、用户态 vs 内核态——为什么要有权限分级

为什么不能所有代码都在”最高权限”下跑?

 
一个 Windows 的经典问题:
 
  程序 A 写 bug 了 → 乱写内存 → 把操作系统自己的数据覆盖了
 
  → 整个系统蓝屏
 
如果所有代码都是最高权限 → 任何一个程序出 bug 都能搞崩整个系统。
 

Intel CPU 设计了 4 个特权级:

 
Ring 0(内核态):操作系统内核 —— 什么都能干
 
Ring 1、2:设备驱动(一般不用)
 
Ring 3(用户态):你的程序 —— 限制最多
 
 
类比:
 
内核态 = 公司 CEO —— 什么都能看、什么都能批
 
用户态 = 普通员工 —— 只能看自己该看的
 
好处:
 
  一个员工搞砸了 → 开掉他就完了
 
  CEO 搞砸了 → 公司可能完了
 
所以:大部分代码在用户态跑,只有 OS 核心在内核态。
 
        一个程序崩了 → 只杀它一个,不影响其他人。
 

三种方式从用户态进入内核态

方式怎么触发的谁发起的类比
系统调用主动请求 OS 帮忙程序自己你举手问老师问题
异常程序执行出错了CPU 自己你走路踩到坑摔了
外中断硬件有通知设备门铃响了

切换成本

 
用户态 → 内核态 → 用户态
 
  ① 保存用户态寄存器
 
  ② 切换栈(用户栈 → 内核栈)
 
  ③ 检查权限
 
  ④ 执行内核代码
 
  ⑤ 恢复用户态寄存器
 
  ⑥ 切回用户栈
 
大约 50-100ns —— 比普通函数调用慢 10 倍以上
 

三、一张表串起所有概念

概念一句话谁触发的影响谁开销
中断(狭义=外中断)设备说”有事了”硬件当前进程
异常CPU 执行指令出问题了CPU当前进程
系统调用程序求 OS 帮忙程序当前进程中(50-100ns)
上下文切换换一个进程跑调度器整个系统大(1-10μs)

四、记忆口诀

 
调度流程一条线:就绪→运行→阻塞→就绪
 
中断是设备找CPU,异常是CPU踩坑了
 
时钟中断是心跳,没它没人换
 
系统调用求OS,用户切内核
 
上下文切换最贵,换进程要全记住
 
时间片不能太小,都去切换没人干活
 

总结:调度流程是执行实体(线程/进程)的一生,中断是 OS 的消息机制,系统调用是程序找 OS 办事的通道,上下文切换是调度的代价。中断里面的时钟中断是分时系统的根基——没有它就没有现代操作系统。

速记卡(面试闪卡)

Q1:一句话讲清「操作系统基础:CPU调度流程 / 中断 / 系统调用 / 上下文切换」到底是什么?

A:这篇梳理 CPU 调度全流程、中断分类、系统调用与上下文切换,是理解操作系统底层机制的核心八股。

Q2:一、CPU 调度全流程——从进程产生到执行结束 —— 怎么理解?

A:进程一生:程序→创建 PCB(进程身份证)→就绪排队→调度器选中运行→时间片到/等 IO 阻塞→再就绪。Context Switch(上下文切换)换人时把寄存器、PC 存进 PCB 再恢复,约 1-10μs,频繁切换很贵。

Q3:二、用户态 vs 内核态——为什么要有权限分级 —— 怎么理解?

A:Intel 分 Ring0(内核态,啥都能干)和 Ring3(用户态,限制多),像 CEO 与普通员工。三种进内核方式:系统调用(主动求 OS)、异常(CPU 踩坑)、外中断(门铃响)。切换开销 50-100ns,比函数调用慢十倍。

Q4:三、一张表串起所有概念 —— 怎么理解?

A:中断=设备找 CPU(硬件触发,开销小);异常=CPU 执行出错(开销小);系统调用=程序求 OS 帮忙(中,50-100ns);上下文切换=换进程跑(大,1-10μs)。时钟中断是分时系统的心跳,没它没人换人。

Q5:四、记忆口诀 —— 怎么理解?

A:口诀:调度一条线“就绪→运行→阻塞→就绪”;中断是设备找 CPU、异常是 CPU 踩坑;时钟中断是心跳;系统调用求 OS 切内核;上下文切换最贵,时间片不能太小否则都去切换没人干活。

Q6:核心速记主线有哪些?

  • 调度流程:就绪→运行→阻塞,PCB 存现场

  • 用户态/内核态分级,三方式进内核

  • 中断(设备)/异常(CPU)/系统调用(程序)区分

  • 上下文切换最贵,时钟中断是分时根基

口诀

A:调度一线就绪运行阻,

中断设备异常踩坑;

系统调用求 OS 帮,

切换最贵片勿小。

相关链接