编辑
2026-02-15
记录知识
0
请注意,本文编写于 84 天前,最后修改于 84 天前,其中某些信息可能已经过时。

目录

schedule dequeue
other dequeue
总结

上文介绍了cfs的enqueue操作,那对应的,就需要dequeue,dequeue就是在rb-tree中删除一个任务

schedule dequeue

以schedule为例,其流程为

schedule _schedule pick_next_task pick_next_task_fair set_next_entity __dequeue_entity rb_erase_cached

我们可以看到dequeue的核心函数是__dequeue_entity, 根据上述流程可以知道,当任务被选中执行的时候,会选中rb-tree最小的任务后,主动调用__dequeue_entity来pop queue

other dequeue

其他非标准场景例如

  • blocking,sleep
  • migration
  • exit
  • hotplug
  • resched
  • reweight
  • cgroups change

这些场景上单独封装了和enqueue_task成对的函数,如下

static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags) { if (!(flags & DEQUEUE_NOCLOCK)) update_rq_clock(rq); if (!(flags & DEQUEUE_SAVE)) { sched_info_dequeued(rq, p); psi_dequeue(p, flags & DEQUEUE_SLEEP); } uclamp_rq_dec(rq, p); p->sched_class->dequeue_task(rq, p, flags); }

同样的也有成对的deactivative_task

void activate_task(struct rq *rq, struct task_struct *p, int flags) { enqueue_task(rq, p, flags); p->on_rq = TASK_ON_RQ_QUEUED; } void deactivate_task(struct rq *rq, struct task_struct *p, int flags) { p->on_rq = (flags & DEQUEUE_SLEEP) ? 0 : TASK_ON_RQ_MIGRATING; dequeue_task(rq, p, flags); }

值得注意的是,deactivate_task和dequeue_task有一些额外的flag判断,简单解析一下

  1. p->on_rq = (flags & DEQUEUE_SLEEP) ? 0 : TASK_ON_RQ_MIGRATING;

说明这次deactivate_task大概率是因为睡眠或者迁移导致的dequeue,分别为sleep和migration设置了on_rq标志位

  1. DEQUEUE_NOCLOCK

如果设计到睡眠,迁移,那么入队出队需要更新时间,但是如果是直接schedule,那么这个会默认带上,也就意味着没必要重复的更新时间

  1. DEQUEUE_SAVE

它和ENQUEUE_RESTORE成对,通过记录enqueue和dequeue的sched_info,然后在dequeue的时候故意不去将sched_info dequeue,这样可以在例如负载均衡的场景上获取sched_info信息统计负载,当然也可以调试.sched_info会更新enqueue和dequeue的时间戳

总结dequeue的大概流程为

deactivate_task dequeue_task dequeue_task_fair dequeue_entity __dequeue_entity rb_erase_cached

值得注意的是,我们在enqueue提到了为了维护公平性需要单独加上当前cpu的min_vruntime,那对应的就要在dequeue上单独减去当前cpu的min_vruntime,这个代码就藏在dequeue_entity中

if (!(flags & DEQUEUE_SLEEP)) se->vruntime -= cfs_rq->min_vruntime;

注意的是,如果不是SLEEP,那假设是MIDRATION,那是通过 update_curr() -> update_min_vruntime() 更新的,所以这里就不需要再多减一次了.

总结

与enqueue成对的,本文介绍了cfs的dequeue,简单理解就是cfs的dequeue就是在rb-tree上删掉成员,所以有两个路径

  • 路径1: pick_next_task上,直接删掉节点就行
  • 路径2: 其他场景,调用成对的api,dequeue_task,它会更新和维护好vruntime,以及调度时钟信息