13 · Housekeeping CPU:内核家务的隔离——原理、内核实现与友商做法
结论:housekeeping CPU 是内核里的正式概念(
include/linux/sched/isolation.h:10-19、kernel/sched/isolation.c):把 CPU 分成两组,隔离核只跑你指定的实时线程,housekeeping 核承担内核的全部「家务」——周期 tick、定时器迁移、unbound workqueue、内核线程、RCU 回调、负载均衡、managed 中断、杂务统计,再加上用irqaffinity赶过去的设备中断。RT 内核解决「内核可抢占」,housekeeping 解决「隔离核不被打扰」,两者叠加才有 10 µs 级最坏延迟。它由四条启动参数决定(isolcpus=domain,managed_irq,…、nohz_full=、rcu_nocbs=、irqaffinity=),缺一条就漏一类家务;CPU0 永远是 housekeeping。友商——Red Hat tuned / OpenShift、Ubuntu Real-time、NVIDIA Jetson、百度 Apollo——做的都是这同一套参数的封装,差别只在自动化程度、验证手段,以及厂商 RT 内核是否带齐了所需配置。
第一层:家务落在哪里
先看结论:没有 housekeeping 时,九类家务按各自规则随机落核,任何一颗核都可能被打扰;配置后家务集中到 housekeeping 核,隔离核只剩 arch timer、IPI 和线程自己触发的内核路径。
第二层:内核机制与参数映射
先看结论:
nohz_full=一次接管六类家务(tick/timer/RCU-misc/wq/kthread),isolcpus=管调度域与 managed 中断,rcu_nocbs=管回调线程,irqaffinity=管其余设备中断;cgroup cpuset 只能补充任务隔离,管不了 tick 与中断。
13.1 九类家务与对应的内核机制
include/linux/sched/isolation.h:10-19 定义了 HK_TYPE_TIMER / RCU / MISC / SCHED / TICK / DOMAIN / WQ / MANAGED_IRQ / KTHREAD,每种一个 cpumask(isolation.c:27)。内核各处用 housekeeping_cpumask(type) / housekeeping_any_cpu(type) / housekeeping_test_cpu() 决定家务落点:
| 家务 | 谁产生 | 默认落点 | 配置后 | 位置 |
|---|---|---|---|---|
| 周期 tick | 每 HZ 一次的 tick_sched_timer |
每颗核 | nohz_full 核上单任务运行时停止 |
kernel/time/tick-sched.c |
| 定时器迁移 | 非 pinned 的 timer/hrtimer 到期核选择 | 本核 | 优先 HK_TYPE_TIMER 核 |
kernel/time/hrtimer.c:225, 2275;kernel/sched/core.c:1140-1155 |
| unbound workqueue | kworker/u*(文件系统回写、驱动延后工作) |
任意核 | 限制到 HK_TYPE_WQ ∩ HK_TYPE_DOMAIN |
kernel/workqueue.c:7800-7807;延迟工作 :2553-2554 |
| 内核线程 | kthreadd 及其子线程 |
任意核 | 亲和 HK_TYPE_KTHREAD |
kernel/kthread.c:371, 746 |
| RCU 回调 | call_rcu/kfree_rcu 的回调执行 |
本核(rcuc/softirq) | rcu_nocbs 后由 rcuo 线程执行,线程亲和 HK_TYPE_RCU |
kernel/rcu/tree_plugin.h:1378;kernel/rcu/tree_nocb.h |
| 负载均衡 / 调度域 | CFS 周期均衡、唤醒选核 | 全部核 | 隔离核退出 sched domain | kernel/sched/topology.c:1435, 2625, 2717, 2752 |
| managed 中断 | 多队列 NVMe / 网卡按队列自动分配的中断 | 每队列一核 | 只用 HK_TYPE_MANAGED_IRQ 核 |
kernel/irq/manage.c:258;kernel/irq/cpuhotplug.c:198 |
| 杂务 | vmstat 更新、软/硬死锁检测线程、idle 负载均衡 | 每颗核 | HK_TYPE_MISC/TIMER 核 |
kernel/watchdog.c:1249;kernel/sched/fair.c:12354;kernel/cpu.c:1490, 1905 |
| 设备中断 | GIC SPI / ITS LPI | irq_default_affinity(全部核)+ irqbalance 打散 |
irqaffinity= 设默认;/proc/irq/*/smp_affinity 逐个改 |
kernel/irq/irqdesc.c;kernel-parameters.txt:2436 |
RT 内核下这些家务大多已经是线程(irq/*、ksoftirqd、rcuc、kworker),可抢占;housekeeping 做的是把它们从隔离核上挪走,否则它们即使优先级低,也会带来缓存污染、IPI 与偶发的高优先级唤醒。
13.2 内核实现:kernel/sched/isolation.c
- 两个入口:
housekeeping_nohz_full_setup()(:194-201)把nohz_full=映射成HK_FLAG_TICK | TIMER | RCU | MISC | WQ | KTHREAD;housekeeping_isolcpus_setup()(:205-253)解析isolcpus=的标志:nohz(等价于 nohz_full 那组)、domain(默认,HK_FLAG_DOMAIN)、managed_irq(HK_FLAG_MANAGED_IRQ),非法标志pr_warn("isolcpus: Invalid flag")(:241)。 - 合并规则(
housekeeping_setup():118-190):cpulist_parse失败告警(:134);CPU 集里没有任何在线 CPU 时回退到 boot CPU(:148)——所以 CPU0 不能隔离(它承担 timekeeping);两条参数的集合不一致时pr_warn("Housekeeping: nohz_full= must match isolcpus=")并忽略(:169)。 - 消费者:见 13.1 表的「位置」列;另外 CPU 热插拔时下线核上的定时器/中断迁移目标也取 housekeeping 核(
kernel/cpu.c:1905;kernel/sched/core.c:8359)。 - 文档态度:
Documentation/admin-guide/kernel-parameters.txt:2436-2438把isolcpus标为「Deprecated - use cpusets instead」,domain标志的说明强烈建议用 cpuset 关闭负载均衡——但 tick、RCU offload、managed 中断三样只有启动参数能管,所以 RT 场景仍以启动参数为主、cpuset 为辅。 - cpuset 替代/补充:cgroup v2
cpuset.cpus.partition=isolated(kernel/cgroup/cpuset.c:126PRS_ISOLATED;Documentation/admin-guide/cgroup-v2.rst:2449-2505)能在运行时创建/撤销隔离分区并关闭其调度域,适合动态场景;它不影响 tick、RCU、managed 中断和已存在的内核线程亲和。
13.3 隔离核上还剩什么
即使四条参数齐全,隔离核上仍会出现:
| 剩余项 | 来源 | 处理 |
|---|---|---|
| arch timer PPI | 隔离核上任务自己的 hrtimer 到期(nohz_full 只是停周期 tick) |
正常,属于任务自己的时间 |
| IPI | 其它核发来的 TLB 失效(共享 mm 的 munmap)、smp_call_function、内核文本修补(ftrace/static key 切换)、reschedule |
隔离核进程用独立地址空间;housekeeping 进程避免频繁 munmap/mprotect;生产环境不动 ftrace/静态键 |
| per-CPU 内核线程 | migration/N、cpuhp/N、ksoftirqd/N、kworker/N:*、RT 下的 rcuc/N |
只要没人 raise softirq / 排 per-CPU work,它们不会跑;用 osnoise 确认 |
| 任务自己触发的内核路径 | 系统调用、缺页、信号 | 应用侧 mlockall、预分配、热路径无系统调用 |
| 伪 NMI / PMU | 开启伪 NMI 且 perf 采样时 | 生产环境关闭采样 |
osnoise -c <隔离核> 的输出把这些逐项列出(NMI/IRQ/softirq/线程各占多少),是验收隔离质量的标准工具。
13.4 怎么做
- 划分:以簇为单位(共享 L3 的核放一起);big.LITTLE 上小核簇做 housekeeping、大核簇隔离;CPU0 必在 housekeeping;经验值 8 核给 2、12 核给 4。
- 启动参数(示例 12 核,0-3 housekeeping):
isolcpus=domain,managed_irq,4-11 nohz_full=4-11 rcu_nocbs=4-11 irqaffinity=0-3 - 运行时脚本:停
irqbalance;/proc/irq/default_smp_affinity与所有/proc/irq/*/smp_affinity_list指向 housekeeping;需要留在隔离核的控制设备中断单独指定;RT 线程taskset/pthread_setaffinity_np钉到隔离核;其它进程默认留在 housekeeping(isolcpus=domain保证调度器不往隔离核放)。systemd 服务可用CPUAffinity=0-3(或systemd.cpu_affinity=启动参数)兜底。 - 验证:
/sys/devices/system/cpu/isolated、/sys/devices/system/cpu/nohz_full、/proc/interrupts隔离列、ps -eo psr,cls,rtprio,comm、osnoise/timerlat、ls /sys/kernel/debug/sched/domains/cpu4/(应为空)。 - 预算:housekeeping 核会很忙——所有
irq/*、ksoftirqd、rcuo、kworker、中间件线程都挤在这里;它的吞吐是牺牲品,用第 11 章的方法量化。
13.5 友商怎么做
| 友商 / 产品 | 机制 | 参数与做法(公开资料) | 特点与启示 |
|---|---|---|---|
Red Hat RHEL for Real Time(tuned realtime profile) |
一个变量 isolated_cores(/etc/tuned/realtime-variables.conf)驱动全部配置 |
[bootloader] 生成 isolcpus=[managed_irq,]<cores>、禁 P-state、nosoftlockup;[sysctl] kernel.sched_rt_runtime_us=-1;[sysfs] 把 workqueue/cpumask 与 machinecheck 指向非隔离核;[irqbalance] 黑名单隔离核;[scheduler] 把已存在线程/中断挪走;realtime-virtual-host 再加 nohz_full=、rcu_nocbs=、irqaffinity=,关 KSM,并给 ksoftirqd/ktimers/rcuc/rcub 设 FIFO 优先级 |
最成熟的封装:一处变量、启动+运行时一起管、包含 workqueue cpumask 与 irqbalance 这两个常被漏掉的点;把「运行时挪线程」做进 profile 而不是靠人 |
| Red Hat OpenShift(PerformanceProfile / Node Tuning Operator) | CR 里声明 cpu.reserved(= housekeeping)与 cpu.isolated |
自动生成 nohz=on rcu_nocbs=<iso> tuned.non_isolcpus=<mask> nosoftlockup tsc=nowatchdog iommu=pt systemd.cpu_affinity=<reserved> isolcpus=managed_irq,<iso>(balanceIsolated=false 时含 domain),配合 kubelet 静态 CPU 管理与「工作负载分区」把平台守护进程钉到 reserved |
明确把用户态守护进程也归入 housekeeping(systemd.cpu_affinity);managed_irq 与 domain 分开可选——高吞吐容器可以保留负载均衡 |
| Canonical Ubuntu Real-time | 文档化的一组启动参数 | nohz=on nohz_full=<list> isolcpus=<list> rcu_nocbs=<list> irqaffinity=<list> rcu_nocb_poll;示例 isolcpus=3-5 irqaffinity=0-2,6-N;告诫:boot CPU 不能进 nohz_full、irqaffinity 不能为空、softirq 无法完全排除、运行时用 tuna 调整 |
与内核文档一致的最小集;rcu_nocb_poll 让 rcuo 轮询而不靠 IPI 唤醒,是隔离核减少 IPI 的补充手段 |
| NVIDIA Jetson(L4T r36.x / JetPack 6,Orin) | RT 内核以 OTA Debian 包提供(nvidia-l4t-rt-kernel*),extlinux.conf 切换 |
官方文档只给安装步骤,并提示 UEFI runtime services 默认开启会增加延迟;未给 isolcpus/nohz_full 指导。社区反馈:OTA RT 内核未启用 NO_HZ_FULL/RCU_NOCB_CPU,nohz_full=/rcu_nocbs= 被忽略,需自编译(meta-tegra LINUX_KERNEL_TYPE=preempt-rt);隔离后 cyclictest 50–200 µs |
厂商 RT 内核不一定带齐 housekeeping 需要的配置——上手第一步是 zcat /proc/config.gz \| grep -E 'NO_HZ_FULL\|RCU_NOCB';EFI runtime 在 6.12 RT 上默认已禁(第 10 章) |
| 百度 Apollo Cyber RT | 用户态协程调度器按数据流分组绑核 | example_sched_choreography.conf:process_level_cpuset "0-7,16-23";线程 lidar 绑 CPU1 SCHED_RR 10、shm 绑 CPU2 SCHED_FIFO 10;choreography 处理器 8 个绑 0-7 SCHED_FIFO 10,pool 处理器 8 个绑 16-23 SCHED_OTHER;任务按 processor + prio 编排 |
典型的用户态分区:按数据流把协程调度器钉到 cpuset 并分优先级,与第 14 章「接入/感知 vs 控制」分簇同构;但它不做内核侧 housekeeping,必须配合本章参数才能把内核家务赶走 |
| Intel ECI / TCC(x86 对照) | isolcpus + 缓存分区(CAT)+ TCC 缓冲 |
隔离核 + L2/L3 way 分配给实时任务 | 缓存/带宽分区是 housekeeping 的下一层;arm64 对应 MPAM,6.12.107 无驱动,只能靠 SoC QoS 寄存器 |
| Xenomai 4 / EVL、Jailhouse / NXP Harpoon | 另一条路:把隔离核整个交给另一个内核(EVL 核或 RTOS) | Jailhouse cell 独占 CPU + 设备;Harpoon 在隔离核跑 Zephyr/FreeRTOS | AMP 方案把「家务」问题从根上消灭,代价是应用不能用 Linux API;自动驾驶 SoC 上通常由 Cortex-R 安全岛承担这一角色 |
| ROS 2 / Apex.AI 实时指南、OSADL QA farm | 应用层规范 | isolcpus + SCHED_FIFO executor + mlockall + 无动态分配;OSADL 测试机用 shielded CPU 长期跑 cyclictest |
与本章一致;OSADL 数据是第 11 章量级的来源之一 |
共同点:没有人绕开这四条启动参数;差别在 ① 是否把 workqueue cpumask、irqbalance、已存在线程的迁移做成自动化(Red Hat 最完整);② 是否把用户态守护进程也纳入 housekeeping(OpenShift 明确做了);③ 厂商内核是否带齐 NO_HZ_FULL/RCU_NOCB_CPU(Jetson 的教训)。
13.6 对自动驾驶 SoC 的启示
- 参数集直接采用 Red Hat 那一套的等价物(第 14.5 节已给出 arm64 版本),并像 OpenShift 一样用
systemd.cpu_affinity=/CPUAffinity=把中间件守护进程钉在 housekeeping。 - 像 Apollo 那样在用户态按数据流分组绑核,但必须叠加内核侧 housekeeping。
- 拿到厂商 RT 内核先查
/proc/config.gz;缺NO_HZ_FULL/RCU_NOCB_CPU就自编译(第 10 章的 Kconfig 清单)。 - 隔离核上的 IPI 是最后一类噪声:控制进程用独立地址空间,housekeeping 侧避免频繁
munmap,生产环境不切 ftrace。
源码定位
| 主题 | 位置 |
|---|---|
| HK 类型与标志 | include/linux/sched/isolation.h:10-19;kernel/sched/isolation.c:12-27 |
| 参数解析 | kernel/sched/isolation.c:118-253 |
| workqueue / kthread | kernel/workqueue.c:2553-2554, 7800-7807;kernel/kthread.c:371, 746 |
| 定时器 | kernel/time/hrtimer.c:225, 2275;kernel/sched/core.c:1140-1155 |
| RCU | kernel/rcu/tree_plugin.h:1378;kernel/rcu/tree_nocb.h |
| 调度域 | kernel/sched/topology.c:1435, 2625, 2717, 2752;kernel/sched/core.c:8359 |
| managed 中断 | kernel/irq/manage.c:258;kernel/irq/cpuhotplug.c:198 |
| 杂务 / 热插拔 | kernel/watchdog.c:1249;kernel/sched/fair.c:12354;kernel/cpu.c:1490, 1905 |
| cpuset 分区 | kernel/cgroup/cpuset.c:126;Documentation/admin-guide/cgroup-v2.rst:2449-2505 |
| 参数文档 | Documentation/admin-guide/kernel-parameters.txt:2436-2470, 3984-3990 |
小结
- housekeeping = 九类内核家务的归属地;四条启动参数各管一部分,CPU0 必留,
nohz_full与isolcpus集合必须一致。 - 隔离核上最后只剩 arch timer、IPI、per-CPU 线程与任务自己的内核路径;
osnoise是验收工具。 - 友商做法殊途同归:Red Hat tuned 封装最完整,OpenShift 把用户态守护进程也纳入,Ubuntu 给最小集,Jetson 提醒厂商内核可能缺配置,Apollo 是用户态分区的样板。
- 承上启下:第 14 章把 housekeeping 与优先级栅格落到自动驾驶 SoC 的三个分区样板与完整参数集上。