zoukankan      html  css  js  c++  java
  • RT调度1—cpupri

    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进行运行。

  • 相关阅读:
    网络流(平面图转对偶图)
    666
    期望总结
    docker-1-简介
    22、整合mybatis
    21、整合Druid数据源
    20、Springboot 与数据访问(JDBC/自动配置)
    19、配置嵌入式servlet容器(下)
    18、配置嵌入式servlet容器(2)
    17、配置嵌入式servlet容器(1)
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/15294465.html
Copyright © 2011-2022 走看看