2.6.4.2 带宽控制 - 带宽时间

前文提到实际的带宽控制是在任务组下属的cfs_rq队列中进行的,而cfs_rq 对带宽时间的操作归总起来就两点:更新与申请。

cfsrq申请到的时间保存在字段 runtime_remaining 中,每当更新任务的时间时,系统也会更新该字段。前面讨论vruntime时我们提到系统通过函数 update_curr 来更新与任务相关的时间信息,实际上该函数也会更新与带宽控制相关的时间:

/* file: kernel/sched/fair.c */
static void update_curr(struct cfs_rq *cfs_rq) {
    struct sched_entity *curr = cfs_rq->curr;
    u64 now = rq_clock_task(rq_of(cfs_rq));
    u64 delta_exec;

    /* 本次更新 vruntime 与上次更新 vruntime 之间的差值 */
    delta_exec = now - curr->exec_start;

    /* 省略其他更新时间的代码,这部分在讨论vruntime时已经讨论过 */

    /* 更新与带宽控制相关的字段 */
    account_cfs_rq_runtime(cfs_rq, delta_exec);
}

/* 判断是否启用了带宽控制,如果是的话调用函数__account_cfs_rq_runtime更新对应字段
 */
static __always_inline void account_cfs_rq_runtime(struct cfs_rq *cfs_rq,
                                                   u64 delta_exec) {
    if (!cfs_bandwidth_used() || !cfs_rq->runtime_enabled)
        return;

    __account_cfs_rq_runtime(cfs_rq, delta_exec);
}

static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) {
    /* 从剩余时间中减去这次消耗掉的部分,注意这里可能导致cfs_rq->runtime_remaining为负数
     */
    cfs_rq->runtime_remaining -= delta_exec;

    /* 如果还有剩余时间,则函数返回 */
    if (likely(cfs_rq->runtime_remaining > 0))
        return;

    /* 接下来要向所在的任务组申请时间,但如果当前cfs_rq已经被挂起了的话,就不用麻烦了
     */
    if (cfs_rq->throttled)
        return;
    /*
     * 此时我们调用函数assign_cfs_rq_runtime向任务组申请时间。如果申请时间失败,则cfs_rq应该被挂起。
     * 这里调用resched_curr标记cfs_rq->curr的TIF_NEED_RESCHED位,以便随后将其调度出去
     */
    if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr))
        resched_curr(rq_of(cfs_rq));
}

更新带宽时间的逻辑其实很简单,就是从 cfs->runtime_remaining 减去本次执行的物理时间。如果此时调度器发现时间余额已经耗尽,则会立即尝试从任务组中申请,如果申请失败,说明在本周期内整个任务组的时间都已经耗尽了,而如果当前正在执行的任务在本cfsrq中的话,则需要将其调度出去。从任务组中申请时间的函数是 assign_cfs_rq_runtime:

Last updated

Was this helpful?