Xen调度过程是调度机制和策略相分离的,上一节主要分析了调度机制,可以出,调度机制中最关键的调用了do_schedule()函数,该函数在credit算法中是如何实现的呢?
通过整理,我们可以理清一下思路:
首先介绍一下一个VCPU任务都有哪些优先级:
#define CSCHED_PRI_TS_BOOST 0 /* time-share waking up 优先调度*/ #define CSCHED_PRI_TS_UNDER -1 /* time-share w/ credits 需调度*/ #define CSCHED_PRI_TS_OVER -2 /* time-share w/o credits 不调度*/ #define CSCHED_PRI_IDLE -64 /* idle 空闲*/
(一)第一个步骤是通过一个判断语句来实现的,如下
if (!is_idle_vcpu(scurr->vcpu)) { /* Update credits of a non-idle VCPU. */ //消耗credit值 burn_credits(scurr, now); scurr->start_time -= now; } else { /* Re-instate a boosted idle VCPU as normal-idle. */ //恢复空闲vcpu scurr->pri = CSCHED_PRI_IDLE; }
这里首先判断了是否是空闲VCPU,如果不是则进行正常调度,通过burn_credits()来消耗该进程的credit值。
若是VCPU则恢复他的优先级。
(二)中间略去一些代码,我们来到第二块
/* * Select next runnable local VCPU (ie top of local runq) */ //选取下一个可运行的本地VCPU(比如链表头) //若当前vcpu可运行则 if (vcpu_runnable(current)) //将当前的vcpu插入队列中合适的位置 __runq_insert(cpu, scurr); snext = __runq_elem(runq->next); ret.migrated = 0;
这里判断了一下该VCPU是否是还需运行,如果还需要运行,则将该代码插入到CPU链表中从大到小的合适位置。
过插入到合适的位置,来维护整个链表的有序性。
插入后,snext自然是需要调度的下一个VCPU了。
(三)略去一些代码,来到第三部分。第三部分是关于负载均衡的。
//SMP负载均衡 //如果下一个最高优先级的VCPU已经用光了credits,看看其他pcpus有没有更紧急的工作 //没有的话,csched_load_balance() 将会返回snext,返回前该节点从队列移除 if (snext->pri > CSCHED_PRI_TS_OVER) __runq_remove(snext); else snext = csched_load_balance(prv, cpu, snext, &ret.migrated);
这段代码首先判断了第一个节点的snext的优先级,如果是可以调度的,则取下该节点,准备开始调度该节点的VCPU了。
如果不是,则通过csched_load_balance()来进行负载均衡。
(四)再略去一些代码,我们来到第四步就是返回需要调度的VCPU的信息了。
ret.time = (is_idle_vcpu(snext->vcpu) ? -1 : tslice); ret.task = snext->vcpu; CSCHED_VCPU_CHECK(ret.task); return ret;
tslice就是需要返回的时间大小,snext的VCPU信息就是需要返回需要调度的VCPU。
接下来对几个部分的细节进行分析。