Linux内核修改进程nice优先级后CPU时间片未生效的原因排查
你遇到的核心问题是:虽然修改了task_struct的static_prio和prio,但CFS调度器用于分配CPU时间片的核心参数——sched_entity.load.weight并没有被更新。
为什么修改static_prio不影响时间片分配?
在Linux 5.3.18的CFS调度器中,进程的CPU时间片分配实际上由sched_entity(调度实体)的load.weight决定,而这个权重值是基于static_prio计算的,但只会在特定时机初始化或更新:
load.weight的初始化时机
在进程创建流程中,__sched_fork()会调用init_sched_entity(),该函数在kernel/sched/core.c中定义,其中有关键代码:se->load.weight = prio_to_weight[p->static_prio - MAX_RT_PRIO]; se->load.inv_weight = prio_to_wmult[p->static_prio - MAX_RT_PRIO];这里直接使用当前
p->static_prio的值(此时还是继承自父进程的默认值)计算负载权重,这个初始化发生在你修改static_prio的代码之前。你的修改时机太晚
你选择在sched_fork()中修改static_prio,但此时init_sched_entity()已经执行完毕,load.weight已经用原始的static_prio计算完成。后续修改static_prio并不会自动同步更新load.weight,所以调度器依然按照旧的权重分配时间片。getpriority()返回正确值的原因
用户态的getpriority()是直接读取task_struct.static_prio并转换为nice值(转换公式:nice = 20 - (static_prio - MAX_RT_PRIO)),所以你的修改能被getpriority()正确感知,但这和调度器实际使用的负载权重无关。
解决方法
你需要确保修改static_prio后,同步更新load.weight,或者提前修改static_prio让初始化时就使用新值:
方案1:修改后手动更新负载权重
在sched_fork()中修改完static_prio后,调用内核提供的set_load_weight()函数重新计算负载权重:
if(fair_policy(p->policy)) { p->static_prio = p->normal_prio = p->prio = 117; set_load_weight(p); // 同步更新load.weight和inv_weight }
set_load_weight()函数会根据新的static_prio重新计算并设置se->load.weight和se->load.inv_weight,确保调度器使用新的权重值。
方案2:提前修改static_prio
将修改static_prio的代码移到copy_process()中调用sched_fork()之前。在kernel/fork.c的copy_process()函数里,原本有一行:
p->static_prio = current->static_prio;
你可以在这行之后加入你的判断逻辑,修改p->static_prio,这样后续调用sched_fork()时,init_sched_entity()会直接使用新的static_prio计算负载权重。
验证修改效果
修改后重新编译内核,除了用getpriority()验证nice值,还可以通过以下方式确认负载权重更新:
- 查看
/proc/[pid]/sched文件中的se.load.weight字段,确认其值与prio_to_weight[17]对应(因为117-100=17,prio_to_weight[17]的值是2048,对应nice=-3)。 - 使用
chrt -p [pid]查看进程优先级,或者通过实际的CPU占用测试(比如用CPU密集型进程对比默认优先级和修改后优先级的CPU分配比例)。
内容的提问来源于stack exchange,提问作者user1534664

