ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

看懂Linux进程调度的2个核心结构:task_struct与sched_entity如何决定谁先跑

看懂Linux进程调度的2个核心结构:task_struct与sched_entity如何决定谁先跑 看懂Linux进程调度的2个核心结构task_struct与sched_entity如何决定谁先跑【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux你正在导出一段4K视频渲染进程把CPU吃满可浏览器还能流畅滑动、微信照样弹出新消息。这不是巧合背后是Linux内核的进程调度器在工作它把有限的CPU时间在成百上千个进程之间切分、抢占、归还。而整个调度的账本只记在两个数据结构上task_struct进程的完整档案和sched_entity调度器手里的排序依据。本文用一个进程的一生串起它们。进程刚出生时内核记下了哪些东西你可以把task_struct想象成一本户籍档案。每个进程被fork()创建的那一刻内核就为它分配了一本这样的档案大名、编号、此刻是在岗还是请假、和谁同属一个家庭全都写死在第一页。技术上说它是内核中描述一个进程或线程的完整结构体定义在 include/linux/sched.h 的第835行从第843行的__state一直延伸到上千行之后是内核里最庞大的结构体之一。档案里和调度直接相关的字段值得你单独看这几处字段记的是什么位置__state进程此刻的状态位在跑还是在睡include/linux/sched.h 第843行pid/tgid进程编号与进程组编号第1080~1081行prio当前优先级数字越小越优先第884行se调度实体调度器真正读的那一页第889行comm进程名就是你在top里看到的名字第1190行状态字段__state是调度的第一道门槛。内核给它编了明确的位标志第107~109行TASK_RUNNING0x0、TASK_INTERRUPTIBLE0x1、TASK_UNINTERRUPTIBLE0x2再加上停止、冻结、僵尸等状态位组合。一个进程的完整状态流转大致长这样注意两条边的区别可中断睡眠随时能被信号拽回来不可中断睡眠则是这件事办完之前谁都别碰。你上方那张poll流程图演示的就是这条链路——进程查不到事件就睡进队列事件一到驱动调wake_up()把它从睡眠拉回TASK_RUNNING重新参与排队。但档案本身并不直接决定排序。调度器翻遍整本档案只为找一样东西那就是第889行嵌在档案里的se。调度器凭什么决定谁先跑 如果 task_struct 是户籍档案sched_entity就是你去银行取的那张排队号。柜员从不查看你的身份证他只盯着取号机上的数字决定叫谁。CFS完全公平调度器做决定时读的也全是这个号。它的定义在 include/linux/sched.h 第572行摘掉与主题无关的字段后是这样struct sched_entity { struct load_weight load; /* 权重nice值最终折算到这里 */ struct rb_node run_node; /* 红黑树节点挂在CFS队列上 */ u64 vruntime; /* 虚拟运行时间排序的核心 */ u64 sum_exec_runtime; /* 累计真实运行时长 */ struct sched_avg avg; /* PELT负载均值指数加权滑动平均 */ };它和 task_struct 是什么关系回看上一节的表格——se不是指针而是内嵌成员一个进程的档案里直接焊着一张取号纸一对一、随档案同生共死实时进程另有rt截止时间进程另有dl档案第890~891行。所以调度器的视角被裁剪得很干净档案里的__state决定你有没有资格站进队列而取号纸上的数字才决定谁先被叫到。排序规则就一行话vruntime 最小者先跑。vruntime 是按权重折算后的运行时间——你权重越高nice 越负同样一秒的真实运行折算出的虚拟时间就越少号涨得越慢自然更容易再次被叫到。CFS 把所有可运行实体的run_node挂在一棵以 vruntime 为键的红黑树上每次切CPU直接取最左节点pick_next_task_fair()见 kernel/sched/core.c当前进程跑着的时候update_curr()kernel/sched/fair.c 第2024行持续给它加 vruntime。负载感知则交给avg字段struct sched_avg第510行用注释里明说的无穷几何级数累积最近窗口的负载权重表struct load_weight第460行提供 nice→weight 的换算。这就是档案与取号纸的配合全过程——档案管进出取号纸管次序。一条命令看清进程调度的现场调度不是黑盒/proc 把账本摊开了给你看。挑三个进程比如渲染那个依次敲cat /proc/pid/sched # 单个进程的调度账本 cat /proc/schedstat | head -8 # 每个CPU的运行次数/时长/切换 sysctl kernel.sched_latency_ns kernel.sched_min_granularity_ns输出字段对应源码怎么读se.vlag/se.sum_exec_runtimesched_entity 第592~590行虚拟滞后与真实累计运行时间判断它亏没亏prio/normal_priotask_struct 第884~886行当前与正常优先级数字小者优先nr_migrationssched_entity 第599行进程被搬到过多少颗CPU频繁迁移说明在流浪schedstat第二列内核统计该CPU累计切换次数突增意味着抢占密集如果prio一直偏大、sum_exec_runtime涨得慢大概率是优先级或 nice 值的问题如果nr_migrations疯涨多半是CPU亲和性或争抢问题。先定位再谈调优。顺着这些线索继续挖相对路径里面有什么include/linux/sched.htask_struct 与 sched_entity 的完整定义kernel/sched/core.c唤醒与pick_next_task_fair()选择路径kernel/sched/fair.cCFS 队列逻辑update_curr()在2024行kernel/sched/pelt.cPELT 负载均值的具体累积实现Documentation/scheduler/sched-design-CFS.rstCFS 设计文档红黑树与 vruntime 的原始推导Documentation/scheduler/sched-eevdf.rst下一代 EEVDF 调度算法的设计说明Documentation/scheduler/sched-debug.rst调度统计与调试开关的说明从sched-design-CFS.rst的推导入手再对照fair.c里的红黑树操作你会发现取号纸上的每个字段都有出处。而 EEVDF 那篇文档能告诉你这套号接下来怎么演化——它给每个进程补上了截止时刻让延迟敏感任务不再被长时间IO型任务拖累。调度的本质不过两件事档案决定谁能排队取号纸决定谁先上。 下次机器卡顿时别再凭感觉猜先cat /proc/pid/sched看一眼 prio 和 se 的账目——五分钟定位比重启管用得多。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表