OS-实现一个RR调度算法
原理
RR调度相比于FCFS要公平一点,RR为每一个任务分配了一段固定的时间片(timeslice),当这个任务消耗完分配的时间片后,就会切换到下一个任务进行调度,这样相同优先级的任务都能够得倒相对公平的调度机会。在RR调度中就是timeslice的大小的划分,太大就退化成了FCFS,太小会引起过多的上下文切换,无味的消耗CPU是很浪费的。
什么是timeslice?
OS会使用一个timer来定时发生中断,timer的定时周期就是timeslice,比如说我们设定timer interval为1ms,也就是1ms会发生一次中断,发生中断后,调度器会切换到下一个任务去运行,那每个任务的timeslice就是1ms,也就是这个任务在一次调度循环中,最多只能运行1ms,就必须切换出去,当然你的任务也可能运行不到1ms就切换到下一个任务,这时候触发切换就是可能是(放弃CPU资源/被更高优先级抢占)。
什么时候会发生调度?
- 主动放弃cpu资源(sleep/suspend)
- 被动放弃cpu资源(mutex/semaphore)
- 被更高优先级任务抢占
- 时间片耗尽
扩展数据结构
相比于FIFO我们增加了timeslice来作为任务切换的一个发起点
typedef struct tcb
{
struct list_head link_head;
time64_t readytime;
uint32_t timeslice;
} tcb_t;
schedule流程
调度流程图
相比于FIFO,我们只是增加了一个schedule的调用时机
线性查找法
伪代码
void schedule(void)
{
tcb_t *cur_task = current();
tcb_t *task = NULL;
tcb_t *task_tmp = NULL;
sched_queue_t *ready_queue = NULL;
for (int i = 0; i < CONFIG_MAX_TASK_PRIORITY; i++)
{
/* 按照优先级由高到低依次查找就绪队列 */
if (!list_empty(&g_readytorun[i].tasks_head))
{
/*
* 找到不为空的对应优先级队列
* 找到的第一个就是优先级最高的队列(默认优先级数值越低优先级越高)
*/
ready_queue = &g_readytorun[i];
int64_t remain = clock_systime_ticks() - task->readytime;
/* 判断任务是否到达了调度时间 */
if (remain >= 0)
{
list_for_each_entry_safe(task, task_tmp, &ready_queue->tasks_head, link_head)
{
if (cur_task != task)
{
list_del_init(&task->link_head);
list_add_tail(&task->link_head, &ready_queue->tasks_head);
task_switch(task);
goto out;
}
}
}
}
}
out:
}
时间复杂度
RR的时间复杂度和FIFO一样,因为采用的都是线性查找算法,下一章我们将采用位图查找来优化我们时间复杂度,使得最后可以达到O(1)