2.6.4.3 带宽控制 - 定时器
带宽控制器中有两个定时器,分别是 period_timer 与 slack_timer, 前者用来周期性地更新带宽时间并对cfs_rq解挂,后者用来酌情回收已经分配给下属cfs_rq的时间,本节我们将详细探讨他们的实现原理。
定时器的初始化发生在整个带宽控制器的初始化阶段:
/* file: kernel/sched/fair.c */
void init_cfs_bandwidth(struct cfs_bandwidth *cfs_b) {
raw_spin_lock_init(&cfs_b->lock);
cfs_b->runtime = 0;
/* 没有限额 */
cfs_b->quota = RUNTIME_INF;
/* 初始化周期为100ms */
cfs_b->period = ns_to_ktime(default_cfs_period());
/* 初始化 throttled 列表 */
INIT_LIST_HEAD(&cfs_b->throttled_cfs_rq);
/* 初始化 period timer */
hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
cfs_b->period_timer.function = sched_cfs_period_timer;
/* 初始化slack timer */
hrtimer_init(&cfs_b->slack_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
cfs_b->slack_timer.function = sched_cfs_slack_timer;
cfs_b->slack_started = false;
}该函数在初始化任务组时被调用,可以看到两个定时器的回调函数分别是 sched_cfs_period_timer 与 sched_cfs_slack_timer, 这里我们先看前者。
该函数相当于是一个控制层,保证带宽的period与quota的大小合适,并且确保实际工作顺利完成,函数 do_sched_cfs_period_timer 可以精简为如下逻辑:
重置好带宽的时间之后,最后调用函数 distribute_cfs_runtime 来解挂所有的被挂起的队列:
至此,整个 period_timer 就介绍完毕了,接下来我们看一下 slack_timer 是做什么的。
我们知道cfs_rq每次向任务组申请5ms的时间,对于CPU而言这个时间也不少了,但如果该cfs_rq中的任务刚好在申请完时间额度之后就进入了睡眠状态,并且还一睡不醒,那么让它在睡眠过程中一直持有这么多时间是不合理的,这完全可能导致任务组在其他CPU上的cfs_rq在本周期内分配不到时间而被挂起。时间是个宝贵的资源,我们必须保证它的利用率,如果这种情况下我们能将cfs_rq的时间返回一部分给任务组的话,其它有需要的cfs_rq就可以使用了。
返回时间的操作发生在调度器对任务进行dequeue操作时:
函数 start_cfs_slack_bandwidth 用来开启 slack 定时器,该定时器的回调函数是 sched_cfs_slack_timer, 该函数调用 do_sched_cfs_slack_timer 来完成解挂操作:
可以看出 slack 定时器其实是一个辅助角色,它在cfs_rq归还时间后被CFS开启,然后尽可能地提前解挂其它队列,以便提升带宽时间的利用率。
Last updated
Was this helpful?