解决Linux系统cgroups v2 cpu控制器启用及实时线程问题
cgroups v2 CPU控制器启用与适配问题解答
1. 实时线程检测命令的局限性
你当前使用的ps -T axo pid,ppid,user,group,lwp,nlwp,start_time,comm,cgroup,cls|grep RR仅能捕获使用SCHED_RR调度策略的实时线程,无法覆盖所有实时线程。cgroups v2 CPU控制器限制的实时线程还包括使用SCHED_FIFO和SCHED_DEADLINE策略的线程,正确的检测命令应包含这三类:
ps -T axo pid,ppid,user,group,lwp,nlwp,start_time,comm,cgroup,cls | grep -E '(RR|FIFO|DEADLINE)'
其中cls列对应线程的调度类,上述三个值分别对应三种实时调度策略。
2. 实时线程重复生成的解决思路
直接杀死实时线程不可取(多为系统服务依赖的线程,会自动重启),正确做法是将这些线程移动到root cgroup,而非销毁:
- 从上述检测命令中获取目标线程的LWP(轻量级进程ID)和当前cgroup路径;
- 将LWP写入root cgroup的
cgroup.procs文件,完成移动:echo <LWP> > /sys/fs/cgroup/cgroup.procs - 若线程属于systemd管理的服务,可通过修改服务配置文件(如
/usr/lib/systemd/system/<service>.service)添加CPUAccounting=no或调整CGroup相关参数,避免服务启动时将线程放入非root cgroup。
3. Ubuntu 22.04 CPU控制器未启用的其他排查方向
即使清理了所有非root cgroup的实时线程,CPU控制器仍未出现,需排查以下几点:
- 内核配置检查:确认内核是否开启了cgroups v2 CPU控制器支持,执行:
若输出为zcat /proc/config.gz | grep CONFIG_CGROUP_CPU# CONFIG_CGROUP_CPU is not set,说明内核未编译该功能,需更换支持的内核或重新编译。 - systemd配置限制:systemd可能未默认启用CPU控制器,检查
/etc/systemd/system.conf中的DefaultControllers参数,确保包含cpu。修改后执行systemctl daemon-reexec重启systemd生效。 - 内核日志排查:查看内核启动日志中cgroups相关报错:
若存在dmesg | grep cgroupcpu controller cannot be enabled类提示,根据日志信息定位冲突项(如部分驱动或模块占用导致控制器无法加载)。
4. C/C++代码适配多发行版的CPU控制器使用方案
前置检测逻辑
代码中需先确认系统环境满足条件:
- 检测是否为cgroups v2环境:
bool isCgroupV2() { struct statfs fs; if (statfs("/sys/fs/cgroup", &fs) == -1) return false; return fs.f_type == 0x63677270; // cgroup2fs的magic number } - 检查root cgroup是否启用CPU控制器:
bool hasCpuController() { FILE* fp = fopen("/sys/fs/cgroup/cgroup.controllers", "r"); if (!fp) return false; char buf[256]; bool found = false; while (fgets(buf, sizeof(buf), fp)) { if (strstr(buf, "cpu")) { found = true; break; } } fclose(fp); return found; }
CPU限制配置步骤(需root权限)
若环境满足条件,可创建自定义cgroup并设置CPU限制:
- 创建子cgroup目录:
mkdir("/sys/fs/cgroup/myapp_cgroup", 0755); - 启用CPU控制器(允许子cgroup使用):
FILE* fp = fopen("/sys/fs/cgroup/myapp_cgroup/cgroup.subtree_control", "w"); if (fp) { fprintf(fp, "+cpu"); fclose(fp); } - 设置CPU使用率上限(例如限制为50%,格式为
max 100000,100000对应100%):fp = fopen("/sys/fs/cgroup/myapp_cgroup/cpu.max", "w"); if (fp) { fprintf(fp, "50000 100000"); fclose(fp); } - 将当前进程加入该cgroup:
pid_t pid = getpid(); fp = fopen("/sys/fs/cgroup/myapp_cgroup/cgroup.procs", "w"); if (fp) { fprintf(fp, "%d", pid); fclose(fp); }
发行版适配注意事项
- RHEL9:默认已启用CPU控制器,可直接使用上述逻辑;
- Ubuntu 22.04/CentOS9 Stream:需先通过内核配置、systemd调整启用CPU控制器,代码中需增加对控制器未启用场景的错误提示,引导用户先完成系统层面的配置。
内容的提问来源于stack exchange,提问作者JenyaKh
相关产品推荐
相关产品推荐

