Skip to main content
 首页 » 操作系统

Linux 调度器之cpupri

2022年07月19日53lori

1. 相关结构

struct cpupri_vec { 
    atomic_t        count; //记录了此mask成员中被set的CPU的个数,见cpupri_set() 
    cpumask_var_t    mask;  //设置了哪些CPU 
}; 
struct cpupri { 
    struct cpupri_vec    pri_to_cpu[CPUPRI_NR_PRIORITIES]; //102个元素,见convert_prio(),invalid:-1,idle:0,cfs:1,rt:101-prio(0-99) 
    /*rq_offline_rt()中设置为 CPUPRI_INVALID */ 
    int *cpu_to_pri; //每个CPU一个的数组,存放cpupri_set()的参数3,保存的是最新设置的 new_pri 值,也是此cpu上此时最高的优先级 
};

2. cpupri_set

(1) 调用路径

                    rq_offline_rt //rq_offline回调,设置 CPUPRI_INVALID 这个无效优先级到cpu_to_pri 
                    rq_online_rt //rq_online回调,设置 rt_rq->highest_prio.curr这个优先级 
dequeue_rt_entity 
enqueue_rt_entity 
    dequeue_rt_stack 
        __dequeue_rt_entity 
            dec_rt_tasks         
                dec_rt_prio    //最高优先级的dequeue了,设置第二大的优先级 
                    dec_rt_prio_smp(struct rt_rq *rt_rq, int prio, int prev_prio) //prev_prio 取设置之前的rt_rq->highest_prio.curr 
    enqueue_rt_entity 
    dequeue_rt_entity 
        __enqueue_rt_entity 
            inc_rt_tasks 
                inc_rt_prio //新任务优先级比rt_rq->highest_prio.curr高才设置 
                    inc_rt_prio_smp(struct rt_rq *rt_rq, int prio, int prev_prio) //prev_prio 取设置之前的rt_rq->highest_prio.curr 
                        cpupri_set

(2) 函数解析

void cpupri_set(struct cpupri *cp, int cpu, int newpri) 
{ 
    int *currpri = &cp->cpu_to_pri[cpu]; //per-cpu的 
    int oldpri = *currpri; //上次调用这个函数设置的newpri 
 
    if (newpri == oldpri) 
        return; 
 
    newpri = convert_prio(newpri); //对优先级转换一下,cpupri = 101-newpri,(RT的)优先级越高,这个数值越大 
 
    if (likely(newpri != CPUPRI_INVALID)) { 
        //将此 cpu id 设置到 newpri 对应的 vec 中,并计数加1 
        struct cpupri_vec *vec = &cp->pri_to_cpu[newpri]; 
        cpumask_set_cpu(cpu, vec->mask); 
        atomic_inc(&(vec)->count); 
    } 
    if (likely(oldpri != CPUPRI_INVALID)) { 
        //将oldpri对应的vec中记录的cpu清除并计数减1 
        struct cpupri_vec *vec  = &cp->pri_to_cpu[oldpri]; 
        atomic_dec(&(vec)->count); 
        cpumask_clear_cpu(cpu, vec->mask); 
    } 
 
    *currpri = newpri; 
}

设置新的时,同时会把旧的给删除掉。比如设置cpu1上运行的新任务的优先级为10,第一次设置后,cp->cpu_to_pri[1]=10,cp->pri_to_cpu[10].mask记录了cpu1,然后cpu1上切换到一个优先级为20的任务在运行了,再次设置后,cp->cpu_to_pri[1]=20,cp->pri_to_cpu[20].mask记录了cpu1,并且会将之前在 cp->pri_to_cpu[10].mask 中设置的cpu1给清理掉。

虽然每次调用 cpupri_set 都会对 cp->cpu_to_pri[cpu] 赋值为新的值,但是其保存的是始终是最高优先级,因为在所有调用 cpupri_set 之前都进行了判断,只有newpri的优先级比之前设置的高,才会调用 cpupri_set 进行设置。

(3) rt_rq->highest_prio 中有两个成员,curr 和 next,主要在 rt.c中进行赋值

curr 的赋值逻辑为:
inc_rt_prio 中,若 prio 比 curr 成员记录的优先级还高,就将 curr 成员设置为prio;
dec_rt_prio 中,若 rq 中还有rt线程运行,赋值为 sched_find_first_bit(array->bitmap),否则赋值为 MAX_RT_PRIO

next 的赋值逻辑为:
enqueue_pushable_task 时,若是新的线程的优先级比next高,则next赋值为新线程的优先级
dequeue_pushable_task 时,赋值为 rq->rt.pushable_tasks 链表上的第一个线程的优先级

使用逻辑为:
sched_rt_rq_enqueue 中,若发现 rt_rq->highest_prio.curr 比 curr->prio 的优先级还高,就触发重新调度
dec_rt_prio_smp 中,将 rt_rq->highest_prio.curr 设置到 cpu_to_pri 中
select_task_rq_rt 中,新线程的优先级要比选中的cpu的 rt_rq->highest_prio.curr 成员记录的优先级还高才能选中此cpu.
find_lock_lowest_rq 中,给新线程选cpu,若选出的rq的 rt_rq->highest_prio.curr 比新线程的优先级还高,就不选择此cpu
pull_rt_task 中,若src cpu 的 src_rq->rt.highest_prio.next 比当前cpu的 this_rq->rt.highest_prio.curr 记录的优先级低,就不从这个src cpu上拉任务过来
rq_online_rt 中,将 rq->rt.highest_prio.curr 设置到 cpu_to_pri 上
prio_changed_rt 中,若 rq->rt.highest_prio.curr 记录的优先级比正在运行的任务的优先级还高,触发重新调度

rq->rt.highest_prio.curr:表示 rt 的优先级链表中任务的最高的优先级
rq->rt.highest_prio.next:主要是在push/pull任务迁移中使用

3. __cpupri_find

(1) 调用路径

push_rt_task 
    find_lock_lowest_rq 
    select_task_rq_rt //RT的select_task_rq回调 
        find_lowest_rq //rt.c 
check_preempt_curr_rt //RT的check_preempt_curr回调 
    check_preempt_equal_prio 
    cpupri_find_fitness 
        cpupri_find //cpupri.c 传参fitness_fn=NULL 
            cpupri_find_fitness 
                for (idx = 0; idx < task_pri; idx++) { 
                    __cpupri_find 
                }

(2) 函数解析

//lowest_mask既是输入参数,也是输出参数 
static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p, struct cpumask *lowest_mask, int idx,bool drop_nopreempts) //cpupri.c 
{ 
    struct cpupri_vec *vec  = &cp->pri_to_cpu[idx]; 
    int skip = 0; 
 
    /*此pri没有对应的CPU*/ 
    if (!atomic_read(&(vec)->count)) 
        return 0; 
 
    //按位与的结果的第一个,只是一个test,若与的结果为空,则返回大于nr_cpu_ids的值 
    if (cpumask_any_and(p->cpus_ptr, vec->mask) >= nr_cpu_ids) 
        return 0; 
 
    if (lowest_mask) { 
        //尊重task_struct的cpu亲和性的设置 
        cpumask_and(lowest_mask, p->cpus_ptr, vec->mask); 
 
        if (drop_nopreempts) { 
            //去除掉不能抢占的cpu,主要是判断是有软中断在运行就不抢占 
            drop_nopreempt_cpus(lowest_mask); 
        } 
        //去除掉isolated的cpu 
        cpumask_andnot(lowest_mask, lowest_mask, cpu_isolated_mask); 
 
        if (cpumask_empty(lowest_mask)) 
            return 0; 
    } 
 
    return 1; 
}

按优先级由小到大遍历 cp->pri_to_cpu[] 数组,直到找到合适的CPU后返回,目标CPU放在 lowest_mask 中。因此,更容易运行在最低优先级任务运行的cpu上。

3. cpupri_check_rt 函数

__do_softirq 
    softirq_deferred_for_rt 
        cpupri_check_rt
bool cpupri_check_rt(void) 
{ 
    int cpu = raw_smp_processor_id(); 
    //当前cpu的rt优先级链表上是否有RT线程等待运行,cpu_to_pri总是保存最新一次设置的值 
    return cpu_rq(cpu)->rd->cpupri.cpu_to_pri[cpu] > CPUPRI_NORMAL; 
}

4. 总结

每个 cpu 的 rq 的 root_domain 中都有一个 cpupri 结构,用于RT调度类的选核、迁移的优先级参考依据,支撑高优先级任务选择低优先级任务运行的cpu进行运行。


本文参考链接:https://www.cnblogs.com/hellokitty2/p/15294465.html