操作系统(6)第二章- 处理器调度
处理器调度 & 调度层次
1. 调度到底在干嘛?
CPU 只有一个(或几个核心),但线程成千上万。调度 = 操作系统决定:哪个线程在哪个时刻占用哪个 CPU 核心。
调度的目标:
- 让程序响应快
- 让 CPU 尽量不闲着
- 避免某个线程饿死
- 保证公平、稳定
2. 调度的三个层次(工程里真实存在)
① 高级调度(作业调度)
对应场景:批处理系统、离线任务、大规模计算集群
- 决定:哪些任务能被加载进系统、创建进程
- 调度单位:作业 / 任务组
- 频率很低,基本不影响你写业务代码
- 现代 PC、服务器、云应用里基本不用关心
你写后端、APP、微服务,基本接触不到。
② 中级调度(内存调度)
对应场景:内存不够用时的换入换出
- 决定:哪些进程暂时挪到交换分区(swap),哪些再拉回内存
- 目的:缓解内存压力
- 调度单位:进程(挂起 / 唤醒)
- 你写代码时感知不到,但它会影响程序卡顿、延迟
比如:内存爆了 → 进程被换出 → 再次运行时卡顿这就是中级调度在起作用。
③ 低级调度(CPU 调度 / 线程调度)
这是你写代码时真正要关心的唯一一层
- 决定:就绪队列里哪个线程 next 上 CPU
- 调度单位:线程
- 频率极高(毫秒级)
- 操作系统核心功能,必须有
你写多线程、锁、异步、协程,全都是在跟它打交道。
进程调度的方式、时机、切换和过程
调度时机(内核真正会调用 schedule () 的时刻)
会发生调度(调用 schedule ())的时机
- 时间片耗尽(时钟滴答中断)HZ 定时器中断,current 进程时间片用完,触发重新调度
开发意义:CPU 公平轮转基础
- 进程主动阻塞(自愿调度)socket read、磁盘 IO、sleep、wait、信号量 P、互斥锁阻塞进程主动放弃 CPU,进入等待队列
开发最常见:网络 IO、文件 IO 阻塞都会触发调度
- 高优先级进程唤醒(抢占唤醒)阻塞进程唤醒变为就绪,优先级 > 当前运行进程,立即抢占
开发意义:实时进程、中断下半部唤醒高优先级任务
- 中断处理返回用户态前夕中断处理完,即将回到用户态执行进程时,检查是否需要调度
核心原理:中断处理期间绝对不调度,防止内核栈混乱
内核态抢占点(preempt check)Linux 内核抢占打开后,内核代码打开抢占、关闭抢占,在抢占检查点判断是否需要调度
进程正常退出 exec/exit
中断内部不调度!
中断上下文 = 特殊上下文,不能睡眠、不能调度、不能抢占只有退出中断、返回进程上下文才调度。
进程调度方式(内核真实实现:抢占 / 非抢占)
开发视角:用户永远感知不到调度,调度全部在内核态完成
1. 非抢占式调度(协作式调度 Cooperative)
内核不能强行抢走 CPU当前进程必须主动放弃 CPU才会调度:
- 主动阻塞(read/write/poll/select/sleep/ 信号量 P)
- 进程退出
- 主动调用 schedule ()
特点:
- 无抢占延迟、上下文切换少
- 缺点:一个死循环进程可以占满 CPU 永不释放
- Linux 早期内核、单片机、RTOS 轻量级、用户态协程(Go 协程、libco)都是协作非抢占
2. 抢占式调度(抢占式 Preemptive,Linux 默认)
内核可以在任意时刻强行剥夺当前进程 CPU分为两种开发重点:
- 用户态抢占:时钟中断到来,直接抢占(最常见)
- 内核态抢占:Linux 2.6 以后支持,内核代码运行时也可以被抢占(解决内核延迟问题)
开发理解:抢占 = 时钟中断触发 + 调度器重新选进程现代服务器、移动端、桌面 OS全部抢占式
进程切换(上下文切换 Context Switch)
调度 ≠ 切换
- 调度:调度器算法 pick 下一个进程(policy)
- 切换:硬件层面保存 / 恢复寄存器,完成 CPU 使用权转移(mechanism)
上下文切换内容(真实硬件):
- 用户态通用寄存器 eax/ebx/ecx...
- 程序计数器 RIP/PC
- 栈指针 RSP/SP
- 程序状态字 RFLAGS/PSW
- 页表基址 CR3(地址空间切换!开销最大)
- 内核栈指针、PCB 指针
开销来源:寄存器保存恢复 +页表切换 TLB 刷新+ 缓存失效
开发调优重点:频繁上下文切换 = CPU 性能暴跌
完整调度 + 切换全过程(Linux 内核真实执行流程)
按 CPU 执行顺序,一步不差:
前提:当前进程 A 正在用户态运行
硬件触发中断(时钟中断 / IO 中断)CPU 自动保存用户态现场(部分寄存器)→ 强制进入内核态中断入口
中断处理程序运行处理中断事件(IO 完成、时钟计数 ++)
中断处理完毕,检查是否需要调度判断条件:
- 当前进程时间片用完?
- 有更高优先级就绪进程?
- need_resched 标志置位?
如果需要调度:调用内核函数schedule()进入进程调度主逻辑:① 把当前进程 A 状态:Running → Ready,插入就绪队列 CFS / 实时队列② 调度器遍历就绪队列,按照 CFS 完全公平调度算法选出进程 B③ 把进程 B 状态:Ready → Running
硬件上下文切换(进程切换核心)① 保存进程 A 全部硬件上下文到 A 的 PCB② 加载进程 B 的 PCB 上下文到 CPU 寄存器③ 切换页表 CR3,刷新 TLB(地址空间切换)
切换完成,CPU 现在属于进程 B从内核态中断返回指令恢复用户态现场
CPU 开始执行进程 B 用户态代码
调度器和闲逛进程
一、调度器(Scheduler)
调度器是操作系统内核的核心组件,负责管理进程 / 线程、决定谁用 CPU、用多久、何时切换,确保系统公平、高效、可响应。
1. 核心职责
- 选择进程:按调度算法(如 FCFS、时间片轮转、优先级)从就绪队列选下一个运行进程。
- 上下文切换:保存当前进程状态、恢复新进程状态,实现 CPU 控制权交接。
- 状态管理:维护进程就绪、运行、阻塞三态转换。
2. 调度触发时机
- 进程创建 / 退出、运行进程阻塞(如 I/O)、I/O 中断唤醒进程、时钟中断(抢占式)。
3. 调度方式
- 抢占式:时钟中断强制切换,防长进程独占 CPU。
- 非抢占式:进程主动放弃 CPU(阻塞 / 退出)才切换。
二、闲逛进程(Idle Process)
闲逛进程(空闲进程)是系统优先级最低的特殊进程,仅当就绪队列为空时运行,避免 CPU 无任务可做。
1. 核心特点
- 优先级最低:仅在无其他就绪进程时被调度。
- 空循环 + 节能:执行
halt或nop,让 CPU 进入低功耗 C-state,省电降温。 - 永不退出:系统启动时创建(Linux 为 0 号进程,Windows 为 “系统空闲进程”)。
- 不抢占:多核下每个 CPU 核心一个 idle,禁止抢占防栈溢出。
2. 核心作用
- 避免 CPU 空转:就绪队列永不空,调度器总有进程可选。
- 节能降耗:CPU 空闲时降频 / 休眠,延长续航、降低散热。
- 快速响应:新进程就绪时,调度器立即抢占 idle,零延迟切换。
3. 工作流程
- 系统初始化创建 idle 进程,优先级设为最低。
- 调度器检查就绪队列:
- 非空:按算法选正常进程运行。
- 为空:调度 idle,执行
halt让 CPU 休眠。
- 中断(如 I/O、时钟)唤醒 CPU,回到步骤 2。
三、调度器与闲逛进程的关系
- 备胎机制:idle 是调度器的 “永远备胎”,确保 CPU 永不空闲。
- 优先级隔离:调度器永远优先调度正常进程,仅无任务时才用 idle 填补空闲。
- 协同节能:调度器负责任务分配,idle 负责空闲节能,共同平衡性能与功耗。
四、关键区别
| 特性 | 调度器 | 闲逛进程 |
|---|---|---|
| 角色 | 资源分配决策者 | 空闲占位任务 |
| 优先级 | 内核级(调度自身) | 系统最低 |
| 运行条件 | 持续工作(触发时调度) | 仅就绪队列为空 |
| 核心功能 | 选择 / 切换进程 | 空循环、节能 |
总结
调度器是 CPU 资源的 “管理者”,决定任务分配;闲逛进程是 CPU 空闲时的 “占位符”,保证系统稳定与节能。二者协同,让系统在繁忙时高效、空闲时省电。
