调度器69—ENQUEUE/DEQUEUE flags
基于 msm-4.14
一、简介
1. 在 enqueue_task/dequeue_task 向就绪队列插入和移除任务的时候,通过 flags 参数判断是由于什么原因触发的enqueue和dequeue,并进行不同的响应。
2. 相关函数:
//kernel/sched/core.c static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags); static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags); /* 这两个是对上面两个函数的封装 */ void activate_task(struct rq *rq, struct task_struct *p, int flags); void deactivate_task(struct rq *rq, struct task_struct *p, int flags);
3. flags参数
//kernel/sched/sched.h #define DEQUEUE_SLEEP 0x01 #define DEQUEUE_SAVE 0x02 /* matches ENQUEUE_RESTORE */ #define DEQUEUE_MOVE 0x04 /* matches ENQUEUE_MOVE */ #define DEQUEUE_NOCLOCK 0x08 /* matches ENQUEUE_NOCLOCK */ #define ENQUEUE_WAKEUP 0x01 #define ENQUEUE_RESTORE 0x02 #define ENQUEUE_MOVE 0x04 #define ENQUEUE_NOCLOCK 0x08 #define ENQUEUE_HEAD 0x10 #define ENQUEUE_REPLENISH 0x20 #define ENQUEUE_MIGRATED 0x40
各值的含义:
DEQUEUE_SLEEP - 任务不再处于可运行状态了,表示自己是由于要进入阻塞休眠状态而dequeue出去的。
ENQUEUE_WAKEUP - 表示任务刚被唤醒,变为可运行状态时的enqueue.
DEQUEUE_SAVE/ENQUEUE_RESTORE - 否则,会出现虚假的出队/入队,以确保任务处于已知状态,允许修改。此类对应尽可能多地保留状态。
DEQUEUE_MOVE/ENQUEUE_MOVE - 与 SAVE/RESTORE 配对,明确不保留就绪队列中的位置。
ENQUEUE_HEAD - 放置在运行队列的前面(如果未指定,则放置在尾部)
ENQUEUE_REPLENISH - CBS(补充运行时间并推迟截止时间)
ENQUEUE_MIGRATED - 任务在唤醒期间被迁移
二、对flags的使用
1. DEQUEUE_SLEEP
表示是由于要进入阻塞休眠状态而dequeue出去的。使用位置:
__schedule //core.c if (!preempt && prev->state) //【】阻塞休眠触发的切换以SLEEP类型dequeue出去 deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK); dequeue_task //core.c if (!(flags & DEQUEUE_SAVE)) psi_dequeue(p, flags & DEQUEUE_SLEEP); //【】根据此标志判断PSI应该统计的状态 dequeue_entity //fair.c update_stats_dequeue //fair.c if (flags & DEQUEUE_SLEEP) //【】对SLEEP类型的dequeue更新任务休眠的开始时间 if (tsk->state & TASK_INTERRUPTIBLE) schedstat_set(se->statistics.sleep_start, rq_clock(rq_of(cfs_rq))); if (tsk->state & TASK_UNINTERRUPTIBLE) schedstat_set(se->statistics.block_start, rq_clock(rq_of(cfs_rq))); dequeue_entity //fair.c if (!(flags & DEQUEUE_SLEEP)) 【】若非SLEEP的dequeue,se->vruntime保存的就是delta值 se->vruntime -= cfs_rq->min_vruntime; enqueue_entity bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_MIGRATED); bool curr = cfs_rq->curr == se; if (renorm && !curr) //若不是WAKEUP的enqueue或是MIGRATED类型的enqueue则在新就绪队列上加回来 se->vruntime += cfs_rq->min_vruntime; throttle_cfs_rq //fair.c dequeue_entity(qcfs_rq, se, DEQUEUE_SLEEP); //【】被throttle的任务是SLEEP的方式dequeue出去的
2. DEQUEUE_SAVE/ENQUEUE_RESTORE
看起来使用这两个标志主要是为了避免更新 sched_info 和 psi 信息。
dequeue_task //core.c if (!(flags & DEQUEUE_SAVE)) { sched_info_dequeued(rq, p); psi_dequeue(p, flags & DEQUEUE_SLEEP); } dequeue_entity if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) != DEQUEUE_SAVE) update_min_vruntime(cfs_rq); move_entity //rt.c if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) == DEQUEUE_SAVE) return false; dequeue_task_dl //deadline.c if (flags & DEQUEUE_SAVE) { sub_running_bw(p->dl.dl_bw, &rq->dl); sub_rq_bw(p->dl.dl_bw, &rq->dl); } /*-------------------------------*/ enqueue_task //core.c if (!(flags & ENQUEUE_RESTORE)) { sched_info_queued(rq, p); psi_enqueue(p, flags & ENQUEUE_WAKEUP); } enqueue_dl_entity if (flags & ENQUEUE_RESTORE) setup_new_dl_entity(dl_se);
3. DEQUEUE_MOVE/ENQUEUE_MOVE
包含 DEQUEUE_MOVE 可以避免更新 cfs_rq->min_vruntime。ENQUEUE_MOVE 没有使用位置。
dequeue_entity //fair.c if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) != DEQUEUE_SAVE) update_min_vruntime(cfs_rq); move_entity //rt.c if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) == DEQUEUE_SAVE) return false; /*-------------------------------*/
4. DEQUEUE_NOCLOCK/ENQUEUE_NOCLOCK
使用这两个标志可以避免更新 rq->clock。
dequeue_task //core.c if (!(flags & DEQUEUE_NOCLOCK)) update_rq_clock(rq); enqueue_task if (!(flags & ENQUEUE_NOCLOCK)) update_rq_clock(rq);
5. ENQUEUE_WAKEUP
主要用于判断是否是唤醒类型的入队。
update_stats_enqueue //fair.c if (flags & ENQUEUE_WAKEUP) update_stats_enqueue_sleeper(cfs_rq, se); enqueue_entity //fair.c bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_MIGRATED); bool curr = cfs_rq->curr == se; if (renorm && !curr) //若不是WAKEUP的enqueue或是MIGRATED类型的enqueue则在新就绪队列上加回来 se->vruntime += cfs_rq->min_vruntime; if (flags & ENQUEUE_WAKEUP) //只有WAKEUP类型的入队才会进行虚拟时间的奖励和惩罚 place_entity(cfs_rq, se, 0); enqueue_task_fair //fair.c int task_new = !(flags & ENQUEUE_WAKEUP); if (!task_new) update_overutilized_status(rq); enqueue_task_rt //rt.c if (flags & ENQUEUE_WAKEUP) rt_se->timeout = 0; enqueue_dl_entity //deadline.c if (flags & ENQUEUE_WAKEUP) { task_contending(dl_se, flags); update_dl_entity(dl_se, pi_se); enqueue_task_dl //deadline.c if (flags & ENQUEUE_WAKEUP) task_contending(&p->dl, flags);
6. ENQUEUE_HEAD
决定插入在链表头还是链表尾,默认插入链表尾,带这个标志插入链表头。
rt_mutex_setprio //core.c if (oldprio < prio) queue_flags |= ENQUEUE_HEAD; __sched_setscheduler //core.c if (oldprio < p->prio) queue_flags |= ENQUEUE_HEAD; enqueue_task(rq, p, queue_flags); __enqueue_rt_entity //rt.c if (flags & ENQUEUE_HEAD) list_add(&rt_se->run_list, queue); else list_add_tail(&rt_se->run_list, queue);
7. ENQUEUE_REPLENISH
只有DL任务使用。
enqueue_dl_entity //deadline.c if (flags & ENQUEUE_REPLENISH) { replenish_dl_entity(dl_se, pi_se);
8. ENQUEUE_MIGRATED
看起来主要使用在任务迁移后的enqueue,控制 se->vruntime 由delta值转换为当前CPU上的虚拟时间。
enqueue_entity //fair.c bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_MIGRATED); bool curr = cfs_rq->curr == se; if (renorm && !curr) //若不是WAKEUP的enqueue或是MIGRATED类型的enqueue则在新就绪队列上加回来 se->vruntime += cfs_rq->min_vruntime; task_contending //deadline.c if (flags & ENQUEUE_MIGRATED) add_rq_bw(dl_se->dl_bw, dl_rq);
posted on 2024-12-04 18:08 Hello-World3 阅读(15) 评论(0) 编辑 收藏 举报