Linux进程调度原理与算法实现详解
Linux操作系统调度基本准则和实现
1. 操作系统调度概述
1.1 调度基本概念
在多道程序系统中,进程数量通常超过处理机数量,导致进程争用处理机资源的情况不可避免。处理机调度的核心任务是从就绪队列中,按照特定算法选择进程并分配处理机资源,实现进程的并发执行。
操作系统调度需要平衡两个关键因素:
- 公平性:确保所有进程都能获得合理的CPU时间
- 效率:最大化系统吞吐量,最小化响应时间
1.2 调度层次划分
现代操作系统通常采用三级调度体系:
| 调度级别 | 名称 | 主要功能 | 执行频率 |
|---|---|---|---|
| 高级调度 | 作业调度 | 从外存选择作业调入内存 | 几分钟一次 |
| 中级调度 | 内存调度 | 内外存进程交换(挂起/激活) | 视内存压力 |
| 低级调度 | 进程调度 | 选择就绪进程分配CPU | 毫秒级 |
2. 调度实现机制
2.1 调度时机与限制
进程调度和切换属于操作系统内核核心功能,其执行时机受到严格限制:
禁止调度的情况:
- 中断处理过程中:中断上下文不属于任何进程
- 内核临界区:需要独占访问共享数据
- 原子操作期间:如加锁/解锁、上下文保存等
允许调度的条件:
- 进程主动放弃CPU(阻塞/退出)
- 中断返回用户空间前(若设置了调度标志)
- 时间片耗尽(分时系统)
2.2 上下文切换过程
进程切换涉及以下关键操作:
- 保存当前进程的CPU上下文(寄存器、堆栈指针等)
- 更新进程控制块(PCB)状态信息
- 从新进程的PCB恢复执行上下文
- 更新内存管理单元(MMU)设置
- 跳转到新进程的代码位置继续执行
典型上下文切换开销在几微秒到几十微秒之间,现代处理器通过硬件加速(如TLB标签)优化此过程。
3. 调度算法分类
3.1 非剥夺式调度
特点:
- 进程保持CPU直到主动释放
- 实现简单,系统开销小
- 可能导致长进程垄断CPU
适用场景:
- 批处理系统
- 实时性要求不高的嵌入式系统
3.2 剥夺式调度
特点:
- 高优先级进程可抢占CPU
- 响应速度快
- 实现复杂度高
抢占原则:
- 优先级原则:高优先级进程优先
- 短作业优先:短进程可抢占长进程
- 时间片原则:分时系统按时间片轮转
适用场景:
- 交互式系统(如Linux桌面环境)
- 实时系统(如工业控制系统)
4. 经典调度算法
4.1 先来先服务(FCFS)
算法实现:
struct task_struct *pick_next_task_fcfs(struct rq *rq) { return list_first_entry(&rq->tasks, struct task_struct, tasks); }性能指标(示例4个作业):
| 作业 | 提交时间 | 运行时间 | 等待时间 | 周转时间 | 带权周转时间 |
|---|---|---|---|---|---|
| 1 | 8.0 | 2.0 | 0.0 | 2.0 | 1.0 |
| 2 | 8.4 | 1.0 | 1.6 | 2.6 | 2.6 |
| 3 | 8.8 | 0.5 | 2.2 | 2.7 | 5.4 |
| 4 | 9.0 | 0.2 | 2.5 | 2.7 | 13.5 |
特点:
- 平均等待时间:1.575
- 平均周转时间:2.5
- 对长作业有利,短作业不利
4.2 短作业优先(SJF)
算法伪代码:
while 就绪队列不为空: 选择估计运行时间最短的进程 分配CPU执行直到完成 更新统计信息性能对比:
| 指标 | FCFS | SJF | 改进 |
|---|---|---|---|
| 平均等待时间 | 1.575 | 1.175 | -25% |
| 平均周转时间 | 2.5 | 2.1 | -16% |
| 带权周转时间 | 5.625 | 3.525 | -37% |
缺点:
- 长作业可能"饥饿"
- 依赖准确的运行时间预估
- 忽略作业紧迫性
4.3 优先级调度
实现变种:
静态优先级:
- 在进程创建时确定
- 依据:进程类型、资源需求、用户级别
动态优先级:
- 根据系统状态调整
- 常见调整策略:
- CPU密集型:随运行时间降低优先级
- I/O密集型:提高优先级
Linux优先级示例:
// include/linux/sched.h #define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO #define DEFAULT_PRIO (MAX_RT_PRIO + 20)4.4 时间片轮转(RR)
关键参数:
- 时间片长度:典型值10-100ms
- 就绪队列组织:环形缓冲区
性能影响因素:
- 时间片过小:上下文切换开销大
- 时间片过大:退化为FCFS
数学建模: 最优时间片长度应满足:
T = max(T_ctxsw, R/N)其中:
- T_ctxsw: 上下文切换时间
- R: 预期响应时间
- N: 活跃进程数
4.5 多级反馈队列(MLFQ)
典型实现参数:
| 队列级别 | 优先级 | 时间片 | 调度策略 |
|---|---|---|---|
| 0 | 最高 | 10ms | 剥夺式 |
| 1 | 高 | 20ms | 剥夺式 |
| 2 | 中 | 40ms | 剥夺式 |
| 3 | 低 | 80ms | 非剥夺式 |
算法优势:
- 交互式进程:快速响应(高优先级队列)
- 批处理进程:高吞吐(低优先级队列)
- 自适应调整:CPU密集型自动降级
5. 调度准则与评估
5.1 核心评价指标
CPU利用率:
U = (1 - T_idle/T_total) * 100%系统吞吐量:
Throughput = N_completed/T_observation周转时间:
T_turnaround = T_completion - T_submission响应时间:
T_response = T_firstresponse - T_submission
5.2 调度算法比较
| 算法 | 平均响应时间 | 吞吐量 | 公平性 | 实现复杂度 |
|---|---|---|---|---|
| FCFS | 高 | 中 | 低 | 低 |
| SJF | 低 | 高 | 低 | 中 |
| 优先级 | 可变 | 可变 | 中 | 高 |
| RR | 中 | 中 | 高 | 中 |
| MLFQ | 低 | 高 | 高 | 高 |
5.3 Linux调度器演进
O(n)调度器:
- 全局单一队列
- 时间复杂度随进程数线性增长
O(1)调度器:
- 引入优先级数组
- 固定时间选择最高优先级进程
CFS(完全公平调度):
- 基于红黑树实现
- 虚拟运行时间(vruntime)概念
// kernel/sched/fair.c struct sched_entity { struct load_weight load; struct rb_node run_node; u64 vruntime; // ... };
在实际系统设计中,调度算法的选择需要综合考虑工作负载特征、硬件平台特性和系统设计目标。现代操作系统如Linux采用动态混合策略,针对不同场景自动调整调度参数。
