Linux rcu_nocbs 与 rcu_nocb_poll 的无回调核优化经验

1次阅读

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

Linux rcu_nocbs 与 rcu_nocb_poll 的无回调核优化经验

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/2rcuop/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 时间片,功耗上升明显
  • 电池设备或高密度部署场景慎用;服务器类低延迟系统可考虑,但建议搭配 isolcpusrcuo* 绑定到非隔离核上

为什么 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 来源。

text=ZqhQzanResources