1. 进程调度概述
1.1 进程与线程的本质区别
在 Linux 内核中,进程是资源分配的最小单位,线程是 CPU 调度的最小单位。两者共享同一个内核任务结构 task_struct,但资源视图不同。
| 对比维度 | 进程 (Process) | 线程 (Thread) |
|---|---|---|
| 资源拥有 | 独立地址空间、文件描述符表、信号处理表 | 共享所属进程的地址空间和资源 |
| 创建开销 | 高(需要复制页表、文件表等) | 低(仅分配栈和寄存器上下文) |
| 调度实体 | 内核以进程/线程统一视为可调度实体 | 内核以进程/线程统一视为可调度实体 |
| 上下文切换 | 涉及地址空间切换(TLB 刷新) | 不涉及地址空间切换(同一进程内) |
| 内核视角 | task_struct 独立存在 |
task_struct 共享 mm_struct |
关键理解:Linux 内核并不区分进程和线程的调度优先级——两者都通过
task_struct 参与调度。区别仅在于资源隔离程度。调度器面对的是“可调度实体”(sched_entity),而非进程或线程的概念。
1.2 调度器在操作系统中的位置
调度器位于内核核心层,介于硬件中断与系统调用之间,是 CPU 资源分配的中枢。其上下层关系如下:
调度器核心职责:在时钟中断或任务主动让出 CPU 时,从就绪队列中选出下一个要运行的
task_struct,执行上下文切换,恢复其寄存器状态和页表,使该任务继续执行。
1.3 调度器的三大核心目标
Linux 调度器设计始终围绕三个有时相互冲突的目标进行权衡:
- 公平(Fairness) — 每个可调度实体应获得与其优先级/权重成比例的 CPU 时间,避免饥饿。
- 高效(Efficiency) — 调度开销(上下文切换、缓存污染)最小化,吞吐量最大化。
- 低延迟(Low Latency) — 交互式任务(键盘输入、鼠标移动)能快速响应,减少调度延迟。
这三个目标在 CFS(完全公平调度器)中通过虚拟运行时间(vruntime)机制统一实现:
1.4 调度器核心目标权衡矩阵
实际系统中,三个目标无法同时达到最优,需要根据负载特征进行取舍:
| 场景 | 优先目标 | 调度策略倾向 | 典型参数 |
|---|---|---|---|
| 批处理 / 科学计算 | 高效(吞吐量) | SCHED_NORMAL + 较大调度延迟 | sched_latency_ns 调大 |
| 桌面 / 交互式应用 | 低延迟 | SCHED_NORMAL + 较小调度延迟 | sched_latency_ns 调小 |
| 实时音视频 | 低延迟 + 确定性 | SCHED_FIFO / SCHED_RR | 实时优先级 80-99 |
| 虚拟化宿主机 | 公平 + 高效 | CFS + CPU pinning | cpu_shares 权重分配 |
常见误区:追求“绝对公平”会导致频繁上下文切换,降低吞吐量;追求“极致低延迟”可能导致 CPU 密集型任务被过度抢占。CFS 通过可配置的
sched_latency_ns 和 sched_min_granularity_ns 提供调优空间,让管理员根据负载特征在三个目标间滑动。
1.5 调度器核心数据结构关系
调度器在运行时依赖以下关键数据结构协同工作:
- task_struct — 每个任务的描述符,包含调度策略、优先级、vruntime、调度实体等字段。
- sched_entity — 调度实体,嵌入在
task_struct中,是 CFS 红黑树的节点。 - cfs_rq — 每个 CPU 核心一个的就绪队列,维护红黑树根节点和运行统计。
- rq — 每个 CPU 核心的全局运行队列,包含 cfs_rq、rt_rq、dl_rq 等子队列。
调度决策路径:时钟中断 → scheduler_tick() → 更新当前任务 vruntime → 检查是否需要抢占 → 若需要则设置 TIF_NEED_RESCHED → 返回用户态或中断结束时 → schedule() → pick_next_task() → 从 cfs_rq 红黑树选最左节点 → context_switch()。