Linux CFS调度算法总结

1. ideal_runtime 理想运行时间(根据进程权重在CFS中的比重乘以CFS调度周期,具体函数sched_slice)

2. exec_runtime 实际运行时间

3. vruntime 虚拟运行时间(exec_delta*(NICE_0_weight)/weight)= exec_delta*NICE_0_weight*inv_weight,具体可以看函数calc_delta_fair。

4. prio_to_weight数组

nice前后数字是1.25倍关系,每增加一级,可提升10% cpu使用时间

static const int prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

虚拟运行时间 vruntime = delta_exec_runtime * (NICE_0_LOAD/nice_n_weight)=delta_exec_runtime * (NICE_0_LOAD*nice_n_inv_weight)>>32 ;//最后把除法变成乘法和移位操作

优先级越高(nice值更小),权重越大,虚拟运行时间增长越慢,时间运行时间会更长.

内核使用优先级0-139,数值越低,优先级越高,其中0-99给实时进程,100-139给普通进程。用户空间nice值(-19,20)对应100-139的优先级.

CFS调度器总是选择虚拟运行时间最小的进程运行

虚拟时间计算函数:

static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{
    if (unlikely(se->load.weight != NICE_0_LOAD))
        delta = __calc_delta(delta, NICE_0_LOAD, &se->load);

    return delta;
}

 

5.进程资源

  文件,信号量,数据段,堆和栈,寄存器值等等.

6.进程上下文(context)

寄存器和页表(TLB)

7.调度类与调度策略

8. 调度延迟

调度延迟就是让每个可运行状态的进程都运行一次的时间间隔,Linux下的计算函数

sched_nr_latency:值为8,如果CFS中的进程只有8个,那么调度延迟为sysctl_sched_latency(6ms)

如果大于,则调度延迟=nr_running * sysctl_sched_min_granularity(0.75ms),保证每个进程至少运行0.75ms

static u64 __sched_period(unsigned long nr_running)
{
    if (unlikely(nr_running > sched_nr_latency))
        return nr_running * sysctl_sched_min_granularity;
    else
        return sysctl_sched_latency;
}

9 就绪队列(runqueue)

每个CPU都有一个percpu结构体变量struct rq,里面包含有结构体struct cfs_rq, cfs_rq维护着可运行状态的调度实体(struct sched_entity)的红黑树,每次调度时,总是选择最左侧的实体(虚拟时间最小)

10.  唤醒抢占粒度(sysctl_sched_wakeup_granularity=1ms)

在wake_up时,抢占当前进程的条件是:

唤醒进程的虚拟时间需要比当前进程小,且差值要大于根据唤醒进程权重来计算的1ms的虚拟时间.

static int
wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)
{
    s64 gran, vdiff = curr->vruntime - se->vruntime;/*计算虚拟时间差值 */

    if (vdiff <= 0) 
        return -1;

    gran = wakeup_gran(se);/*1ms对应的虚拟时间 */
    if (vdiff > gran)
        return 1;/*如果大于1ms对应的虚拟时间 */

    return 0;
}

11.周期性抢占调度(scheduler_tick)

抢占的条件:由函数check_preempt_tick判断

1. 实际运行时间大于理想运行时间

2.两个进程的虚拟时间差值大于理想运行时间

不发生抢占的条件:函数

1.当前进程运行时间小于最小粒度(sysctl_sched_min_granularity(0.75ms))

2. 当前进程虚拟时间更小

static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
    
    ideal_runtime = sched_slice(cfs_rq, curr);/*根据进程权重分到的理想运行时间 */
    delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
    if (delta_exec > ideal_runtime) {/*运行时间大于理想时间 */
        resched_curr(rq_of(cfs_rq));
        return;
    }
    /*如果运行时间小于最小粒度(0.75ms),则不抢占 */
    if (delta_exec < sysctl_sched_min_granularity)
        return;
    /*获取CFS中最小虚拟时间进程 */
    se = __pick_first_entity(cfs_rq);
    delta = curr->vruntime - se->vruntime;
    /*当前进程虚拟时间更小,不用调用 */
    if (delta < 0)
        return;函数
    /*虚拟时间差值跟理想时间比较???,作者意图是让权重小的进程更加容易被抢占 */
    if (delta > ideal_runtime)
        resched_curr(rq_of(cfs_rq));函数
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值