4. 子系统裁剪(上):进程调度、内存管理、中断子系统的核心选项与裁剪策略

嵌入式 Linux 内核的裁剪工作,核心在于理解各子系统的功能边界与资源消耗。本章聚焦于三个最基础的子系统:进程调度、内存管理和中断子系统。这些子系统在内核中无处不在,其配置选项直接影响系统的实时性、内存占用和响应速度。错误的裁剪可能导致系统崩溃或功能缺失,而精准的裁剪则能显著降低内核体积并提升启动性能。

4.1 进程调度子系统裁剪

进程调度器负责决定哪个任务获得 CPU 使用权。Linux 内核支持多种调度类(Scheduling Classes),每种调度类对应不同的调度策略和适用场景。对于嵌入式系统,通常只需要其中一到两种。

4.1.1 核心调度选项

配置选项默认值裁剪建议说明
CONFIG_SCHED_DEBUGyn调度器调试信息,生产环境必须关闭
CONFIG_SCHEDSTATSyn调度统计,增加运行时开销
CONFIG_SCHED_COREyn核心调度(Core Scheduling),仅多租户场景需要
CONFIG_FAIR_GROUP_SCHEDynCFS 组调度,无多用户/容器需求可关闭
CONFIG_RT_GROUP_SCHEDyn实时组调度,无严格优先级分组需求可关闭
CONFIG_CGROUP_SCHEDyncgroup 调度控制,无容器场景可关闭
裁剪要点:对于单用户、单任务的深度嵌入式系统,可以关闭所有与组调度和调试相关的选项。这通常能减少约 10-15 KB 的内核映像大小,并降低调度路径上的分支预测开销。

4.1.2 调度类选择

Linux 内核支持以下主要调度类,每个调度类都对应一个 CONFIG_ 选项:

  • CFS (Completely Fair Scheduler) — CONFIG_SCHED_CFS:通用调度器,默认开启,通常保留。
  • RT (Real-Time) — CONFIG_SCHED_RT:实时 FIFO/RR 调度,硬实时场景必须保留。
  • DL (Deadline) — CONFIG_SCHED_DEADLINE:EDF 调度器,非实时系统可关闭。
  • IDLE — CONFIG_SCHED_IDLE:空闲调度类,通常保留。
裁剪策略:如果系统不需要硬实时任务,可以关闭 CONFIG_SCHED_RT 和 CONFIG_SCHED_DEADLINE。这能减少约 8 KB 的代码段。但需注意,关闭 RT 调度后,sched_setscheduler() 系统调用将无法设置 SCHED_FIFO/SCHED_RR 策略。

4.1.3 进程调度裁剪流程图

进程调度子系统裁剪决策流程 开始裁剪 需要多用户/容器隔离? 是 保留组调度 否 关闭组调度 需要硬实时任务? 是 保留 RT+DL 否 关闭 RT+DL 生产环境? 是 关闭调试选项 否 保留调试选项 裁剪完成

4.2 内存管理子系统裁剪

内存管理子系统是内核中代码量最大的模块之一。对于内存受限的嵌入式系统,精细裁剪可以释放数百 KB 的代码和数据空间。

4.2.1 页面分配器与伙伴系统

配置选项默认值裁剪建议说明
CONFIG_COMPACTIONyn内存碎片整理,无长时间运行需求可关闭
CONFIG_MIGRATIONyn页面迁移,与 COMPACTION 联动
CONFIG_KSMyn内核同页合并,仅虚拟化场景需要
CONFIG_TRANSPARENT_HUGEPAGEyn透明大页,嵌入式小内存系统无收益
CONFIG_CMAy按需连续内存分配器,DMA 设备需要时保留

4.2.2 SLUB 分配器调优

内核对象缓存分配器(SLUB)是内存管理的关键路径。以下选项直接影响性能和内存占用:

# 针对小内存系统的 SLUB 优化配置
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=n          # 关闭调试,减少约 4 KB
CONFIG_SLUB_MEMCG_SYSFS=n    # 关闭 memory cgroup 接口
CONFIG_SLUB_CPU_PARTIAL=y    # 保留 CPU 部分空闲列表,提升性能
CONFIG_SLAB_FREELIST_HARDENED=n  # 关闭 freelist 加固,节省约 2 KB
注意:关闭 CONFIG_SLAB_FREELIST_HARDENED 会降低内核对抗堆溢出攻击的能力。仅在完全可信的执行环境中执行此操作。

4.2.3 内存管理裁剪前后对比

内存管理子系统裁剪效果(典型 ARM Cortex-A7 系统) 裁剪前:约 180 KB 透明大页 + KSM + 碎片整理 SLUB 调试 + 加固 内存 cgroup 接口 裁剪后:约 120 KB 仅保留基本伙伴系统 + SLUB 关闭调试,保留 CPU_PARTIAL 关闭 cgroup 接口 节省约 60 KB(33%)

4.3 中断子系统裁剪

中断子系统是系统响应外部事件的基石。其配置直接影响中断延迟和 CPU 开销。嵌入式系统通常只需要支持有限的中断控制器类型。

4.3.1 中断控制器驱动选择

内核支持多种中断控制器(IRQ Chip)驱动。通过 make menuconfig 进入 Device Drivers -> Interrupt controllers 进行选择:

驱动选项适用硬件裁剪建议
CONFIG_ARM_GICARM Generic Interrupt ControllerARM 平台必须保留
CONFIG_ARM_GIC_V3GICv3/v4ARMv8 平台必须保留
CONFIG_IMX_IRQSTEERi.MX 中断转向控制器仅 i.MX 平台需要
CONFIG_RENESAS_IRQCRenesas IRQC仅 Renesas 平台需要
CONFIG_TI_SCI_INTA_IRQCHIPTI SCI INTA仅 TI 平台需要
裁剪策略:只保留目标平台对应的中断控制器驱动。例如,对于 i.MX6ULL 平台,只保留 CONFIG_ARM_GIC 和 CONFIG_IMX_IRQSTEER,关闭所有其他厂商的驱动。这能减少约 20-30 KB 的内核映像。

4.3.2 软中断与 tasklet 配置

软中断(SoftIRQ)和 tasklet 是中断下半部的主要机制。以下选项控制其行为:

# 中断下半部相关选项
CONFIG_IRQ_WORK=y            # 必须保留,用于 RCU 和 perf
CONFIG_TASKLET_STATISTICS=n  # 关闭 tasklet 统计,减少开销
CONFIG_IRQ_TIME_ACCOUNTING=n # 关闭中断时间统计,节省约 2 KB
CONFIG_IRQ_DOMAIN_DEBUG=n    # 关闭中断域调试

4.3.3 中断响应路径优化

中断响应路径上的每个分支都会增加延迟。以下配置可以精简路径:

  • 关闭中断嵌套:CONFIG_IRQ_PREFLOW_FASTEOI=n — 减少中断控制器交互次数。
  • 使用线性中断域:CONFIG_IRQ_DOMAIN_HIERARCHY=y 但确保只注册必要的层级。
  • 关闭稀疏中断:CONFIG_SPARSE_IRQ=n — 对于固定中断数量的系统,使用线性表更高效。

4.3.4 中断子系统架构图

中断子系统核心组件与裁剪点 硬件中断源 中断控制器 (IRQ Chip) 裁剪点:只保留目标平台驱动 中断域 (IRQ Domain) 裁剪点:关闭 SPARSE_IRQ,使用线性域 中断处理核心 (do_IRQ / handle_IRQ) 裁剪点:关闭 IRQ_TIME_ACCOUNTING、调试选项 软中断 (SoftIRQ) tasklet 工作队列 (Workqueue)

4.4 综合裁剪清单与验证

完成上述三个子系统的裁剪后,建议按以下清单进行验证:

  1. 功能验证:确认系统调用(如 sched_setscheduler、mmap、request_irq)行为符合预期。
  2. 性能基准:使用 cyclictest 测量中断延迟,使用 lmbench 测量上下文切换时间。
  3. 内存占用:通过 size vmlinux 和 nm --size-sort vmlinux 检查各子系统的代码段大小。
  4. 启动时间:使用 initcall_debug 内核参数,观察各子系统初始化耗时。
推荐工具:使用 scripts/bloat-o-meter 脚本对比裁剪前后的内核映像,精确定位每个子系统的体积变化。