在理解框架之前,需要先了解一下调度器框架所需要的数据结构。
1 struct sched_class {
2 // 调度器的名字
3 const char *name;
4 // 初始化运行队列
5 void (*init) (struct run_queue *rq);
6 // 将进程 p 插入队列 rq
7 void (*enqueue) (struct run_queue *rq, struct proc_struct *p);
8 // 将进程 p 从队列 rq 中删除
9 void (*dequeue) (struct run_queue *rq, struct proc_struct *p);
10 // 返回 运行队列 中下一个可执行的进程
11 struct proc_struct* (*pick_next) (struct run_queue *rq);
12 // timetick 处理函数
13 void (*proc_tick)(struct run_queue* rq, struct proc_struct* p);
14 };
1 struct proc_struct {
2 // . . .
3 // 该进程是否需要调度,只对当前进程有效
4 volatile bool need_resched;
5 // 该进程的调度链表结构,该结构内部的连接组成了 运行队列 列表
6 list_entry_t run_link;
7 // 该进程剩余的时间片,只对当前进程有效
8 int time_slice;
9 // round-robin 调度器并不会用到以下成员
10 // 该进程在优先队列中的节点,仅在 LAB6 使用
11 skew_heap_entry_t lab6_run_pool;
12 // 该进程的调度优先级,仅在 LAB6 使用
13 uint32_t lab6_priority;
14 // 该进程的调度步进值,仅在 LAB6 使用
15 uint32_t lab6_stride;
16 };
在此次实验中,你需要了解 default_sched.c中的实现RR调度算法的函数。在该文件中,你可以看到ucore 已经为 RR 调度算法创建好了一个名为 RR_sched_class 的调度策略类。
通过数据结构 struct run_queue 来描述完整的 run_queue(运行队列)。它的主要结构如下:
1 struct run_queue {
2 //其运行队列的哨兵结构,可以看作是队列头和尾
3 list_entry_t run_list;
4 //优先队列形式的进程容器,只在 LAB6 中使用
5 skew_heap_entry_t *lab6_run_pool;
6 //表示其内部的进程总数
7 unsigned int proc_num;
8 //每个进程一轮占用的最多时间片
9 int max_time_slice;
10 };
在 ucore 框架中,运行队列存储的是当前可以调度的进程,所以,只有状态为runnable的进程才能够进入运行队列。当前正在运行的进程并不会在运行队列中,这一点需要注意。