每日阅读1之内核设计与实现(第三版)4.5——linux调度实现之时间记账

前面讨论CFS调度算法的动机和内在逻辑,下面开始探索CFS是如何实现的。

相关代码位于kernel/sched_fair.c文件中。

主要包含四个组成部分:

(1)时间记账

(2)进程选择

(3)调度器入口

(4)睡眠与唤醒

今天,主要关注时间记账这个部分。。。

所有的调度器都必须对进程的运行时间进行记账,大多数unix系统采用为进程分配一个时间片,每当系统时钟节拍发生时,时间片减少一个节拍周期,当剩余时间片减少到0时,该进程被其它可运行进程抢占。

在现在的CFS调度中,没有了时间片的概念,但是仍然需要进行时间记账,以确保每个进程只在分配给它的处理器时间内运行,CFS使用调度器实体结构struct sched_entity(定义位于linux/sched.h中)来跟踪进程运行记账。。

struct sched_entity {
struct load_weight load; /* for load-balancing */
struct rb_node run_node;
struct list_head group_node;
unsigned int on_rq;


u64 exec_start;
u64 sum_exec_runtime; //虚拟运行时间,以ns为单位,和定时器节拍不在相关
u64 vruntime;
u64 prev_sum_exec_runtime;


u64 last_wakeup;
u64 avg_overlap;


u64 nr_migrations;


u64 start_runtime;
u64 avg_wakeup;


u64 avg_running;

。。。。。。。。。。

}

调度器实体结构,实际上是一个成员变量se,嵌入在进程结构体中。

kernel/sched_fair.c中的update_curr()函数实现vruntime记账功能。

static void update_curr(struct cfs_rq *cfs_rq)
{
struct sched_entity *curr = cfs_rq->curr;
u64 now = rq_of(cfs_rq)->clock_task;
unsigned long delta_exec;


if (unlikely(!curr))
return;


/*
* Get the amount of time the current task was running
* since the last time we changed load (this cannot
* overflow on 32 bits):
*/
delta_exec = (unsigned long)(now - curr->exec_start); //该函数计算当前进程的执行时间,并放在delta_exec变量中,而后将该运行时间传递到__update
if (!delta_exec)
return;


__update_curr(cfs_rq, curr, delta_exec);
curr->exec_start = now;


if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr);


trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
cpuacct_charge(curtask, delta_exec);
account_group_exec_runtime(curtask, delta_exec);
}

好,一个好的开始,明天接着分析。。。

/*
 * Update the current task's runtime statistics. Skip current tasks that
 * are not in our scheduling class.
 */
static inline void
__update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
     unsigned long delta_exec)
{
unsigned long delta_exec_weighted;


schedstat_set(curr->exec_max, max((u64)delta_exec, curr->exec_max));


curr->sum_exec_runtime += delta_exec;
schedstat_add(cfs_rq, exec_clock, delta_exec);
delta_exec_weighted = calc_delta_fair(delta_exec, curr);


curr->vruntime += delta_exec_weighted; //__update_curr进行加权计算(根据可运行进程总数),最终值加入到当前进程的vruntime中去
update_min_vruntime(cfs_rq);
}

update_curr()是由系统定时器周期性调用(中断上下文?),无论进程处于运行态或阻塞态,根据这种方式vruntime可以准确的测定给定进程的运行时间。。

这句话看不懂,这里的系统定时器的工作层次在哪里?

下一节去研究一下,系统定时器。。

欲知后事如何,see U next paper!






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章