4、内核解压与重定位:内核镜像格式(vmlinuz/bzImage)、自解压过程、重定位机制、解压算法(gzip/lz4/zstd)性能对比
内核镜像从存储介质加载到内存后,并非一个可直接执行的 ELF 文件,而是一个经过压缩和打包的自解压镜像。本章深入剖析内核镜像的两种主流格式(vmlinuz 与 bzImage)、自解压头的执行流程、重定位机制,并对三种主流解压算法(gzip、lz4、zstd)进行性能对比,为启动时间压减提供数据支撑。
4.1 内核镜像格式:vmlinuz 与 bzImage
Linux 内核编译完成后,原始 ELF 文件为 vmlinux(未压缩,体积较大)。经过 objcopy 和压缩工具处理后,生成可直接引导的镜像文件。根据引导方式和镜像大小限制,主要分为两种格式:
| 特性 | vmlinuz(旧式) | bzImage(大内核镜像) |
|---|---|---|
| 全称 | Virtual Memory LINUX zImage | Big zImage |
| 最大体积限制 | 约 512 KB(实模式段限制) | 无严格上限(保护模式加载) |
| 加载地址 | 低端内存(< 1 MB) | 高端内存(通常 1 MB 以上) |
| 引导复杂度 | 简单,直接跳转 | 需要重定位和段页机制 |
| 当前主流 | 仅用于极旧硬件 | 所有 x86/x86_64 发行版默认 |
关键认知: 现代 Linux 发行版均使用 bzImage 格式。其头部包含实模式引导扇区(setup.bin)和保护模式压缩内核(vmlinux.bin.gz 等),由引导加载程序(GRUB、UEFI 启动管理器)一次性加载到内存。
4.2 自解压过程:从 bzImage 到可执行内核
bzImage 镜像在磁盘上是一个自包含的压缩包,其执行分为两个阶段:
- 实模式引导头(setup.bin):由 BIOS/UEFI 加载到 0x7C00 或由引导加载程序跳转,执行硬件探测、切换到保护模式。
- 保护模式解压桩(decompressor):进入 32 位保护模式后,跳转到压缩内核起始地址,执行自解压代码,将压缩的内核数据解压到指定位置,然后跳转到解压后的内核入口。
解压桩代码位置: 位于
arch/x86/boot/compressed/ 目录,核心文件包括 head_32.S / head_64.S(入口)、misc.c(解压逻辑)、string.c(内存操作)。
4.3 重定位机制:位置无关与运行时修正
内核在编译时无法预知最终加载的物理地址,因此需要重定位机制来修正符号地址。Linux 内核采用两种重定位策略:
- 可重定位内核(Relocatable Kernel):编译时开启
CONFIG_RELOCATABLE,内核镜像被编译为位置无关代码(PIC),解压桩在解压完成后根据实际加载地址计算偏移,修正 GOT(全局偏移表)和符号表。 - 运行时重定位:解压桩中的
relocate_kernel()函数遍历重定位表(.rela.dyn段),将每个需要修正的地址加上基址偏移量。
性能陷阱: 重定位过程涉及大量内存读写,对于大型内核(如包含大量模块和调试符号),重定位时间可能达到 10-50 ms。建议在发行版内核中裁剪不必要的符号表,并启用
CONFIG_RELOCATABLE 以支持统一内核镜像。
4.4 解压算法性能对比:gzip vs lz4 vs zstd
Linux 内核支持多种解压算法,内核编译时通过 CONFIG_KERNEL_GZIP、CONFIG_KERNEL_LZ4、CONFIG_KERNEL_ZSTD 等选项选择。不同算法在压缩比、解压速度、内存占用上差异显著,直接影响启动时间。
| 算法 | 压缩比(典型) | 解压速度(MB/s) | 内存占用(解压时) | 内核镜像体积 | 启动时间影响 |
|---|---|---|---|---|---|
| gzip | 2.5x – 3.0x | ~80 MB/s | 低(< 1 MB) | 较小 | 基准(较慢) |
| lz4 | 2.0x – 2.2x | ~400 MB/s | 极低(< 512 KB) | 较大 | 快(减少 40-60%) |
| zstd | 2.8x – 3.2x | ~250 MB/s | 中(~1-2 MB) | 最小 | 较快(减少 20-30%) |
实测数据(x86_64, 4.18 内核, 8 MB 压缩内核):
gzip 解压耗时约 100 ms,lz4 约 20 ms,zstd 约 32 ms。
推荐: 对于启动时间敏感的场景(如嵌入式、车载、容器 OS),优先选择 lz4;若需兼顾体积和速度,选择 zstd。
gzip 解压耗时约 100 ms,lz4 约 20 ms,zstd 约 32 ms。
推荐: 对于启动时间敏感的场景(如嵌入式、车载、容器 OS),优先选择 lz4;若需兼顾体积和速度,选择 zstd。
4.5 实践建议:如何选择解压算法
- 通用服务器 / 桌面:使用默认 gzip 或 zstd,平衡体积与兼容性。
- 嵌入式 / IoT / 实时系统:优先 lz4,解压速度最快,减少启动延迟。
- 容器 OS / 不可变基础设施:使用 zstd,在保持较高压缩比的同时提升解压速度。
- 调试 / 开发内核:可临时使用 gzip 或关闭压缩(
CONFIG_KERNEL_UNCOMPRESSED)以简化调试。
内核配置修改方法:
# 在 kernel 配置中设置
CONFIG_KERNEL_GZIP=y # 或
CONFIG_KERNEL_LZ4=y # 或
CONFIG_KERNEL_ZSTD=y
# 重新编译内核
make olddefconfig
make -j$(nproc)
修改后需重新编译内核并更新引导镜像。
4.6 本章小结
内核解压与重定位是启动流程中计算密集的阶段之一。理解 bzImage 的自解压结构、重定位原理以及不同解压算法的性能特征,是进行启动时间压减的基础。在实际优化中,选择 lz4 或 zstd 算法通常能带来 20-60% 的解压时间缩减,且无需修改内核逻辑,是性价比极高的优化手段。