必须启用config_rcu_nocb_cpu=y编译内核,rcu_nocbs=2,4-6才生效;需配合nohz_full和isolcpus实现零干扰;rcu_nocb_poll以轮询换唤醒抖动,但增cpu开销。

rcu_nocbs 怎么配才真正起效
不加 CONFIG_RCU_NOCB_CPU=y 编译内核,rcu_nocbs 参数完全无效——这是最常被忽略的前提。很多用户加了启动参数却看不到 rcuo 线程,根源就在这儿。
- 必须在内核配置中启用
CONFIG_RCU_NOCB_CPU(不是模块,得设为y) -
rcu_nocbs=2,4-6表示 CPU 2、4、5、6 不再执行 RCU 回调,所有回调转交对应rcuob/2、rcuop/4等 kthread 处理 - 不能只写
rcu_nocbs而不指定 CPU 列表,否则参数被忽略(内核日志里会打印rcu_nocbs: no CPUs specified) - 该设置是静态的:启动后无法增删 CPU,
/sys/kernel/debug/rcu/nocb下也无运行时接口
rcu_nocb_poll 是省唤醒还是烧 CPU
rcu_nocb_poll 的本质是让 rcuo* 线程放弃等待信号,改用轮询方式主动扫回调队列。它不降低延迟,而是把“唤醒延迟”换成“轮询开销”。
- 开启后,
rcuo*线程默认以约 1ms 周期轮询(可通过/proc/sys/kernel/rcu_nocb_poll查看状态) - 好处:卸载核上彻底消除唤醒抖动,对 SCHED_FIFO 实时线程更友好
- 代价:每个
rcuo*线程持续占用一个调度实体,即使空闲也消耗 CPU 时间片,功耗上升明显 - 电池设备或高密度部署场景慎用;服务器类低延迟系统可考虑,但建议搭配
isolcpus把rcuo*绑定到非隔离核上
为什么 rcu_nocbs 和 nohz_full 必须配对使用
单独用 rcu_nocbs 只卸载回调,但定时器滴答(tick)还在跑——这意味着该 CPU 仍会周期性被中断打断,实时线程照样被干扰。
-
nohz_full=2,4-6让这些核进入“全动态无滴答”模式,关掉周期 tick,仅在必要时触发 one-shot timer -
isolcpus=2,4-6进一步禁止调度器往这些核上放普通任务,避免负载迁移和缓存污染 - 三者组合(
isolcpus+nohz_full+rcu_nocbs)才能实现真正的“零干扰”执行环境 - 漏掉任一环,比如只加
rcu_nocbs不加nohz_full,你会发现perf record -e irq:irq_handler_entry里仍有大量timer中断
验证是否生效的三个关键检查点
别只看 ps aux | grep rcuo 有无进程,那只是表面。真正要看的是行为是否符合预期。
- 确认卸载核已无 RCU 回调执行:
cat /sys/kernel/debug/rcu/rcu_node中对应 CPU 的qll(queued callbacks)字段应长期为 0,且gp(grace period)相关计数不增长 - 检查
rcuo*线程是否真在干活:ps -o pid,comm,psr,rtprio -L -C "rcuo[bp]/*"看其绑定 CPU 和实时优先级(通常为 1) - 观察卸载核的中断分布:
cat /proc/interrupts | grep -E "(CPU2|CPU4|CPU5|CPU6)"应几乎只有 IPI(如Rescheduling interrupts),而function call interrupts显著减少
最容易被忽略的是:RCU 宽限期(GP)本身仍需所有 CPU 参与投票,rcu_nocbs 只卸载回调执行,并不豁免宽限期同步。所以即便 CPU 被卸载,它依然要响应 GP 相关 IPI——这点在做超低延迟测量时,可能成为隐藏的 jitter 来源。