news 2026/5/25 20:15:56

pick_next_task()

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pick_next_task()

pick_next_task()是 Linux 内核调度器的 **“决策核心”,它的核心作用是从当前 CPU 的运行队列(runqueue)中,按照调度类的优先级顺序,挑选出下一个最应该获得 CPU 执行权的进程 **,是schedule()函数中最关键的 “选进程” 步骤。

该函数位于kernel/sched/core.c,本身是一个架构无关的通用函数,最终会调用各个调度类的专属pick_next_task方法完成具体挑选逻辑。

函数核心作用

  1. 遵循调度类优先级排序:按照内核预设的调度类优先级,从高到低依次检查各调度类是否有可运行进程(硬实时 > 软实时 > 普通进程 > 空闲进程)。
  2. 委托专属调度类挑选:对每个有可运行进程的调度类,调用其自身实现的pick_next_task方法,完成该类内的最优进程挑选。
  3. 返回最终候选进程:一旦从某个调度类中挑选到有效进程,立即返回该进程,确保高优先级调度类的进程优先获得 CPU。
  4. 兜底选择空闲进程:若所有调度类都无可用进程,最终返回当前 CPU 的idle进程(空循环占用 CPU,避免 CPU 处于无任务状态)。

函数原型(以 Linux 5.10 为例)

static inline struct task_struct * pick_next_task(struct rq *rq, struct task_struct *prev, bool preempt)
  • 参数
    • rq:指向当前 CPU 的运行队列(runqueue),所有可运行进程都挂载在此队列中。
    • prev:指向当前即将被换下 CPU 的进程(上一个运行进程)。
    • preempt:标记是否为抢占式调度(true表示高优先级进程抢占当前进程,false表示当前进程主动放弃 CPU)。
  • 返回值:指向挑选出的下一个待运行进程的task_struct指针(绝不会返回NULL,最坏返回idle进程)。

核心实现流程(基于 Linux 5.10)

pick_next_task()的核心逻辑是「高优先级调度类优先遍历 + 专属调度类内挑选」,简化版代码如下:

static inline struct task_struct * pick_next_task(struct rq *rq, struct task_struct *prev, bool preempt) { const struct sched_class *class; struct task_struct *next; // 快速路径优化:优先检查上一个进程的调度类(大部分场景下,下一个进程仍属于同一调度类) // 减少遍历所有调度类的开销,提升调度效率 if (likely(prev->sched_class == &fair_sched_class || prev->sched_class == &idle_sched_class)) { // 1. 先检查高优先级调度类(DL > RT) class = sched_class_highest; // 指向优先级最高的调度类(DL 硬实时) while (class != &fair_sched_class) { // 遍历到 CFS 调度类为止 // 调用该调度类的 pick_next_task 方法,挑选进程 next = class->pick_next_task(rq, prev, preempt); if (next) // 若挑选到有效进程,直接返回 return next; // 切换到下一个优先级更低的调度类 class = class->next; } // 2. 检查 CFS 普通调度类(大部分进程属于此类) next = fair_sched_class.pick_next_task(rq, prev, preempt); if (next) return next; // 3. 若快速路径无结果,进入慢速路径(遍历所有调度类) class = sched_class_highest; } else { // 慢速路径:上一个进程属于特殊调度类,直接遍历所有调度类 class = prev->sched_class->next; } // 慢速路径:从最高优先级调度类开始,依次遍历所有调度类 for (; class; class = class->next) { next = class->pick_next_task(rq, prev, preempt); if (next) return next; } // 兜底:无任何可运行进程,返回当前 CPU 的 idle 进程 return idle_task(rq); }
  1. 调度类优先级顺序 :内核预设的调度类优先级从高到低为:DL_SCHED_CLASS(硬实时调度类)>RT_SCHED_CLASS(软实时调度类)>CFS_SCHED_CLASS(完全公平调度类,普通进程默认)>IDLE_SCHED_CLASS(空闲调度类)

    • DL:用于要求绝对截止时间的任务(如工业控制、航空航天),优先级最高,一旦有可运行进程,直接抢占其他所有进程。
    • RT:用于要求快速响应的任务(如音频、视频播放),优先级高于普通进程,支持 99 个实时优先级(0-98,数值越大优先级越高)。
    • CFS:用于普通用户态进程(如bashchrome),通过vruntime(虚拟运行时间)保证所有进程公平获得 CPU 时间。
    • IDLE:仅当无其他可运行进程时运行,负责执行 CPU 空闲时的低功耗操作。
  2. 快速路径与慢速路径

    • 快速路径:大部分场景下,当前进程是CFSIDLE类进程,下一个进程也大概率属于CFS类,优先遍历DLRTCFS,减少遍历开销,提升调度效率(占调度场景的 90% 以上)。
    • 慢速路径:当当前进程是DLRT类特殊进程时,直接遍历所有调度类,确保不遗漏高优先级进程。
  3. 专属调度类的pick_next_task:实现pick_next_task()本身只是 “调度员”,真正的 “挑选逻辑” 由各调度类自身实现:

    • CFS 调度类:核心逻辑是从运行队列的红黑树中,找到vruntime(虚拟运行时间)最小的进程(红黑树的最左节点),保证 “先到先得” 的公平性。关键代码逻辑:
      struct task_struct *pick_next_task_fair(struct rq *rq, struct task_struct *prev, bool preempt) { struct sched_entity *se; struct task_struct *next; // 找到红黑树中 vruntime 最小的调度实体 se = pick_next_entity(rq, &rq->cfs); // 转换为 task_struct 进程指针 next = task_of(se); return next; }
    • RT 调度类:核心逻辑是遍历实时优先级链表(0-98),从最高优先级链表中找到第一个可运行进程(链表头节点),保证高优先级实时进程优先执行。关键代码逻辑:
      struct task_struct *pick_next_task_rt(struct rq *rq, struct task_struct *prev, bool preempt) { struct rt_prio_array *array = &rq->rt.rt_prio_array; int prio; // 从最高优先级(98)开始遍历链表 for (prio = MAX_RT_PRIO - 1; prio >= 0; prio--) { if (!list_empty(&array->queue[prio])) { // 返回链表中的第一个进程 return list_first_entry(&array->queue[prio], struct task_struct, rt_entry); } } return NULL; }
    • IDLE 调度类:直接返回当前 CPU 的idle进程,无复杂挑选逻辑。
  4. 兜底返回idle进程 :若所有调度类都无可用进程,最终返回idle_task(rq)(当前 CPU 的空闲进程),该进程会执行空循环或低功耗指令(如 ARM64 的wfe),直到有新进程被唤醒加入运行队列。

函数的关联(schedule()/wake_up_*

  1. wake_up_new_task()/wake_up_process():将进程加入对应调度类的运行队列(红黑树 / 优先级链表),为pick_next_task()提供 “候选进程池”。
  2. pick_next_task():在schedule()执行过程中,从 “候选进程池” 中挑选最优进程,是schedule()的 “决策核心”。
  3. schedule():接收pick_next_task()返回的候选进程,完成上下文切换,让该进程获得 CPU 执行权。

简单说:wake_up_*是 “把选手送进对应赛道”,pick_next_task()是 “从所有赛道中挑选最有资格上场的选手”,schedule()是 “让选手上场比赛”。

架构相关差异(以 ARM64 为例)

pick_next_task()的核心逻辑(调度类遍历、进程挑选)是架构无关的,与架构相关的差异仅体现在:

  1. 运行队列(rq)的获取:ARM64 异构架构(大小核)中,每个 CPU 对应独立的rqpick_next_task()仅操作当前 CPU 的rq,确保进程在指定核组内被挑选。
  2. idle进程的实现:ARM64 的idle进程会根据 CPU 型号(大核 / 小核)执行不同的低功耗指令(如wfi/wfe),pick_next_task()仅负责返回该idle进程,不参与低功耗逻辑。
  3. 缓存优化:ARM64 中,pick_next_task()挑选进程时,会间接优先选择与当前进程共享 L2/L3 缓存的进程(由 CFS 调度类的vruntime优化实现),减少缓存失效开销。

常见问题与注意事项

  1. 实时进程饥饿普通进程DL/RT调度类优先级高于CFS,若实时进程长期占用 CPU,会导致普通CFS进程 “饥饿”(无法获得 CPU 执行权),需合理设置实时进程的运行时间。
  2. CFS 红黑树失衡:若频繁创建 / 销毁CFS进程,可能导致红黑树失衡,影响pick_next_task()的挑选效率,内核已通过优化红黑树插入 / 删除逻辑缓解该问题。
  3. 异构架构调度不均:ARM64 大小核架构中,若未合理配置进程的 CPU 亲和性,pick_next_task()可能将 CPU 密集型进程调度到小核,导致性能瓶颈,需通过sched_setaffinity()绑定进程到大核。

总结

  1. pick_next_task()是调度器的决策核心,核心职责是按调度类优先级从运行队列中挑选最优下一个进程。
  2. 核心逻辑分为快速路径(优先遍历 CFS 相关调度类)和慢速路径(遍历所有调度类),兼顾效率与完整性。
  3. 具体挑选逻辑由各调度类专属实现(CFS 选最小vruntime,RT 选最高优先级),最终兜底返回idle进程。
  4. 它是schedule()与各调度类之间的桥梁,不负责上下文切换,仅负责 “选进程”。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/24 13:05:47

笔记本购物商城|基于java+ vue笔记本购物商城系统(源码+数据库+文档)

笔记本购物商城 目录 基于springboot vue笔记本购物商城系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取: 基于springboot vue笔记本购物商城系统 一、前言 博主介绍…

作者头像 李华
网站建设 2026/5/24 13:53:32

TypeScript学习-第8章:高级类型

TypeScript学习-第8章:高级类型 上一章咱们搞定了泛型这个“类型复用神器”,以为能在TS世界横着走了?可一碰到复杂业务场景就懵了:既要让变量支持多种类型,又要精准区分类型做不同操作;既要合并多个接口的属…

作者头像 李华
网站建设 2026/5/20 22:48:46

技术演进中的开发沉思-351:并发模型(下)

今天深入理解 Java 的这两种执行方式(解释执行、JIT 编译执行),以及 HotSpot 里 C1、C2 编译器的核心区别和工作逻辑,我会从执行原理、适用场景、协同工作方式三个维度讲清楚,新手也能轻松理解。 一、先理清核心概念 …

作者头像 李华
网站建设 2026/5/20 23:46:44

【含文档+PPT+源码】基于微信小程序的猎兔汽车保养维修美容服务平台的设计与实现

项目介绍本课程演示的是一款基于微信小程序的猎兔汽车保养维修美容服务平台的设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料2.带你从零开始部署运…

作者头像 李华
网站建设 2026/5/23 1:39:18

C语言对话-21.模板特化,缺省参数和其他一些有趣的事情

amature 翻译 我们监视着不远处的大门,生怕从木卫二基地尾随而来的入侵者会跟进来。但看来他们很满足于让我们呆在里面,或至少等我们自己出来。大门那边依然静悄悄一片。 不管我们是多么警戒,大笨蛋们仍然占领了我们的许多岗哨。我们杀死了很…

作者头像 李华
网站建设 2026/5/23 3:38:44

<span class=“js_title_inner“>年终总结 | AI 正在光速进化,而我们还得在 2026 年的泥潭里挣扎</span>

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / 卷圈 监制 / 姝琦 封面 / 姝琦 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 # 在技术狂飙突进与宏观经济承压的交织点上,企业与个人都需要抛弃“上升期惯性”,寻找新的生存锚点…

作者头像 李华