lab6 — 调度器
前置知识
进程调度算法。
改动点
相比于 lab5
源代码,lab6
主要做了如下改动:
proc.h
扩展struct proc_struct
成员属性1
2
3
4
5
6
7
8
9
10struct proc_struct {
... // 同以往结构的属性
struct run_queue *rq; // 该进程所在的 run_queue
list_entry_t run_link; // 以链表形式链接各就绪进程
int time_slice; // 该进程所占用的 CPU 时间片
skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: 以优先队列形式链接各就绪进程
// 当前进程的 stride 和优先级属性。
uint32_t lab6_stride; // FOR LAB6 ONLY: 该进程对应的 stride 属性 (此二者用于 stride 调度算法)
uint32_t lab6_priority; // FOR LAB6 ONLY: 该进程对应的 priority 属性
};在
alloc_proc()
实现中,增加这些属性的初始化工作:1
2
3
4
5list_init(&(proc->run_link));
proc->time_slice = 0;
skew_heap_init(&(proc->lab6_run_pool));
proc->lab6_stride = 0;
proc->lab6_priority = 0;schedule.[ch]
增加调度器框架信息ucore
调度器框架使用struct sched_class
加以表示:1
2
3
4
5
6
7
8
9
10
11
12
13
14struct sched_class {
// 调度器名称
const char *name;
// 初始化调度器
void (*init)(struct run_queue *rq);
// 进程入队
void (*enqueue)(struct run_queue *rq, struct proc_struct *proc);
// 进程出队
void (*dequeue)(struct run_queue *rq, struct proc_struct *proc);
// 选择待运行的进程
struct proc_struct *(*pick_next)(struct run_queue *rq);
// 时钟触发的处理函数
void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc);
};ucore
调度器具体使用struct run_queue
进行处理:1
2
3
4
5
6struct run_queue {
list_entry_t run_list; // 进程集合的链表形式
unsigned int proc_num; // 进程集合总数
int max_time_slice; // 进程运行的最大时间片
skew_heap_entry_t *lab6_run_pool; // For LAB6 ONLY: 进程集合的优先队列形式
};trap.c
更新时钟中断的处理程序每当发生时钟中断,调用
ucore
调度器的proc_tick()
函数,从而使得调度器能够动态感知时间变化,并更新相关进程的某些调度属性信息。1
2
3case IRQ_OFFSET + IRQ_TIMER:
ticks++;
sched_class_proc_tick(current);default_sched.[ch]
实现 Round-Robin 调度算法sched.c
更新wakeup_proc()
操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void wakeup_proc(struct proc_struct *proc) {
assert(proc->state != PROC_ZOMBIE);
bool intr_flag;
local_intr_save(intr_flag);
{
// proc 若未处于 RUNNABLE 状态,则置为此状态。
if (proc->state != PROC_RUNNABLE) {
proc->state = PROC_RUNNABLE;
proc->wait_state = 0;
// 若当前进程并非 proc,则将 proc 入队 (出现情况:当前进程创建子进程,子进程的 do_fork 最后一步会调用此函数,从而将其入队管理)。
if (proc != current) {
sched_class_enqueue(proc);
}
}
else {
warn("wakeup runnable process.\n");
}
}
local_intr_restore(intr_flag);
}skew_heap.h
提供斜堆的具体实现
练习一
该练习用于了解 Round-Robin 调度算法的具体实现。
Round-Robin 调度算法比较简单,直接查看源代码实现:
1 | // 初始化 RR,只需初始化进程集合,并重置相关属性即可 (max_time_slice 属性由调用者自主设置)。 |
练习二
该练习用于实现 Stride 调度算法。
首先简单介绍 Stride 调度算法的基本思想:Stride 调用算法仍是基于时间片以调度进程,但是它每次选择进展最慢 (表征进程的 struct proc_struct
结构中,stride
表示当前进程的进展度,BIG_STRIDE / priority
表征当前进程被调度后的进展增加值) 的进程加以调度。
接下来,给出 Stride 调度算法的具体实现:
1 | // BIG_STRIDE 取值具有一定原因,它可以保证即使 stride 取值溢出,仍可正确实现两个进程的 stride 比较操作,原理忽略。 |