操作系统中的调度可以发生在不同层面,对应不同的资源管理和时间尺度:
联系: 这三个层次的调度相互关联,共同管理进程从创建到完成的整个生命周期及其资源使用。
调度通常在以下事件发生后,内核处理完相应事件并准备返回用户态之前的最后时刻进行:
exit()
)。abort
)。fork()
)。wait()
)。yield()
)。流程: 事件发生 → 暂停当前进程 → 硬件响应 → 进入内核处理事件 → 事件处理结束(可能导致进程状态变化、就绪队列调整)→ 执行进程调度 → 选择新进程运行。
swtch.S
): (此处展示汇编代码逻辑,具体代码略)swtch
函数接受两个参数:旧进程上下文指针 (old
) 和新进程上下文指针 (new
)。old
进程的callee-saved寄存器到其上下文结构中。new
进程的上下文结构中恢复callee-saved寄存器。ret
指令返回,此时CPU将跳转到 new
进程之前保存的PC地址, effectively switching execution flow. The key is switching the stack pointer.调度算法的选择与操作系统的主要应用场景和目标密切相关:
设计调度算法时,需在多个目标之间进行权衡(Trade-off):
设计或选择调度算法时,需要考虑以下几个方面:
进程优先级 (Priority):
就绪队列组织 (Ready Queue Organization):
抢占 vs. 非抢占 (Preemptive vs. Non-preemptive):
yield
)。适用于批处理,简单,但响应性差。I/O密集型 vs. CPU密集型进程 (I/O-bound vs. CPU-bound):
时间片 (Time Slice / Quantum):
主要目标:高吞吐量、低周转时间、高CPU利用率。
先来先服务 (FCFS - First Come First Serve):
最短作业优先 (SJF - Shortest Job First):
进程 | 到达时刻 | 运行时间 |
---|---|---|
P1 | 0 | 7 |
P2 | 2 | 4 |
P3 | 4 | 1 |
P4 | 5 | 4 |
最高响应比优先 (HRRN - Highest Response Ratio Next):
主要目标:快速响应时间、均衡性、公平性。
时间片轮转 (RR - Round Robin):
虚拟轮转 (Virtual RR - VRR):
优先级调度 (Priority Scheduling):
多级队列调度 (Multilevel Queue Scheduling):
多级反馈队列调度 (Multilevel Feedback Queue Scheduling - MFQ):
其他交互式算法 (简述):
主要目标:满足任务截止时间、可预测性。
可调度性分析: 对于周期性实时任务,需要判断系统是否能在所有任务的截止时间内完成它们。
速率单调调度 (Rate-Monotonic Scheduling - RM):
最早截止时间优先 (Earliest Deadline First - EDF):
调度算法 | 选择依据 | 决策模式 | 吞吐量 | 响应时间 | 开销 | 对进程影响 | 饥饿问题 |
---|---|---|---|---|---|---|---|
FCFS | max[w] | 非抢占 | 不强调 | 可能很差 (长作业阻塞短作业) | 最小 | 对短进程/IO密集型不利 | 无 |
RR | 固定时间片 | 抢占(时间片) | q过小则低 | 短进程好 | 较小 | 公平 | 无 |
SJF | min[s] | 非抢占 | 高 | 短进程好 | 可能较高 | 对长进程不利 | 可能 |
SRTN | min[s-e] | 抢占(到达时) | 高 | 好 | 可能较高 | 对长进程不利 | 可能 |
HRRN | max[(w+s)/s] | 非抢占 | 高 | 较好 | 可能较高 | 平衡 | 无 |
Feedback | 见算法思想 | 抢占(时间片) | 不强调 | 较好 (可调优) | 可能较高 | 可优待IO密集型,可能对某些进程不利 | 可能 (需老化) |
(表中 w
: 等待时间, s
: 总服务时间, e
: 已执行时间)
sched_class
结构就体现了这种思想。yield
)。Quantum
和 QuantumReset
记录在 KTHREAD
结构中。migration/CPUID
内核线程。当检测到负载不均时(如CPU0空闲,CPU1忙),CPU0可以向CPU1的停机工作队列 (stop machine workqueue) 添加一个任务,唤醒CPU1的迁移线程。该线程优先级很高,会立即执行迁移任务(如将进程D从CPU1迁移到CPU0),从而实现负载均衡。调度单位: 线程(内核级线程,Linux中称为’进程’或’任务’)。
进程分类与调度策略:
SCHED_FIFO
(静态优先级,非抢占式,除非更高优先级到达或阻塞)、SCHED_RR
(静态优先级,抢占式,带时间片轮转)。优先级范围 1-99。SCHED_NORMAL
(也叫 SCHED_OTHER
), SCHED_BATCH
, SCHED_IDLE
。主要由 CFS (Completely Fair Scheduler) 算法管理。优先级范围 100-139 (对应nice值 -20 到 +19)。Linux调度算法演化:
vruntime
记录进程的加权运行时间。vruntime
增长速度与实际运行时间成正比,与进程权重(优先级)成反比。 vruntime ≈ 实际运行时间 * (NICE_0_LOAD / 进程权重)
(NICE_0_LOAD 是 nice=0 进程的权重)。vruntime
最小 的进程运行。vruntime
排序。插入、删除、查找最小节点都是 O(log n) 时间。调度器取最左节点运行。vruntime
增长慢,更容易被选中;优先级低的进程权重低,vruntime
增长快。最终达到按权重比例分配CPU时间的效果。CFS 调度器详解:
task_struct
: Linux 进程/任务描述符。
sched_entity
: 调度实体,嵌入 task_struct
中,包含CFS调度所需信息(如 load_weight
权重, rb_node
红黑树节点, vruntime
等)。一个 sched_entity
可以代表一个任务或一个任务组(用于组调度)。
sched_class
: 调度类结构体,定义了一套调度器操作函数接口(如 enqueue_task
, dequeue_task
, pick_next_task
)。CFS, RT(FIFO/RR), Idle 都有自己的 sched_class
实现。内核按优先级顺序查询 sched_class
来决定使用哪个调度器。
cfs_rq
: CFS 运行队列,每个CPU有一个。包含红黑树 tasks_timeline
和 min_vruntime
等信息。min_vruntime
记录该队列中所有进程的最小 vruntime
,作为新进程/唤醒进程 vruntime
计算的基准。
红黑树 (rb_node
, rb_root tasks_timeline
): 按 vruntime
组织就绪的 sched_entity
。
CFS 关键情景:
fork()
):vruntime
初始值通常设为当前 cfs_rq->min_vruntime
(或略大),确保新进程不会立即获得过多优势。vruntime
交换? 如果设置了sysctl_sched_child_runs_first
,且父子在同CPU,父vruntime
< 子vruntime
,则交换,让子进程优先运行。wake_up_process()
):vruntime
:通常设为 max(waker->vruntime, cfs_rq->min_vruntime - delta)
,其中 delta
是一个小的补偿值。确保进程不会因睡眠获得不公平优势,但也给予一定补偿使其尽快运行。vruntime
足够小)。scheduler_tick()
):vruntime
(actual_runtime * NICE_0_LOAD / weight
)。cfs_rq->min_vruntime
。TIF_NEED_RESCHED
),在中断返回前会调用 schedule()
。schedule()
):yield
或被标记抢占时调用。vruntime
。pick_next_task()
选择下一个运行进程:sched_class
(RT -> CFS -> Idle)。vruntime
最小者)。cfs_rq->next
(上次被抢占者) 和 cfs_rq->last
(刚运行完者) 的缓存亲和性,可能优先选择它们。sched_entity
。context_switch()
)。CFS 与进程状态转换图示:
graph TD
New -- fork() --> Ready(Ready State: In Red-Black Tree);
Ready -- schedule() selects --> Running(Running State);
Running -- Block (I/O, wait) --> Blocked(Blocked State);
Blocked -- Wakeup --> Ready;
Running -- Timeslice Check in Tick / Preempted --> Ready;
Running -- exit() --> Terminated(Terminated State);
subgraph CFS Logic
direction LR
Ready -- select min vruntime --> Running;
Running -- update vruntime & re-insert --> Ready;
end
mermaid