简介在服务器虚拟化、容器云、边缘嵌入式集群场景中多业务进程混部是线上环境常态。传统基于进程 nice 的权重调节仅能管控单进程 CPU 占比无法实现批量业务整体资源隔离例如一台物理机同时部署数据库、业务服务、日志采集三类进程数据库突发满载时极易挤占其他业务 CPU 资源引发接口超时、业务雪崩。Linux 在 CFS 完全公平调度器基础上依托CONFIG_FAIR_GROUP_SCHED配置开启组调度机制通过 cgroup CPU 子系统把多个进程归集为 task_group 任务组依靠shares相对权重完成整组 CPU 时间片分配控制。用户层修改 cgroup 的cpu.shares(cgroup v1)/cpu.weight(cgroup v2)配置项时内核底层统一由sched_group_set_shares作为核心入口函数完成权重落地实时刷新全 CPU 运行队列中组调度实体权重动态变更各组 CPU 资源分配比例无需重启进程、无需重载内核即可在线调优资源配比。从落地价值来看云原生运维工程师依靠该机制完成容器资源配额动态扩容缩容内核研发人员依托该函数源码理解 CFS 分层调度架构嵌入式工程师在工控多任务场景实现核心任务资源优先保障。吃透sched_group_set_shares调用链路、源码实现、负载刷新逻辑是容器资源管控、CFS 内核二次裁剪、线上 CPU 抢占异常排查的必备基础。本文从原理、环境、内核源码、实操验证、排错、工程优化全链路落地内容可直接用于学术论文数据佐证、项目技术方案编写。一、核心概念与术语解析1.1 CFS 组调度基础架构CFS 组调度打破单进程调度粒度采用分层树形调度根任务组root_task_group为系统顶层节点所有自建 cgroup 任务组作为子节点挂靠在根节点下子组还可继续嵌套创建下级分组形成多级调度树。内核配置CONFIG_FAIR_GROUP_SCHEDy后组调度生效对应关闭则退化为传统单进程 CFS 调度The Linux Kernel Archives。1.2 task_group 任务组结构体每一个 cgroup CPU 分组在内核映射一个struct task_group结构体是组调度管理载体关键成员定义// kernel/sched/sched.h struct task_group { /* 当前任务组基础shares权重用户修改cpu.shares最终落地此字段 */ unsigned long shares; /* 每个CPU对应一个组调度实体setask_group[n_cpu]-se[n_cpu] */ struct sched_entity **se; /* 每个CPU对应组CFS运行队列 */ struct cfs_rq **cfs_rq; /* 组带宽控制用于cfs_quota硬限制与shares相对权重无关 */ struct cfs_bandwidth cfs_bandwidth; };shares组基准权重默认新建任务组数值 1024仅代表相对比例不是固定 CPU 核数se[]调度实体每个 CPU 维护一个组级sched_entity代表本任务组在对应 CPU 上参与上层调度cfs_rq[]组私有 CFS 就绪队列组内所有普通进程的 se 挂载到此队列形成 “进程 se→组 cfs_rq→组 se→父组 cfs_rq” 分层调度链路。1.3 shares 权重分配规则shares 为相对权重CPU 空闲时资源不做限制多组同时满载争抢 CPU 时各组占用 CPU 时长 本组 shares / 所有就绪组 shares 总和。 示例GroupA1024、GroupB2048两组满载时 A 占 1/3、B 占 2/3若新增 GroupC1024总 shares4096A:B:C1:2:1。cgroup 版本换算v1cpu.shares(1~UINT_MAX默认1024)v2cpu.weight(1~10000默认100)内核内部统一换算为 sharesshares weight*1024/100最终全部流入sched_group_set_shares处理。1.4 sched_group_set_shares 函数定位用户写cpu.shares文件触发内核文件操作回调最终调用__sched_group_set_shares外层封装sched_group_set_shares作为对外统一接口核心工作更新task_group-shares基准权重遍历整机所有 CPU逐个刷新对应 CPU 上本组se调度实体权重调用update_cfs_group()逐级向上刷新父组负载与权重实时生效新配比。1.5 关联辅助函数update_cfs_shares()单 CPU 维度依据 task_group 基准 shares、当前 CPU 组负载计算实际生效权重reweight_entity()修改 sched_entity 权重调整 CFS 红黑树排序位置触发调度路径更新。二、环境准备2.1 软硬件环境清单环境项参数说明OS 系统Ubuntu20.04/22.04 x86_64内核 5.15、6.1 LTS主流云服务器标配内核CPUx86_64 4 核及以上方便多任务压测验证 CPU 占比内存≥4GB编译工具链gcc-9/gcc-11、make、bison、flex、libncurses-dev、libelf-dev调试工具ftrace、perf、trace-cmd、sysstat (iostat/mpstat)、stress-ng压测进程内核关键配置CONFIG_FAIR_GROUP_SCHEDy、CONFIG_CGROUP_CPUy、CONFIG_FTRACEy、CONFIG_SCHED_DEBUGy2.2 环境部署步骤步骤 1安装依赖工具可全量复制执行# 更新源并安装编译、调试、压测全套依赖 sudo apt update -y sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev stress-ng perf trace-cmd sysstat -y步骤 2内核源码下载与编译配置# 下载Linux6.1长期支持内核 wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz tar -xf linux-6.1.tar.xz cd linux-6.1 # 沿用当前系统内核配置 cp /boot/config-$(uname -r) .config # 打开配置界面 make menuconfigmake menuconfig中开启下述选项General setup → Control Group support → CPU controller for cgroupsy Kernel features → Enable the CFS group scheduling(CONFIG_FAIR_GROUP_SCHED)y Kernel hacking → Tracers → Function tracer(CONFIG_FTRACE)y Kernel hacking → Debug scheduler(CONFIG_SCHED_DEBUG)y保存退出后编译安装内核make -j$(nproc) sudo make modules_install sudo make install sudo update-grub重启服务器在 grub 菜单选中新编译内核进入系统。步骤 3挂载 cgroup v1 文件系统实操使用 v1接口直观# 临时挂载cpu子系统永久生效写入/etc/fstab sudo mkdir -p /sys/fs/cgroup/cpu sudo mount -t cgroup -o cpu none /sys/fs/cgroup/cpu挂载完成后/sys/fs/cgroup/cpu目录下自动生成cpu.shares、cpu.cfs_quota_us等配置文件。2.3 源码目录定位sched_group_set_shares及相关实现全部在kernel/sched/fair.ctask_group 结构体kernel/sched/sched.hcgroup 文件回调函数kernel/sched/core.c三、应用场景302 字sched_group_set_shares驱动的动态权重能力是云平台混部资源管控的底层基石。容器 K8s 集群中运维通过修改 Deployment 资源参数间接调整 pod 对应 cgroup 的 cpu.weight内核经由该函数实时变更容器组 shares实现生产业务容器权重高于测试容器高峰期优先保障核心业务 CPU 资源中小型数据库服务器混部场景MySQL 分组 shares 设为 4096、日志采集分组 1024业务峰值自动按 4:1 分配 CPU避免日志进程抢占 SQL 算力嵌入式工控多任务系统中运动控制任务组调高 shares后台日志任务降低权重依靠动态调整满足设备硬实时调度需求云主机弹性扩容场景无需重启实例在线上调业务 cgroup 权重即可瞬时提升 CPU 分配占比全场景依托sched_group_set_shares完成权重热更新。四、实际案例与步骤内核源码 用户态实操双案例全注释可复现案例一内核源码深度解析 sched_group_set_shares 执行逻辑4.1.1 顶层入口 sched_group_set_shares 源码// kernel/sched/fair.c int sched_group_set_shares(struct task_group *tg, unsigned long shares) { int i; struct rq_flags rf; struct rq *rq; /* 入参合法性校验shares最小不能为0内核默认下限MIN_SHARES1 */ if (shares MIN_SHARES) return -EINVAL; /* 根任务组root_task_group不允许修改shares返回非法参数 */ if (tg root_task_group) return -EINVAL; /* 上锁保护task_group成员防止并发修改 */ mutex_lock(tg-shares_mutex); /* 新权重与原有一致则直接返回跳过无效刷新优化性能 */ if (tg-shares shares) { mutex_unlock(tg-shares_mutex); return 0; } /* 第一步更新task_group基准shares值落盘组基础权重 */ tg-shares shares; mutex_unlock(tg-shares_mutex); /* 第二步遍历系统全部CPU逐个刷新每个CPU上本组调度实体se权重 */ for_each_possible_cpu(i) { /* 获取第i号CPU运行队列 */ rq cpu_rq(i); rq_lock(rq, rf); /* 调用更新函数计算当前CPU环境下组实际生效权重 */ update_cfs_shares(tg-se[i], tg-cfs_rq[i]); rq_unlock(rq, rf); } return 0; }代码说明用户echo 2048 cpu.shares→ cgroup 回调cpu_shares_write_u64解析数值→调用sched_group_set_shares(tg,2048)只有权重发生变化才执行全 CPU 遍历刷新避免频繁写文件带来无效内核开销for_each_possible_cpu遍历所有配置 CPU包括离线 CPU保证 CPU 上下线后权重配置依然生效。4.1.2 关键附属函数 update_cfs_shares reweight_entitystatic void update_cfs_shares(struct sched_entity *se, struct cfs_rq *cfs_rq) { struct task_group *tg cfs_rq-tg; unsigned long new_weight; /* 根据组基准shares、当前队列总负载换算本CPU上se实际权重 */ new_weight calc_group_shares(cfs_rq); /* 新旧权重不一致修改调度实体权重调整CFS红黑树位置 */ if (se-load.weight ! new_weight) { reweight_entity(cfs_rq, se, new_weight); } /* 向上递归刷新父任务组权重实现多级分组联动生效 */ if (cfs_rq_is_group_rq(cfs_rq)) update_cfs_group(se); } /* 重新设置se权重内核CFS调度依据load.weight分配时间片 */ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, unsigned long weight) { /* 从红黑树临时摘除节点 */ if (se-on_rq) rb_erase(se-rb_node, cfs_rq-tasks_timeline); /* 更新调度实体权重 */ se-load.weight weight; /* 重新插入红黑树权重变化带来排序变更 */ if (se-on_rq) rb_add(se-rb_node, cfs_rq-tasks_timeline); }代码逻辑总结修改 shares→更新 tg 基准值→全 CPU 刷新 se 权重→变更 se 在 CFS 红黑树位置→调度选任务时按新权重分配 CPU 时间整套流程同步完成。案例二用户态实操通过修改 cpu.shares 触发 sched_group_set_shares压测验证资源占比步骤 1创建两个 cgroup 分组 group1、group2# 进入cpu子系统挂载目录 cd /sys/fs/cgroup/cpu # 创建两个任务组目录内核自动生成对应cpu.shares等文件 sudo mkdir group1 group2 # 查看默认shares默认新建分组1024 cat group1/cpu.shares cat group2/cpu.shares步骤 2编写 CPU 满载测试小程序无限消耗 CPU可直接编译运行// cpu_stress.c 死循环占用单核CPU #include stdio.h int main(void) { while(1){ ; // 空循环持续消耗CPU算力 } return 0; }编译命令gcc cpu_stress.c -o cpu_stress步骤 3启动两个测试进程分别加入两个分组# 后台启动第一个进程获取PID ./cpu_stress echo $! group1/cgroup.procs # 后台启动第二个进程 ./cpu_stress echo $! group2/cgroup.procs步骤 4修改 shares触发内核 sched_group_set_shares 执行# group1权重1024group2权重3072理论满载CPU占比1:3 sudo echo 1024 group1/cpu.shares sudo echo 3072 group2/cpu.shares写入文件瞬间内核 cgroup 回调触发sched_group_set_shares(group1,1024)、sched_group_set_shares(group2,3072)实时刷新权重。步骤 5perfftrace 跟踪 sched_group_set_shares 函数调用# 挂载debugfs用于ftrace sudo mount -t debugfs none /sys/kernel/debug # 清空跟踪缓存 sudo echo /sys/kernel/debug/tracing/trace # 配置需要跟踪的内核函数 sudo echo sched_group_set_shares /sys/kernel/debug/tracing/set_ftrace_filter sudo echo update_cfs_shares /sys/kernel/debug/tracing/set_ftrace_filter # 开启函数跟踪 sudo echo function /sys/kernel/debug/tracing/current_tracer sudo echo 1 /sys/kernel/debug/tracing/tracing_on # 再次修改权重触发函数调用 sudo echo 2048 group1/cpu.shares # 关闭跟踪 sudo echo 0 /sys/kernel/debug/tracing/tracing_on # 查看跟踪日志验证函数被调用 cat /sys/kernel/debug/tracing/trace日志预期日志中出现sched_group_set_shares调用栈后续跟随update_cfs_shares、reweight_entity调用记录验证修改 shares 真实走进目标内核函数。步骤 6mpstat 观测 CPU 占用比例# 每秒输出一次CPU统计 mpstat -P ALL 1现象group1 进程 CPU≈25%group2≈75%符合 1:3 权重配比再次修改 shares 后占比瞬时变化验证动态调整生效。步骤 7清理测试环境# 杀掉压测进程 killall cpu_stress # 删除测试分组 sudo rmdir group1 group2案例三cgroup v2 权重转换验证v2 cpu.weight 映射 shares# 挂载cgroup v2 sudo mkdir -p /sys/fs/cgroup2 sudo mount -t cgroup2 none /sys/fs/cgroup2 cd /sys/fs/cgroup2 sudo mkdir testgrp # v2默认weight100修改为200内核自动换算shares200*1024/1002048 echo 200 testgrp/cpu.weight写入后内核底层依旧调用sched_group_set_shares(tg,2048)v1/v2 用户接口不同内核统一收敛至同一个权重设置函数。五、常见问题与解答Q1修改 cpu.shares 后 CPU 占比没有按预期变化答shares 是相对权重仅在分组内进程同时满载争抢 CPU 时才按比例分配CPU 空闲时系统会把空闲算力全部分配给运行进程占比不受 shares 约束。排查使用 stress-ng 打满两个分组 CPU 负载后再观察 mpstat确认满载场景分配比例其次用 ftrace 跟踪sched_group_set_shares是否正常触发若函数无调用说明 cgroup 挂载异常、CONFIG_FAIR_GROUP_SCHED 未开启。Q2修改 shares 后 ftrace 看不到 sched_group_set_shares 调用答①内核未开启 CONFIG_FTRACE、CONFIG_FAIR_GROUP_SCHED②写入的 shares 数值和原有一致内核直接 return 跳过函数执行③cgroup cpu 子系统挂载失效写入文件未走到内核回调。核对zcat /proc/config.gz | grep FAIR_GROUP_SCHED确认配置 y重新挂载 cgroup 后复测。Q3多级嵌套 cgroup子组修改 shares 为何会影响父组调度答update_cfs_shares内部update_cfs_group会向上递归刷新父 task_group 权重内核分层调度下子组负载变化会传导上层分组是sched_group_set_shares设计的联动逻辑符合树形组调度规范如需隔离可拆分独立顶层分组。Q4shares 设置很大65535单个分组能否独占多核 CPU答不能shares 仅控制同 CPU 内分组时间片占比单进程受限于 CPU 单核多进程塞满分组内多线程才可占用多核如需限制核数配合 cpuset 子系统绑定 CPU 核心。Q5根分组 root_task_group 为什么禁止调用 sched_group_set_shares 修改 shares答根组承载系统所有未划入自定义 cgroup 的进程内核固定 root_shares 配置修改会破坏全系统基础调度基准源码中if(tgroot_task_group) return -EINVAL直接拦截入参。六、实践建议与最佳实践6.1 线上业务权重配置规范基准配比生产业务基准 shares4096、测试环境 1024、日志采集 512形成固定梯度高峰期天然优先保障生产资源禁止随意设超大 shares100000避免权重溢出带来内核计算异常。动态调优时机业务流量上涨时在线echo xxx cpu.shares调用sched_group_set_shares热调权重无需重启业务凌晨低峰回落权重节约资源。6.2 内核调试排错技巧权重异常排查链路mpstat 资源异常→ftrace 跟踪sched_group_set_shares调用→gdb/kgdb 读取task_group-shares实际数值→查看对应 CPU 的sched_entity-load.weight三层定位配置是否落进内核。高频改 shares 优化不要循环高频 echo 修改 cpu.shares频繁触发sched_group_set_shares全 CPU 遍历刷新带来不必要调度开销批量变更建议通过程序批量写入单次修改目标值。6.3 内核二次开发优化自研调度策略时尽量复用sched_group_set_shares现有刷新逻辑不要重写全 CPU 遍历代码规避锁、队列同步 BUG如需新增自定义权重字段在 task_group 新增成员沿用原有更新链路。嵌入式精简内核不需要组调度时关闭CONFIG_FAIR_GROUP_SCHED剔除 task_group 与 sched_group_set_shares 相关代码缩减内核体积、降低调度开销。6.4 K8s 容器落地优化K8s 的 resources.cpu 配置最终转化为 cgroup cpu.weight底层依托sched_group_set_shares核心业务容器 requests.cpu 调高对应权重非核心组件调低依托内核原生机制实现混部隔离无需额外第三方资源管控组件。七、总结与应用延伸本文从组调度架构、task_group 结构体、sched_group_set_shares源码实现、用户态 cgroup 实操、函数跟踪调试、线上优化六个维度完整拆解 Linux CFS 任务组权重动态调整机制。sched_group_set_shares是连接用户态 cgroup 配置与内核 CFS 调度的关键桥梁核心设计思路为用户配置→更新组基准 shares→全 CPU 遍历刷新组调度实体权重→修改 CFS 红黑树排序→调度器按新权重分配 CPU 时间依托这套机制实现权重在线热变更也是 cgroup CPU 资源相对配额的内核底层实现。从技术落地层面该函数是云原生容器资源调度、物理机多业务混部、嵌入式工控多任务资源隔离的底层依赖从学术研究角度本源码可用于 CFS 分层调度论文撰写、调度器性能优化课题的数据支撑。建议读者在现有环境基础上修改sched_group_set_shares源码例如增加自定义日志打印重新编译内核再次通过 ftrace 和压测程序验证修改效果深度体会内核权重刷新全链路工程落地中结合 cpusetcpu.sharescpu.cfs_quota 三类配置组合实现 “CPU 核数绑定 相对权重分配 最大算力硬限流” 三位一体资源管控方案。
Linux 组调度的 sched_group_set_shares:任务组权重调整
发布时间:2026/6/3 8:52:02
简介在服务器虚拟化、容器云、边缘嵌入式集群场景中多业务进程混部是线上环境常态。传统基于进程 nice 的权重调节仅能管控单进程 CPU 占比无法实现批量业务整体资源隔离例如一台物理机同时部署数据库、业务服务、日志采集三类进程数据库突发满载时极易挤占其他业务 CPU 资源引发接口超时、业务雪崩。Linux 在 CFS 完全公平调度器基础上依托CONFIG_FAIR_GROUP_SCHED配置开启组调度机制通过 cgroup CPU 子系统把多个进程归集为 task_group 任务组依靠shares相对权重完成整组 CPU 时间片分配控制。用户层修改 cgroup 的cpu.shares(cgroup v1)/cpu.weight(cgroup v2)配置项时内核底层统一由sched_group_set_shares作为核心入口函数完成权重落地实时刷新全 CPU 运行队列中组调度实体权重动态变更各组 CPU 资源分配比例无需重启进程、无需重载内核即可在线调优资源配比。从落地价值来看云原生运维工程师依靠该机制完成容器资源配额动态扩容缩容内核研发人员依托该函数源码理解 CFS 分层调度架构嵌入式工程师在工控多任务场景实现核心任务资源优先保障。吃透sched_group_set_shares调用链路、源码实现、负载刷新逻辑是容器资源管控、CFS 内核二次裁剪、线上 CPU 抢占异常排查的必备基础。本文从原理、环境、内核源码、实操验证、排错、工程优化全链路落地内容可直接用于学术论文数据佐证、项目技术方案编写。一、核心概念与术语解析1.1 CFS 组调度基础架构CFS 组调度打破单进程调度粒度采用分层树形调度根任务组root_task_group为系统顶层节点所有自建 cgroup 任务组作为子节点挂靠在根节点下子组还可继续嵌套创建下级分组形成多级调度树。内核配置CONFIG_FAIR_GROUP_SCHEDy后组调度生效对应关闭则退化为传统单进程 CFS 调度The Linux Kernel Archives。1.2 task_group 任务组结构体每一个 cgroup CPU 分组在内核映射一个struct task_group结构体是组调度管理载体关键成员定义// kernel/sched/sched.h struct task_group { /* 当前任务组基础shares权重用户修改cpu.shares最终落地此字段 */ unsigned long shares; /* 每个CPU对应一个组调度实体setask_group[n_cpu]-se[n_cpu] */ struct sched_entity **se; /* 每个CPU对应组CFS运行队列 */ struct cfs_rq **cfs_rq; /* 组带宽控制用于cfs_quota硬限制与shares相对权重无关 */ struct cfs_bandwidth cfs_bandwidth; };shares组基准权重默认新建任务组数值 1024仅代表相对比例不是固定 CPU 核数se[]调度实体每个 CPU 维护一个组级sched_entity代表本任务组在对应 CPU 上参与上层调度cfs_rq[]组私有 CFS 就绪队列组内所有普通进程的 se 挂载到此队列形成 “进程 se→组 cfs_rq→组 se→父组 cfs_rq” 分层调度链路。1.3 shares 权重分配规则shares 为相对权重CPU 空闲时资源不做限制多组同时满载争抢 CPU 时各组占用 CPU 时长 本组 shares / 所有就绪组 shares 总和。 示例GroupA1024、GroupB2048两组满载时 A 占 1/3、B 占 2/3若新增 GroupC1024总 shares4096A:B:C1:2:1。cgroup 版本换算v1cpu.shares(1~UINT_MAX默认1024)v2cpu.weight(1~10000默认100)内核内部统一换算为 sharesshares weight*1024/100最终全部流入sched_group_set_shares处理。1.4 sched_group_set_shares 函数定位用户写cpu.shares文件触发内核文件操作回调最终调用__sched_group_set_shares外层封装sched_group_set_shares作为对外统一接口核心工作更新task_group-shares基准权重遍历整机所有 CPU逐个刷新对应 CPU 上本组se调度实体权重调用update_cfs_group()逐级向上刷新父组负载与权重实时生效新配比。1.5 关联辅助函数update_cfs_shares()单 CPU 维度依据 task_group 基准 shares、当前 CPU 组负载计算实际生效权重reweight_entity()修改 sched_entity 权重调整 CFS 红黑树排序位置触发调度路径更新。二、环境准备2.1 软硬件环境清单环境项参数说明OS 系统Ubuntu20.04/22.04 x86_64内核 5.15、6.1 LTS主流云服务器标配内核CPUx86_64 4 核及以上方便多任务压测验证 CPU 占比内存≥4GB编译工具链gcc-9/gcc-11、make、bison、flex、libncurses-dev、libelf-dev调试工具ftrace、perf、trace-cmd、sysstat (iostat/mpstat)、stress-ng压测进程内核关键配置CONFIG_FAIR_GROUP_SCHEDy、CONFIG_CGROUP_CPUy、CONFIG_FTRACEy、CONFIG_SCHED_DEBUGy2.2 环境部署步骤步骤 1安装依赖工具可全量复制执行# 更新源并安装编译、调试、压测全套依赖 sudo apt update -y sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev stress-ng perf trace-cmd sysstat -y步骤 2内核源码下载与编译配置# 下载Linux6.1长期支持内核 wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz tar -xf linux-6.1.tar.xz cd linux-6.1 # 沿用当前系统内核配置 cp /boot/config-$(uname -r) .config # 打开配置界面 make menuconfigmake menuconfig中开启下述选项General setup → Control Group support → CPU controller for cgroupsy Kernel features → Enable the CFS group scheduling(CONFIG_FAIR_GROUP_SCHED)y Kernel hacking → Tracers → Function tracer(CONFIG_FTRACE)y Kernel hacking → Debug scheduler(CONFIG_SCHED_DEBUG)y保存退出后编译安装内核make -j$(nproc) sudo make modules_install sudo make install sudo update-grub重启服务器在 grub 菜单选中新编译内核进入系统。步骤 3挂载 cgroup v1 文件系统实操使用 v1接口直观# 临时挂载cpu子系统永久生效写入/etc/fstab sudo mkdir -p /sys/fs/cgroup/cpu sudo mount -t cgroup -o cpu none /sys/fs/cgroup/cpu挂载完成后/sys/fs/cgroup/cpu目录下自动生成cpu.shares、cpu.cfs_quota_us等配置文件。2.3 源码目录定位sched_group_set_shares及相关实现全部在kernel/sched/fair.ctask_group 结构体kernel/sched/sched.hcgroup 文件回调函数kernel/sched/core.c三、应用场景302 字sched_group_set_shares驱动的动态权重能力是云平台混部资源管控的底层基石。容器 K8s 集群中运维通过修改 Deployment 资源参数间接调整 pod 对应 cgroup 的 cpu.weight内核经由该函数实时变更容器组 shares实现生产业务容器权重高于测试容器高峰期优先保障核心业务 CPU 资源中小型数据库服务器混部场景MySQL 分组 shares 设为 4096、日志采集分组 1024业务峰值自动按 4:1 分配 CPU避免日志进程抢占 SQL 算力嵌入式工控多任务系统中运动控制任务组调高 shares后台日志任务降低权重依靠动态调整满足设备硬实时调度需求云主机弹性扩容场景无需重启实例在线上调业务 cgroup 权重即可瞬时提升 CPU 分配占比全场景依托sched_group_set_shares完成权重热更新。四、实际案例与步骤内核源码 用户态实操双案例全注释可复现案例一内核源码深度解析 sched_group_set_shares 执行逻辑4.1.1 顶层入口 sched_group_set_shares 源码// kernel/sched/fair.c int sched_group_set_shares(struct task_group *tg, unsigned long shares) { int i; struct rq_flags rf; struct rq *rq; /* 入参合法性校验shares最小不能为0内核默认下限MIN_SHARES1 */ if (shares MIN_SHARES) return -EINVAL; /* 根任务组root_task_group不允许修改shares返回非法参数 */ if (tg root_task_group) return -EINVAL; /* 上锁保护task_group成员防止并发修改 */ mutex_lock(tg-shares_mutex); /* 新权重与原有一致则直接返回跳过无效刷新优化性能 */ if (tg-shares shares) { mutex_unlock(tg-shares_mutex); return 0; } /* 第一步更新task_group基准shares值落盘组基础权重 */ tg-shares shares; mutex_unlock(tg-shares_mutex); /* 第二步遍历系统全部CPU逐个刷新每个CPU上本组调度实体se权重 */ for_each_possible_cpu(i) { /* 获取第i号CPU运行队列 */ rq cpu_rq(i); rq_lock(rq, rf); /* 调用更新函数计算当前CPU环境下组实际生效权重 */ update_cfs_shares(tg-se[i], tg-cfs_rq[i]); rq_unlock(rq, rf); } return 0; }代码说明用户echo 2048 cpu.shares→ cgroup 回调cpu_shares_write_u64解析数值→调用sched_group_set_shares(tg,2048)只有权重发生变化才执行全 CPU 遍历刷新避免频繁写文件带来无效内核开销for_each_possible_cpu遍历所有配置 CPU包括离线 CPU保证 CPU 上下线后权重配置依然生效。4.1.2 关键附属函数 update_cfs_shares reweight_entitystatic void update_cfs_shares(struct sched_entity *se, struct cfs_rq *cfs_rq) { struct task_group *tg cfs_rq-tg; unsigned long new_weight; /* 根据组基准shares、当前队列总负载换算本CPU上se实际权重 */ new_weight calc_group_shares(cfs_rq); /* 新旧权重不一致修改调度实体权重调整CFS红黑树位置 */ if (se-load.weight ! new_weight) { reweight_entity(cfs_rq, se, new_weight); } /* 向上递归刷新父任务组权重实现多级分组联动生效 */ if (cfs_rq_is_group_rq(cfs_rq)) update_cfs_group(se); } /* 重新设置se权重内核CFS调度依据load.weight分配时间片 */ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, unsigned long weight) { /* 从红黑树临时摘除节点 */ if (se-on_rq) rb_erase(se-rb_node, cfs_rq-tasks_timeline); /* 更新调度实体权重 */ se-load.weight weight; /* 重新插入红黑树权重变化带来排序变更 */ if (se-on_rq) rb_add(se-rb_node, cfs_rq-tasks_timeline); }代码逻辑总结修改 shares→更新 tg 基准值→全 CPU 刷新 se 权重→变更 se 在 CFS 红黑树位置→调度选任务时按新权重分配 CPU 时间整套流程同步完成。案例二用户态实操通过修改 cpu.shares 触发 sched_group_set_shares压测验证资源占比步骤 1创建两个 cgroup 分组 group1、group2# 进入cpu子系统挂载目录 cd /sys/fs/cgroup/cpu # 创建两个任务组目录内核自动生成对应cpu.shares等文件 sudo mkdir group1 group2 # 查看默认shares默认新建分组1024 cat group1/cpu.shares cat group2/cpu.shares步骤 2编写 CPU 满载测试小程序无限消耗 CPU可直接编译运行// cpu_stress.c 死循环占用单核CPU #include stdio.h int main(void) { while(1){ ; // 空循环持续消耗CPU算力 } return 0; }编译命令gcc cpu_stress.c -o cpu_stress步骤 3启动两个测试进程分别加入两个分组# 后台启动第一个进程获取PID ./cpu_stress echo $! group1/cgroup.procs # 后台启动第二个进程 ./cpu_stress echo $! group2/cgroup.procs步骤 4修改 shares触发内核 sched_group_set_shares 执行# group1权重1024group2权重3072理论满载CPU占比1:3 sudo echo 1024 group1/cpu.shares sudo echo 3072 group2/cpu.shares写入文件瞬间内核 cgroup 回调触发sched_group_set_shares(group1,1024)、sched_group_set_shares(group2,3072)实时刷新权重。步骤 5perfftrace 跟踪 sched_group_set_shares 函数调用# 挂载debugfs用于ftrace sudo mount -t debugfs none /sys/kernel/debug # 清空跟踪缓存 sudo echo /sys/kernel/debug/tracing/trace # 配置需要跟踪的内核函数 sudo echo sched_group_set_shares /sys/kernel/debug/tracing/set_ftrace_filter sudo echo update_cfs_shares /sys/kernel/debug/tracing/set_ftrace_filter # 开启函数跟踪 sudo echo function /sys/kernel/debug/tracing/current_tracer sudo echo 1 /sys/kernel/debug/tracing/tracing_on # 再次修改权重触发函数调用 sudo echo 2048 group1/cpu.shares # 关闭跟踪 sudo echo 0 /sys/kernel/debug/tracing/tracing_on # 查看跟踪日志验证函数被调用 cat /sys/kernel/debug/tracing/trace日志预期日志中出现sched_group_set_shares调用栈后续跟随update_cfs_shares、reweight_entity调用记录验证修改 shares 真实走进目标内核函数。步骤 6mpstat 观测 CPU 占用比例# 每秒输出一次CPU统计 mpstat -P ALL 1现象group1 进程 CPU≈25%group2≈75%符合 1:3 权重配比再次修改 shares 后占比瞬时变化验证动态调整生效。步骤 7清理测试环境# 杀掉压测进程 killall cpu_stress # 删除测试分组 sudo rmdir group1 group2案例三cgroup v2 权重转换验证v2 cpu.weight 映射 shares# 挂载cgroup v2 sudo mkdir -p /sys/fs/cgroup2 sudo mount -t cgroup2 none /sys/fs/cgroup2 cd /sys/fs/cgroup2 sudo mkdir testgrp # v2默认weight100修改为200内核自动换算shares200*1024/1002048 echo 200 testgrp/cpu.weight写入后内核底层依旧调用sched_group_set_shares(tg,2048)v1/v2 用户接口不同内核统一收敛至同一个权重设置函数。五、常见问题与解答Q1修改 cpu.shares 后 CPU 占比没有按预期变化答shares 是相对权重仅在分组内进程同时满载争抢 CPU 时才按比例分配CPU 空闲时系统会把空闲算力全部分配给运行进程占比不受 shares 约束。排查使用 stress-ng 打满两个分组 CPU 负载后再观察 mpstat确认满载场景分配比例其次用 ftrace 跟踪sched_group_set_shares是否正常触发若函数无调用说明 cgroup 挂载异常、CONFIG_FAIR_GROUP_SCHED 未开启。Q2修改 shares 后 ftrace 看不到 sched_group_set_shares 调用答①内核未开启 CONFIG_FTRACE、CONFIG_FAIR_GROUP_SCHED②写入的 shares 数值和原有一致内核直接 return 跳过函数执行③cgroup cpu 子系统挂载失效写入文件未走到内核回调。核对zcat /proc/config.gz | grep FAIR_GROUP_SCHED确认配置 y重新挂载 cgroup 后复测。Q3多级嵌套 cgroup子组修改 shares 为何会影响父组调度答update_cfs_shares内部update_cfs_group会向上递归刷新父 task_group 权重内核分层调度下子组负载变化会传导上层分组是sched_group_set_shares设计的联动逻辑符合树形组调度规范如需隔离可拆分独立顶层分组。Q4shares 设置很大65535单个分组能否独占多核 CPU答不能shares 仅控制同 CPU 内分组时间片占比单进程受限于 CPU 单核多进程塞满分组内多线程才可占用多核如需限制核数配合 cpuset 子系统绑定 CPU 核心。Q5根分组 root_task_group 为什么禁止调用 sched_group_set_shares 修改 shares答根组承载系统所有未划入自定义 cgroup 的进程内核固定 root_shares 配置修改会破坏全系统基础调度基准源码中if(tgroot_task_group) return -EINVAL直接拦截入参。六、实践建议与最佳实践6.1 线上业务权重配置规范基准配比生产业务基准 shares4096、测试环境 1024、日志采集 512形成固定梯度高峰期天然优先保障生产资源禁止随意设超大 shares100000避免权重溢出带来内核计算异常。动态调优时机业务流量上涨时在线echo xxx cpu.shares调用sched_group_set_shares热调权重无需重启业务凌晨低峰回落权重节约资源。6.2 内核调试排错技巧权重异常排查链路mpstat 资源异常→ftrace 跟踪sched_group_set_shares调用→gdb/kgdb 读取task_group-shares实际数值→查看对应 CPU 的sched_entity-load.weight三层定位配置是否落进内核。高频改 shares 优化不要循环高频 echo 修改 cpu.shares频繁触发sched_group_set_shares全 CPU 遍历刷新带来不必要调度开销批量变更建议通过程序批量写入单次修改目标值。6.3 内核二次开发优化自研调度策略时尽量复用sched_group_set_shares现有刷新逻辑不要重写全 CPU 遍历代码规避锁、队列同步 BUG如需新增自定义权重字段在 task_group 新增成员沿用原有更新链路。嵌入式精简内核不需要组调度时关闭CONFIG_FAIR_GROUP_SCHED剔除 task_group 与 sched_group_set_shares 相关代码缩减内核体积、降低调度开销。6.4 K8s 容器落地优化K8s 的 resources.cpu 配置最终转化为 cgroup cpu.weight底层依托sched_group_set_shares核心业务容器 requests.cpu 调高对应权重非核心组件调低依托内核原生机制实现混部隔离无需额外第三方资源管控组件。七、总结与应用延伸本文从组调度架构、task_group 结构体、sched_group_set_shares源码实现、用户态 cgroup 实操、函数跟踪调试、线上优化六个维度完整拆解 Linux CFS 任务组权重动态调整机制。sched_group_set_shares是连接用户态 cgroup 配置与内核 CFS 调度的关键桥梁核心设计思路为用户配置→更新组基准 shares→全 CPU 遍历刷新组调度实体权重→修改 CFS 红黑树排序→调度器按新权重分配 CPU 时间依托这套机制实现权重在线热变更也是 cgroup CPU 资源相对配额的内核底层实现。从技术落地层面该函数是云原生容器资源调度、物理机多业务混部、嵌入式工控多任务资源隔离的底层依赖从学术研究角度本源码可用于 CFS 分层调度论文撰写、调度器性能优化课题的数据支撑。建议读者在现有环境基础上修改sched_group_set_shares源码例如增加自定义日志打印重新编译内核再次通过 ftrace 和压测程序验证修改效果深度体会内核权重刷新全链路工程落地中结合 cpusetcpu.sharescpu.cfs_quota 三类配置组合实现 “CPU 核数绑定 相对权重分配 最大算力硬限流” 三位一体资源管控方案。