重大更正:CFS调度是没有时间补偿的!

近期看到几篇文章,大多出自本论坛或本网站的博客,比如《Linux进程管理之CFS调度器分析》,我想对这些文章的里面的观点做一下修正,以免更多的人受到误导,毕竟这个篇文章的转载率太高了。
      首先,必须肯定,《Linux进程管理之CFS调度器分析》的作者对CFS的分析还是很透彻的,只不过在有些细节上理解有点错误,现在对照文章的部分内容指出文章中的错误。
原文错误部分:
我们首先来看一下,怎样对进程的vruntime进行调整.这是在place_entity()中进行的,代码如下:
static void
place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
{
    u64 vruntime = cfs_rq->min_vruntime;

    /*
     * The 'current' period is already promised to the current tasks,
     * however the extra weight of the new task will slow them down a
     * little, place the new task so that it fits in the slot that
     * stays open at the end.
     */
     /*sched_vslice():将进程的最大运行时间片转换为vruntime.*/
    if (initial && sched_feat(START_DEBIT))
        vruntime += sched_vslice(cfs_rq, se);

    if (!initial) {
        /* sleeps upto a single latency don't count. */
        if (sched_feat(NEW_FAIR_SLEEPERS)) {
            unsigned long thresh = sysctl_sched_latency;

            /*
             * convert the sleeper threshold into virtual time
             */
            if (sched_feat(NORMALIZED_SLEEPER))
                thresh = calc_delta_fair(thresh, se);

            vruntime -= thresh;
        }

        /* ensure we never gain time by being placed backwards. */
        /*不能小于se->vruntime,即不能使它的vruntime值变得比现在还要小*/
        vruntime = max_vruntime(se->vruntime, vruntime);
    }

    se->vruntime = vruntime;
}
首先,这个函数有一个initial参数,这是为了区别两种不同情况下的vruntime调整,一种情况是新创建进程的情况,也就是我们在上面所分析的情况,它的initial值为1.另外一种情况是唤醒一个进程时的vruntime调整.为了便于后面对唤醒过程的分析,在这里分析这个函数的时候针对这两种情况做一个讲解.
在第一种情况下,新创建的进程都以cfs_rq->min_vruntime都为一个基础,在其后的一段时间后得到调度.这段时间的大小为sched_vslice(),它其实就是将sched_slice()转换为虚拟运行时间.
在第二种情况下,是调整唤醒进程的vruntime值,这种情况比较复杂.
首先,为什么要对唤醒进程的时间进行调整呢?我们来考虑一个这样的问题:
假设进程A睡眠了较长时间,以后被唤醒,如果没有调整其vruntime值,就会使得其vruntime远小于cfs_rq->min_vruntime.这样的后果就是,在其后相当长的一段时间内,进程A都会独占CPU,造成了其它进程不能及时得到响应.
那怎么调整呢?有以下两个情况:、
1):如果进程睡眠的时间很短,也就是se->vruntime仍然大于cfs_rq->min_vruntime.这种情况下,不需要对se->vruntime进程调整.
2):如果进程睡眠时间较长,那当然要对睡眠进程的时间做一个补偿,因此,就将cfs_rq->min_vruntime适当减一个差值做为进程的vruntime值,当然,这种情况下,不能使调整之后的值比之前的vruntime还要小.

上面红字表示的部分是错误的:
1 CFS并不跟踪进程的睡眠时间,也就是说内核并知道进程睡眠的多少时间,内核根本不去管这个睡眠时间,因此不能   以进程睡眠时间长短来分情况本身就是错误的
2 被唤醒的进程的se->vruntime是不可能大于cfs_rq->min_vruntime的,因为尽然已经睡眠过了,就表示是从运行状态进入睡眠状态的,那就是运行过了,既然是在运行的时候睡眠的,那么他的se->vruntime怎么会小于cfs_rq->min_vruntime,后者是前者睡眠后又运行一段时间后的最小值,肯定比前者大。
3 原文中第二点说的要对睡眠时间做补偿也是不正确的,cfs_rq->min_vruntime - thresh ,这只是确保要让被唤醒的进程一定被运行,而且防止原文中提到的:假设进程A睡眠了较长时间,以后被唤醒,如果没有调整其vruntime值,就会使得其vruntime远小于cfs_rq->min_vruntime.这样的后果就是,在其后相当长的一段时间内,进程A都会独占CPU,造成了其它进程不能及时得到响应。这种前情况的出现。

指出几点错误之后,再来总结一下:
这个函数的作用是:减小睡眠的进程的vruntime和当前cfs_rq->minvruntime的差距,让他们尽量接近,这样做的目的是避免了文中假设情况的出现,还能保证被唤醒的进程一定被运行,即包含这么两种情况 :
(1)被唤醒的进程的vruntime太小了,就把它设置成cfs_rq->minvruntime - thresh
(2)但是减去个thresh后,还得保证cfs_rq->minvruntime - thresh > se->vruntime,因为se->vruntime是最小的才是合理的,因为它运行过了!

你可能感兴趣的:(重大更正:CFS调度是没有时间补偿的!)