• 调度器13—p->on_rq 和 se->on_rq 分析


    基于MTK linux-4.14

    看代码过程中发现 put_prev_entity() 中判断 prev 的 se->on_rq 为真还执行 enqueue 操作,感到疑惑,追踪一下代码进行分析。

    1. 相关代码段

    __schedule(bool preempt)
    {
        /* 非抢占且非running状态,表示 prev 任务自己因为休眠主动放弃cpu的 */
        if (!preempt && prev->state) {
            deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK); //只有非抢占状态下才有执行,若是被抢占的,没有执行
                dequeue_entity() {
                    if (se != cfs_rq->curr) //此调用路径不成立,正在running的任务是已经dequeue的,在选prev时pick_next_task_fair()中已经dequeue过了。
                        __dequeue_entity(cfs_rq, se);
                    se->on_rq = 0; //也就是说睡眠导致被切换的任务其 se->on_rq 才会被设置为0 ######
                }
            prev->on_rq = 0; //也就是说睡眠导致被切换的任务其 task->on_rq 才会被设置为0 ######
        }
        ...
        next = pick_next_task(rq, prev, &rf);
        
        /* 下面就直接 switch 了,没有on_rq相关内容了 */
        if (likely(prev != next)) {
            ...
            rq->curr = next; //switch 的前一时刻才对 rq->curr 进行更新 ######
            rq = context_switch(rq, prev, next, &rf);
        }
    }

    dequeue_entity 中将 se->on_rq = 0,其常规调用路径如下,而抢占切换路劲下是没有对prev任务调用 deactivate_task 的。

    __schedule
        deactivate_task
            dequeue_task //调度类的这个回调
                dequeue_task_fair //几乎是唯一调用路径
                    dequeue_entity
                        se->on_rq = 0;

    若是在CFS任务之间切换,pick_next_task 调用的就是 pick_next_task_fair:

    static struct task_struct *pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
    {
        struct sched_entity *curr = cfs_rq->curr;
    
        /*
         * 由于我们在没有执行 put_prev_entity() 的情况下到达这里(这个函数的下面才执行),
         * 我们还需要考虑 cfs_rq->curr。 如果它仍然是一个runnable的实体,update_curr()
         * 将更新它的 vruntime,否则忘记我们见过它。
         */
        if (curr) {
            if (curr->on_rq) //此调度实体还在cfs_rq队列上,此路径下,对应的是prev->se,由上可知若是被抢占的才为真。
                update_curr(cfs_rq);
            else
                curr = NULL;
            ...
        }
        ...
        se = pick_next_entity(cfs_rq, curr); //只是选出来一个se,并没有执行任何dequeue的操作
        p = task_of(se);
        
        if (prev != p) {
            struct sched_entity *pse = &prev->se;
            ...
            put_prev_entity(cfs_rq, pse); //这里面将cfs_rq->curr = NULL
            set_next_entity(cfs_rq, se); //这里面将cfs_rq->curr = se
        }
        ...
    }
    
    //将prev任务放回队列
    static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
    {
        if (prev->on_rq) //为真表示是被抢占的
            update_curr(cfs_rq); //此路径下应该是和上面update_curr重复了。。。
        ...
        /* prev若是被抢占的,条件才成立。只有被抢占的,才需要执行挂回去的操作(若是sleep触发的切换就不用挂回去了)*/
        if (prev->on_rq) { //se是on_rq状态了还要 enqueue!
            /* Put 'current' back into the tree. */
            __enqueue_entity(cfs_rq, prev); //将被抢占的任务重新放回cfs队列。放回去之后其on_rq状态与其实际就在cfs_rq上就匹配上了
            ...
        }
        cfs_rq->curr = NULL; //更新cfs_rq->curr
    }
    
    //从cfs_rq中选出一个任务来运行
    static void set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) //fair.c
    {
        /* 'current' is not kept within the tree. 
         * 这个注释应该就是说这个判断是为了避免对curr重复dequeue。
         */
        if (se->on_rq) {
            ...
            __dequeue_entity(cfs_rq, se); //虽然dequeue了,但是 se->on_rq 并没有清0 ######
        }
        cfs_rq->curr = se; //更新cfs_rq->curr
        ...
    }

    2. se->on_rq分析结论

    准确来说,当任务被执行 set_next_entity() 选出去运行,其 se->on_rq 就不能正确表示其在 cfs_rq 上挂载状态了,直到其由于休眠被执行 deactivate_task() 而被切走,或由于被抢占执行 put_prev_entity() 将其重新放回队列中,其 se->on_rq 状态才与其是否的确挂在 cfs_rq 上相吻合。可以理解为 se->on_rq 表示 running+runnable 的调度实体。


    3. 对于任务的 p->on_rq,选出来作为 next 时默认是不为0的,因为在任务入队列的路径中都是有赋值的:

    (1) 被唤醒

        try_to_wake_up //core.c
            //p->on_rq == 0 的情况
            ttwu_queue //core.c
                ttwu_do_activate //core.c
                    ttwu_activate //core.c
                        enqueue_task
                        p->on_rq = TASK_ON_RQ_QUEUED;
            //p->on_rq !=0 的情况
            if (p->on_rq && ttwu_remote(p, wake_flags)) //ttwu_remote的唯一调用位置,作用主要是将p->state=TASK_RUNNING
                goto stat; //只是统计一些信息就成功返回了

    看 ttwu_remote() 函数的注释,它也是将 p->on_rq !=0 当做是被抢占的情况了.

    (2) 迁移过来

    attach_one_task //fair.c
    attach_tasks //fair.c
        attach_task
            activate_task
                enqueue_entity
            p->on_rq = TASK_ON_RQ_QUEUED;

    (3) 被抢占而插入队列

    (4) 还有一种情况,当任务被迁移走时,其 p->on_rq 也是不等于0的

    active_load_balance_cpu_stop //fair.c
        detach_one_task //fair.c
    load_balance //fair.c
        detach_tasks //fair.c
            detach_task
                p->on_rq = TASK_ON_RQ_MIGRATING;
                deactivate_task
                    dequeue_entity

    4. p->on_rq分析结论

    对于 p->on_rq,若是由于睡眠被切走的才为0,其它情况下都不为0。应该是主要用来判断其是否是由于睡眠而被切走的,只有在睡眠状态下为0。一部分内核代码中通过判断 p->on_rq 不等于0来选择 runnable 的任务。也有一部分代码和 p->state 配合使用来断言特定状态下的任务,例如:

    void set_task_cpu(struct task_struct *p, unsigned int new_cpu) //kernel/sched/core.c
    {
        /*
         * We should never call set_task_cpu() on a blocked task,
         * ttwu() will sort out the placement.
         */
        WARN_ON_ONCE(p->state != TASK_RUNNING && p->state != TASK_WAKING && !p->on_rq);
        ...
    }

    5. 若是没有使能 CONFIG_FAIR_GROUP_SCHED,那么 cfs_rq->curr 基本上等同于 rq->curr。

  • 相关阅读:
    What is the difference between google tag manager and google analytics?
    GetService<IMessageBoxService>() returned null.
    Using Google Consent Mode to Adjust Tag Behavior Based on Consent
    what are the values in _ga cookie?
    DEP019 System table or view is deprecated
    How to set the Google Analytics cookie only after another consent cookie is set and "true"?
    Tag Manager and gtag.js
    Using the OptanonWrapper Callback Function
    elk7.15.1版本收集nginx日志并用kibana图形化分析日志
    ELK分析Nginx日志和可视化展示
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/15459829.html
Copyright © 2020-2023  润新知