cgroup 进程调度之 cfs 时间片限制

一些资料

  1. https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
  2. CPU bandwidth control for CFS

cpu带宽控制的原理是在给定周期内(cpu.cfs_period_us)内限制进程组的运行时间(cpu.cfs_quota_us),这两个参数值通过cpu子系统来设置。

cfs_rq相关的两个成员是 runtime_remainingruntime_expires ,前者是当前进程组的剩余时间片,后者是时间片的到期时间。

内核的做法是:

  1. 每次进程切换的时候更新当前进程的运行时间,检查进程时钟带宽是否超过阈值。
  2. 通过一个定时器,每隔固定的cpu.cfs_period_us周期,更新进程组的时钟带宽

进程切换的时候会统计cpu的实际运行时间,如下:

/**
put_prev_entity()
    update_curr()
        account_cfs_rq_runtime()
 **/ 

static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq,
                      unsigned long delta_exec) {
    /* dock delta_exec before expiring quota (as it could span periods) */
    // 当前cfs_rq的剩余时间片减少
    cfs_rq->runtime_remaining -= delta_exec;
    expire_cfs_rq_runtime(cfs_rq);

    if (likely(cfs_rq->runtime_remaining > 0))
        return;

    /*
     * if we're unable to extend our runtime we resched so that the active
     * hierarchy can be throttled
     */
    if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr))
        resched_task(rq_of(cfs_rq)->curr);
}

注意在cfs带宽控制里,所有的时间都是基于真实时钟而不是虚拟时钟来计算的。与进程组优先级无关。

检查进程组的时钟带宽是否<=0,如果是则throttle

/*
调用栈如下
put_prev_entity()
     check_cfs_rq_runtime()
 */

/* conditionally throttle active cfs_rq's from put_prev_entity() */
static void check_cfs_rq_runtime(struct cfs_rq *cfs_rq)
{
    if (likely(!cfs_rq->runtime_enabled || cfs_rq->runtime_remaining > 0))
        return;

    /*
     * it's possible for a throttled entity to be forced into a running
     * state (e.g. set_curr_task), in this case we're finished.
     */
    if (cfs_rq_throttled(cfs_rq))
        return;
    // 如果cfs_rq的cfs_rq->runtime_remaining <= 0,则throttle
    throttle_cfs_rq(cfs_rq);
}

throttle_cfs_rq所做的事情就是将当前进程组dequeue出cpu的运行队列,并加入到throttled_list里。进程组的cfs_rq既然不在cpu的运行队列里,自然就不会被调度运行了,然后必须等到下一个周期才能重新进入到cpu队列里。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注