在 Linux 中,**进程切换(Process Switch)**本质上是由内核调度器(scheduler)完成的,它涉及 进程被抢占、上下文保存与恢复、以及新进程的运行。下面按“整体流程 → 关键机制 → 代码层面 → 常见误区”来解释。
进程切换 = 从一个进程的执行上下文切换到另一个进程的执行上下文。
切换时至少包括:
注意:进程切换 ≠ 线程切换
- 进程切换:地址空间(页表)通常也要切换
- 线程切换:同一进程内,地址空间不变
进程切换只在 内核态 发生,常见触发点有:
timer interrupt)schedule()sleep()wait()mutex / sem 阻塞schedule() 被显式调用need_reschedexit() → 切换到其他进程用户态执行
↓
中断 / 系统调用进入内核态
↓
调度器选择下一个进程
↓
执行上下文切换(context switch)
↓
恢复新进程的执行上下文
↓
返回用户态继续执行
Linux 使用 完全公平调度器(CFS,Completely Fair Scheduler)(默认)。
schedule()
主要做三件事:
task_struct保存的内容包括:
这些信息保存在:
struct thread_struct thread;
位于 task_struct 中。
switch_mm()
主要工作:
✅ 这是 进程切换与线程切换最重要的区别
switch_to(prev, next, last)
这是 架构相关代码(如 x86):
在 x86 上本质类似:
push regs
mov esp, next->thread.esp
pop regs
ret
切换完成后:
✅ CPU 看起来就像“从未离开过新进程”
| 阶段 | 状态 |
|---|---|
| 切换前 | 当前进程内核态 |
| 上下文切换 | 内核态 |
| 切换后 | 新进程内核态 |
| 返回 | 用户态(或继续内核态) |
进程切换 不会直接发生在用户态
✅ 不会显式保存,但会“间接保存”
原因:
iret 恢复用户态现场开销主要来自:
因此:
❌ 进程切换 = 直接切换 PC
✅ 实际是完整上下文切换
❌ 切换发生在用户态
✅ 只发生在内核态
❌ 调度器每次都切换进程
✅ 可优化为“不切换”
Linux 进程切换是调度器通过
schedule(),在内核态保存当前进程上下文、切换页表和寄存器,恢复新进程上下文完成的执行流切换。
如果你愿意,我也可以:
switch_toschedule() 和 context_switch()你更想深入哪一部分?