在上述的实现描述中,对于每一次pick_next函数,我们都需要完整地扫描来获得当前最小的stride及其进程。这在进程非常多的时候是非常耗时和低效的,有兴趣的同学可以在实现了基于列表扫描的Stride调度器之后比较一下priority程序在Round-Robin及Stride调度器下各自的运行时间。考虑到其调度选择于优先队列的抽象逻辑一致,我们考虑使用优化的优先队列数据结构实现该调度。
优先队列是这样一种数据结构:使用者可以快速的插入和删除队列中的元素,并且在预先指定的顺序下快速取得当前在队列中的最小(或者最大)值及其对应元素。可以看到,这样的数据结构非常符合 Stride 调度器的实现。
本次实验提供了libs/skew_heap.h 作为优先队列的一个实现,该实现定义相关的结构和接口,其中主要包括:
1 // 优先队列节点的结构
2 typedef struct skew_heap_entry skew_heap_entry_t;
3 // 初始化一个队列节点
4 void skew_heap_init(skew_heap_entry_t *a);
5 // 将节点 b 插入至以节点 a 为队列头的队列中去,返回插入后的队列
6 skew_heap_entry_t *skew_heap_insert(skew_heap_entry_t *a,
7 skew_heap_entry_t *b,
8 compare_f comp);
9 // 将节点 b 插入从以节点 a 为队列头的队列中去,返回删除后的队列
10 skew_heap_entry_t *skew_heap_remove(skew_heap_entry_t *a,
11 skew_heap_entry_t *b,
12 compare_f comp);
其中优先队列的顺序是由比较函数comp决定的,sched_stride.c中提供了proc_stride_comp_f比较器用来比较两个stride的大小,你可以直接使用它。当使用优先队列作为Stride调度器的实现方式之后,运行队列结构也需要作相关改变,其中包括:
struct run_queue中的lab6_run_pool指针,在使用优先队列的实现中表示当前优先队列的头元素,如果优先队列为空,则其指向空指针(NULL)。
struct proc_struct中的lab6_run_pool结构,表示当前进程对应的优先队列节点。本次实验已经修改了系统相关部分的代码,使得其能够很好地适应LAB6新加入的数据结构和接口。而在实验中我们需要做的是用优先队列实现一个正确和高效的Stride调度器,如果用较简略的伪代码描述,则有:
init(rq):
– Initialize rq->run_list
– Set rq->lab6_run_pool to NULL
– Set rq->proc_num to 0
enqueue(rq, proc)
– Initialize proc->time_slice
– Insert proc->lab6_run_pool into rq->lab6_run_pool
– rq->proc_num ++
dequeue(rq, proc)
– Remove proc->lab6_run_pool from rq->lab6_run_pool
– rq->proc_num --
pick_next(rq)
– If rq->lab6_run_pool == NULL, return NULL
– Find the proc corresponding to the pointer rq->lab6_run_pool
– proc->lab6_stride += BIG_STRIDE / proc->lab6_priority
– Return proc
proc_tick(rq, proc):
– If proc->time_slice > 0, proc->time_slice --
– If proc->time_slice == 0, set the flag proc->need_resched