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平台配置的四个核心模块串起来了。你可以把它当作配置时的检查清单。

ARM平台内核配置知识体系 ARM平台内核配置核心模块 ARM平台配置 ARM架构选项 SMP / big.LITTLE MMU配置 GIC中断控制器 ARMv7/ARMv8选择 CPU Part Number匹配 Kconfig精确勾选 NR_CPUS设置 大小核调度策略 cpufreq/cpuidle 页大小(4K/16K/64K) TLB覆盖范围 缓存参数设备树 GIC版本(GIC-400/500/600) 中断亲和性绑定 优先级分组 目标:精确匹配硬件,最小化内核,最大化性能

这张图里,四个模块是并列关系,但实际配置时有先后顺序。我一般先搞定架构选项,再配SMP和MMU,最后调GIC。每一步都要对照芯片手册,别想当然。