文章目录
- Cursor的总结
-
- [`pick_next_task_fair` 在做什么](#
pick_next_task_fair在做什么) - 流程图(Mermaid)
- 抽象组织结构(数据关系)
- [`pick_next_task_fair` 在做什么](#
Cursor的总结
问题:kernel/sched/fair.c,帮总结pick_next_task_fair 这个函数的功能,并且画出流程图,以及抽象的组织结构体
pick_next_task_fair 在做什么
它是 CFS(SCHED_NORMAL/SCHED_BATCH) 在 schedule() 里选"下一个谁跑" 的入口:在 CPU 的 rq 上,从 CFS 红黑树里选出 vruntime 最该运行 的实体,一路下到 叶子任务 ;必要时把 上一个任务 从树里"卸下"、把 新选中 的挂上;若没有可运行 CFS 任务则走 idle / newidle_balance 路径。
核心代码:
7191:7332:c:\open_source\linux-5.14.1\kernel\sched\fair.c
struct task_struct *
pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
struct cfs_rq *cfs_rq = &rq->cfs;
struct sched_entity *se;
struct task_struct *p;
int new_tasks;
again:
if (!sched_fair_runnable(rq))
goto idle;
// ... CONFIG_FAIR_GROUP_SCHED 快速路径 vs simple ...
// done: 链表/hrtick/misfit,return p
idle:
if (!rf)
return NULL;
new_tasks = newidle_balance(rq, rf);
// RETRY_TASK / goto again / NULL
}
要点:
-
sched_fair_runnable(rq)等价于
rq->cfs.nr_running > 0。为假时 CFS 侧无人可跑,进入idle。 -
CONFIG_FAIR_GROUP_SCHED时的"快速路径" (prev存在且仍是fair_sched_class)- 不先整棵调用
put_prev_task,而是沿 cgroup 层级 用pick_next_entity选下一个实体,并update_curr/check_cfs_rq_runtime。 - 若选出的任务
prev != p,只在 新旧路径在树上的分叉处 成对调用put_prev_entity/set_next_entity,减少整层遍历。 - 若
prev == p,相当于继续跑当前任务,几乎不动树。
- 不先整棵调用
-
simple路径- 若有
prev,先put_prev_task(rq, prev)(整链put_prev_entity)。 - 自根
cfs_rq起循环:pick_next_entity→set_next_entity→group_cfs_rq(se),直到叶子,p = task_of(se)。
- 若有
-
返回前(
done)- SMP:把
p的se.group_node移到rq->cfs_tasks前(MRU)。 - 可选
hrtick_start_fair、update_misfit_status。 - 返回
p。
- SMP:把
-
idle分支rf == NULL:无法做 newidle 平衡,直接NULL(例如__pick_next_task_fair里prev为 NULL)。- 否则
newidle_balance:< 0→ 返回RETRY_TASK(锁被释放过,需重新选);> 0→goto again;0→update_idle_rq_clock_pelt,返回NULL(CPU 将 idle)。
流程图(Mermaid)
否
是
是
是
否
否
否
是
< 0
> 0
0
进入 pick_next_task_fair
rq->cfs.nr_running > 0?
idle 分支
CONFIG_FAIR_GROUP_SCHED 且 prev 为 fair?
快速路径: 沿层 pick_next_entity / update_curr / check_cfs_rq_runtime
prev != 选中的 p?
在分叉处 put_prev_entity / set_next_entity
MRU 链表 / hrtick / update_misfit_status
simple: put_prev_task 若有 prev
pick_next_entity -> set_next_entity 直到叶子
return p
rf != NULL?
return NULL
newidle_balance
new_tasks
return RETRY_TASK
goto again
update_idle_rq_clock_pelt
return NULL
抽象组织结构(数据关系)
从 "选下一个任务" 的角度,可以看成下面几层:
| 抽象 | 含义 |
|---|---|
struct rq |
每个逻辑 CPU 一条 运行队列 ;rq->cfs 是这条 CPU 上的 CFS 根队列。 |
struct cfs_rq |
CFS 红黑树 + 当前 curr ;组调度时 子 cgroup 对应 子 cfs_rq ,挂在父实体的 group_cfs_rq 链上向下走。 |
struct sched_entity |
调度实体:普通任务 一个 task_struct 里一个 se;cgroup 则是一组任务在树上的 组实体 ,用 pick_next_entity 在树里选 最小 vruntime 的下一个。 |
struct task_struct |
最终选中的 进程/线程 ;task_of(se) 从叶子 se 得到。 |
关系示意:
text
per-CPU
+----------+
| rq |
+----------+
|
+----------+
| cfs_rq | <- rq->cfs,CFS 根
| (rb-tree)|
+----------+
/ \
sched_entity ... (task 或 group)
|
[组调度] group_cfs_rq(se) -> 子 cfs_rq -> 再 pick_next_entity ...
|
[叶子] task_of(se) -> task_struct (下一个运行的 p)
一句话 :pick_next_task_fair 在 rq->cfs 为根的 CFS 树(及组调度时的子 cfs_rq 链) 上选出下一个 sched_entity ,落到 task_struct ,并和 prev 一起决定是走 增量 put/set 还是 整链 put_prev_task + set_next_entity ;无人可跑则配合 newidle_balance 尝试拉任务或返回 idle/重试。