4. 内核裁剪核心方法
ARM平台的内核配置,说白了就是告诉Linux内核:「嘿,你现在跑在什么芯片上,有哪些资源可用」。这一步做不好,后面优化再多也是白搭。我见过太多团队,拿着通用defconfig就开干,结果性能惨不忍睹。
咱们这一章,就聚焦四个核心模块:ARM架构选项、多核SMP与big.LITTLE、MMU配置、GIC中断控制器。每个都是硬骨头,但啃下来之后,你对ARM平台的理解会上一个台阶。
4.1 ARM架构选项——选对CPU家族
内核里ARM架构的配置项,集中在arch/arm/Kconfig和arch/arm64/Kconfig。你需要在make menuconfig里找到System Type这一栏。
核心原则:只选你用的CPU架构,多选一个都不会让内核更通用,只会让镜像更大、启动更慢。
以Cortex-A系列为例,常见的选项有:
| 配置项 | 对应CPU | 我的建议 |
|---|---|---|
| ARMv7-A | Cortex-A5/A7/A8/A9/A15 | 大多数32位嵌入式平台首选 |
| ARMv8-A | Cortex-A53/A55/A72/A76 | 64位平台必选,兼容32位 |
| ARMv7-M | Cortex-M3/M4/M7 | MCU类平台,不支持MMU |
我个人习惯,在配置时先确认芯片的CPU Part Number。比如全志H3是Cortex-A7,那就只勾ARMv7-A,别手贱把ARMv8也选上。曾经有个项目,同事图省事选了所有ARM架构,结果内核镜像大了300KB,启动多了0.5秒——在工业控制场景里,这0.5秒就是问题。
小技巧:用cat /proc/cpuinfo查看当前CPU架构,然后反向去内核里找对应的Kconfig选项。这样最保险。
4.2 多核SMP与big.LITTLE配置
现在ARM平台很少单核了。SMP(对称多处理)和big.LITTLE(大小核)是绕不开的话题。
4.2.1 SMP基本配置
内核里SMP的开关在General setup → Symmetric Multi-Processing Support。打开之后,还需要配置:
- NR_CPUS:最大CPU数量。我一般设为实际核数+1,留点余量。比如4核就设5。
- SCHED_MC:多核调度优化,建议开启。
- SCHED_SMT:超线程支持,ARM平台很少用,但如果有就开。
嗯,这里要注意:NR_CPUS设太大不会让系统更快,反而会浪费内存。每个CPU都有对应的数据结构,设成256的话,光这些结构就要吃掉好几MB。
4.2.2 big.LITTLE——大小核的坑
big.LITTLE架构,比如Cortex-A72+A53的组合,内核里需要开启:
CONFIG_BIG_LITTLE=y
CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
CONFIG_ARM_BIG_LITTLE_CPUIDLE=y
我曾经在一个车机项目上栽过跟头。平台是RK3399(A72×2 + A53×4),默认配置只开了SMP,没开big.LITTLE支持。结果所有任务都往大核上堆,小核闲着,功耗飙到6W,散热压不住。
后来加上CONFIG_BIG_LITTLE和CONFIG_ARM_BIG_LITTLE_CPUFREQ,再用cpufreq的schedutil调度器,才把功耗降到3.5W。你想想看,光一个配置项,功耗差了一倍。
避坑指南:big.LITTLE必须配合CPU调频驱动和调度器一起用。光开内核选项,不改设备树和用户态策略,效果等于零。我曾经调试一个平台,开了big.LITTLE但没配cpufreq governor,结果大小核之间任务迁移乱成一锅粥。
4.3 内存管理单元(MMU)配置
MMU是Linux运行的基础。没有MMU,就没有虚拟内存、进程隔离、页表映射。ARM平台的MMU配置,核心在页大小和TLB管理上。
4.3.1 页大小选择
ARM支持三种页大小:4KB、16KB、64KB。内核里对应:
CONFIG_ARM64_PAGE_SHIFT=12 // 4KB页
CONFIG_ARM64_PAGE_SHIFT=14 // 16KB页
CONFIG_ARM64_PAGE_SHIFT=16 // 64KB页
怎么选?我总结了一个经验:
| 页大小 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 4KB | 内存利用率高,碎片少 | TLB覆盖范围小 | 通用嵌入式、内存敏感 |
| 64KB | TLB覆盖大,大块数据访问快 | 内存浪费多,小文件效率低 | 网络设备、存储服务器 |
我个人习惯,嵌入式设备用4KB。64KB页虽然TLB命中率高,但如果你跑的是Web服务器或数据库,内存碎片会让你头疼。曾经有个路由器项目,用了64KB页,结果内存碎片率到30%,系统频繁OOM。换成4KB页之后,稳定多了。
4.3.2 TLB与缓存配置
ARM的TLB和缓存配置,主要在arch/arm64/mm和arch/arm/mm目录下。内核会自动检测,但你可以通过设备树调整:
// 设备树中设置L2缓存大小
L2: l2-cache {
compatible = "arm,pl310-cache";
cache-level = <2>;
cache-size = <0x100000>; // 1MB
cache-sets = <1024>;
cache-line-size = <32>;
};
嗯,这里要注意:设备树里的缓存参数必须和芯片手册一致。写错了不会报错,但性能会莫名其妙下降。我调试过一个平台,L2 cache-line-size写成了64,实际是32,结果缓存命中率掉了15%。
调试方法:用perf stat -e cache-misses,cache-references跑你的应用,看缓存缺失率。如果超过10%,就该检查MMU和缓存配置了。
4.4 GIC中断控制器配置
GIC(Generic Interrupt Controller)是ARM平台的中枢神经。中断配不好,CPU一半时间都在处理中断,应用性能就别想了。
4.4.1 GIC版本选择
ARM GIC有三个主流版本:
| 版本 | 特点 | 内核配置 |
|---|---|---|
| GIC-400 | 最多8个CPU,支持SPI/PPI/SGI | CONFIG_ARM_GIC=y |
| GIC-500 | ARMv8标准,支持更多CPU | CONFIG_ARM_GIC_V3=y |
| GIC-600 | 低延迟,支持虚拟化 | CONFIG_ARM_GIC_V3_ITS=y |
内核里配置GIC,一般在Device Drivers → Interrupt controller support下。我建议只选你平台用的那个版本。GIC-400和GIC-500的驱动可以共存,但没必要——多一个驱动就多一分启动时间。
4.4.2 中断亲和性设置
多核平台下,中断可以绑定到特定CPU。这能避免中断风暴打垮某个核。配置方法:
// 将网卡中断绑定到CPU0
echo 1 > /proc/irq/83/smp_affinity
// 查看当前中断分布
cat /proc/interrupts
我曾经在一个视频编解码项目上,把视频中断全部绑到CPU0,结果CPU0负载100%,其他核闲着。后来改成轮询模式,中断均匀分布到4个核,帧率从25fps提升到40fps。你想想看,就改了一个亲和性配置,性能提升60%。
避坑指南:GIC的中断优先级不要乱改。默认配置下,内核已经做了合理分组。我见过有人把定时器中断优先级设得比网卡还高,结果网络丢包率飙升。除非你非常清楚自己在做什么,否则保持默认。
4.5 本章知识体系总览
下面这张图,把ARM平台配置的四个核心模块串起来了。你可以把它当作配置时的检查清单。
这张图里,四个模块是并列关系,但实际配置时有先后顺序。我一般先搞定架构选项,再配SMP和MMU,最后调GIC。每一步都要对照芯片手册,别想当然。