拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Linux 进程调度全景图:task_struct 与 sched_entity 两大结构体完整解析

Linux 进程调度全景图task_struct 与 sched_entity 两大结构体完整解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux你一边渲染 4K 视频、一边开着十几个浏览器标签页机器却丝般顺滑——这是谁在幕后给 CPU 排班答案就藏在 Linux 内核的进程调度核心里每个进程在内核中都有两份证件一份是描述一切的进程描述符task_struct另一份是调度器真正打分用的调度实体sched_entity。理解这两个结构体的分工你就看懂了 Linux 如何把一块 CPU 公平地分给成百上千个进程。本文围绕 Linux 内核源码include/linux/sched.h与 kernel/sched/fair.c用最小的必要代码量讲清字段含义、CFS 调度器的运转方式、如何用 3 条命令亲眼验证、以及参数调优的常见坑。一张身份证加一份简历先分清两个结构体一句话结论task_struct是进程的完整档案几千个字节涵盖内存、文件、信号、权限……而sched_entity只是其中一页调度简历——调度器做决策时只看这一页其余一概不关心。task_struct定义在 include/linux/sched.h从 835 行开始结构体横跨数百行是与调度相关的几个关键字段字段作用源码位置__state进程当前状态运行/睡眠/被追踪…调度器判断它能不能跑的第一依据include/linux/sched.h#L843pid/tgid线程 ID / 进程组 ID就是你在ps里看到的编号include/linux/sched.h#L1080-L1081prio当前实际优先级可被动态调整include/linux/sched.h#L884normal_prio静态优先级基准include/linux/sched.h#L886se内嵌的sched_entityCFS 调度器只看它include/linux/sched.h#L889注意se不是指针而是直接嵌在task_struct里的成员——每个普通进程天生自带一份调度简历无需额外分配。同结构里还有sched_rt_entity实时进程用和sched_dl_entity带截止时间的进程用一套task_struct配三张不同岗位的简历。进程状态定义在 include/linux/sched.h#L107-L127最常用的是这三个// include/linux/sched.h (L107-L109) #define TASK_RUNNING 0x00000000 // 就绪等着上 CPU #define TASK_INTERRUPTIBLE 0x00000001 // 可中断睡眠能被打断 #define TASK_UNINTERRUPTIBLE 0x00000002 // 不可中断睡眠关键操作不许打扰状态在任务生命周期里这样流转sched_entity 字段逐个拆CFS 打分的依据全在这一句话结论sched_entity只有几十个字段却装下了 CFS完全公平调度器做决策所需的全部信息权重、虚拟运行时间、负载平均。定义见 include/linux/sched.h#L572-L619挑出必读的字段大白话解释load权重。同样跑 1 秒权重 2 的进程消耗两倍配额nice值就是用来调它的vruntime虚拟运行时间按权重折算后的已享受时间永远最小者先跑CFS 的核心排序键min_slice/max_slice/slice时间片上下限与当前值当前内核的 EEVDF 演进版vlag近似虚拟滞后这个进程欠了 CPU 多少时间抢占判断的关键sum_exec_runtime累计真实运行时长/proc/pid/sched里能看到run_node红黑树节点进程就绪时挂在调度队列的红黑树上avgPELT 负载平均值见下avg里的load_avg、util_avg由PELT利用指数加权移动平均算法滚动计算定义在 include/linux/sched.h#L510-L520——它让调度器能回答这个进程最近有多忙从而决定要不要把它迁移到空闲 CPU 上。 一个值得注意的细节这份源码里的sched_entity已经带上了min_vruntime、vlag、slice等字段说明 CFS 正处在EEVDF最早到期虚拟截止优先的演进过程中——vruntime从排序键变成了截止时间的输入公平性目标没变机制升级了。动态还原调度器 1 毫秒内做了什么一句话结论CFS 把就绪进程挂进一棵以vruntime为键的红黑树每次需要 CPU 时取树的最左节点vruntime 最小者运行进程跑完一段后更新vruntime再插回树中。核心逻辑在 kernel/sched/fair.c配合睡眠唤醒看整体进程等待资源时进入睡眠态事件就绪后被唤醒回到运行态重新参与上面这个循环。以内核中经典的poll等待唤醒路径为例流程与调度唤醒共用同一套睡眠→唤醒骨架等待队列wait_queue_head_t维护着所有睡在同一个资源上的进程唤醒时逐一拉回就绪队列小结调度不是轮流坐庄而是谁欠 CPU 最多谁先跑。高权重进程 vruntime 涨得慢所以自然获得更多时间——公平由此产生。动手验证3 条命令看懂调度行为无需编译内核普通用户即可观察# 1. 看 1 号进程的调度简历权重、优先级、累计运行时间 cat /proc/1/sched # 2. 实时监控一个忙进程观察 vruntime 与 prio 变化 watch -n 1 grep -E se.vruntime|prio /proc/$(pgrep -n stress)/sched # 3. 看 CFS 当前调优参数 sysctl kernel.sched_latency_ns kernel.sched_min_granularity_ns第 1 条命令的输出里se.avg.load_avg、se.avg.util_avg就是上文 PELT 算法的实时结果prio 120表示这是 nice 值为 0 的普通进程。调优与避坑CFS 参数怎么动才安全参数含义调优建议kernel.sched_latency_ns目标延迟所有就绪进程在一轮里都应跑到的总时间低延迟场景调小吞吐场景调大kernel.sched_min_granularity_ns最小时间片调小可让交互更跟手但上下文切换开销上升kernel.sched_wakeup_granularity_ns唤醒抢占阈值决定新唤醒进程多激进地抢占当前进程进程nice值直接改se.load权重后台批处理任务给nice 19比全局改参更精准三个常见坑别拿nice解决卡顿卡顿多发生在 I/O 等待D状态调度参数根本帮不上忙先确认进程到底在睡眠还是在排队。实时优先级是核武器SCHED_FIFO优先级一旦给高会饿死所有普通 CFS 进程误用可致整机无响应。改参数前先sysctl -a | grep sched记录原值出问题一条命令即可回滚。进阶路线资源清单按先头文件、后实现、再看文档的顺序读结构体定义include/linux/sched.htask_struct、sched_entity、状态位掩码优先级换算include/linux/sched/prio.hnice→ 权重映射表CFS 实现kernel/sched/fair.cenqueue_entity、pick_next_task_fair、update_curr调度核心上下文切换kernel/sched/core.c官方设计文档Documentation/scheduler/sched-design-CFS.rst调试接口/proc/pid/sched的实现kernel/sched/debug.c【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门