LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

zImage的压缩、解压以及性能对比

关键词:Image,zImage,piggy,gzip,lzo,xz,lzma等等。

1. zImage的压缩

zImage是Image经过压缩后,加上解压缩头后生成的文件:Image -> piggy_data -> piggy.o ->vmlinux(compressed) -> zImage。

Image经过压缩生成piggy_data:

cmd_arch/arm/boot/compressed/piggy_data := { cat arch/arm/boot/compressed/../Image | lzop -9; printf \\070\\266\\126\\000; } > arch/arm/boot/compressed/piggy_data

piggy.o包含了piggy_data数据:

/* SPDX-License-Identifier: GPL-2.0 */
    .section .piggydata,#alloc
    .globl    input_data
input_data:
    .incbin    "arch/arm/boot/compressed/piggy_data"
    .globl    input_data_end
input_data_end:

cpmpressed/vmlinux包含了piggy.o:

复制代码
cmd_arch/arm/boot/compressed/vmlinux := arm-none-linux-gnueabihf-ld  -EL   --defsym _kernel_bss_size=100900 --no-undefined -X
-T arch/arm/boot/compressed/vmlinux.lds
arch/arm/boot/compressed/head.o
arch/arm/boot/compressed/piggy.o
arch/arm/boot/compressed/misc.o
arch/arm/boot/compressed/decompress.o
arch/arm/boot/compressed/string.o
arch/arm/boot/compressed/hyp-stub.o
arch/arm/boot/compressed/lib1funcs.o
arch/arm/boot/compressed/ashldi3.o
arch/arm/boot/compressed/bswapsdi2.o
-o arch/arm/boot/compressed/vmlinux
复制代码

从compressed/vmlinux.lds可以看出piggydata段放在rodata之后:

复制代码
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    *(.data)
  }
  . = 0;
  _text = .;
  .text : {
    _start = .;
    *(.start)
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.glue_7t)
    *(.glue_7)
  }
  .table : ALIGN(4) {
    _table_start = .;
    LONG((2))
    LONG((0x5a534c4b))
    LONG((__piggy_size_addr - _start))
    LONG((_kernel_bss_size))
    LONG(0)
    _table_end = .;
  }
  .rodata : {
    *(.rodata)
    *(.rodata.*)
    *(.data.rel.ro)
  }
  .piggydata : {
    *(.piggydata)
    __piggy_size_addr = . - 4;
  }
  . = ALIGN(4);
  _etext = .;
  .got.plt : { *(.got.plt) }
  _got_start = .;
  .got : { *(.got) }
  _got_end = .;
  .pad : { BYTE(0); . = ALIGN(8); }
  _edata = .;
  .image_end (NOLOAD) : {
    _edata_real = .;
  }
  _magic_sig = (0x016f2818);
  _magic_start = (_start);
  _magic_end = (_edata);
  _magic_table = (_table_start - _start);
  . = ALIGN(8);
  __bss_start = .;
  .bss : { *(.bss) }
  _end = .;
  . = ALIGN(8);
  .stack : { *(.stack) }
...
}
ASSERT(_edata_real == _edata, "error: zImage file size is incorrect");
复制代码

从compressed/vmlinux中拷贝zImage:

cmd_arch/arm/boot/zImage := arm-none-linux-gnueabihf-objcopy -O binary -R .comment -S  arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage

2. zImage的解压

 由compressed/vmlinux.lds可知,zImage启动从arch/arm/boot/compressed/head.S中start开始:

复制代码
start
  ->__hyp_stub_install
  ->not_angel
  ->restart
  ->wont_overwrite     ->not_relocated--清空bss段,并配置好C执行环境。r0-kernel输出起始地址;r1/r2-分配内存的其实和结束地址;r3-架构类型ID。     ->decompress_kernel--对内核进行解压。       ->arch_decomp_setup       ->do_decompress         ->__decompress           ->unlzo--调用对应的解压缩算法进行解压。 ->cache_clean_flush--刷新并清空cache。 ->cache_off--关闭cache。 ->__enter_kernel--准备好寄存器跳转到解压后的内核。
复制代码

decompress_kernel是解压Image入口:

  • output_start是指向zImage起始地址+TEXT_OFFSET的地址,对于0x80000000来说即为0x80008000。
  • free_mem_ptr_p和free_mem_ptr_end_p指向分配的一段64KB空间起始和结束地址,给compressed中malloc/free使用。
  • arch_id为架构ID,此为0x80004000。
复制代码
void
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
        unsigned long free_mem_ptr_end_p,
        int arch_id)
{
    int ret;

    output_data        = (unsigned char *)output_start;
    free_mem_ptr        = free_mem_ptr_p;
    free_mem_end_ptr    = free_mem_ptr_end_p;
    __machine_arch_type    = arch_id;

    arch_decomp_setup();

    putstr("Uncompressing Linux...");
    ret = do_decompress(input_data, input_data_end - input_data,
                output_data, error);--input_data和input_data_end实在piggy.S中定义的piggy_data起始和结束地址。此处将piggy_data解压到TEXT_OFFSET之下那个的地方。
    if (ret)
        error("decompressor returned an error");
    else
        putstr(" done, booting the kernel.\n");
}
复制代码

__enter_kernel未进入内核做准备,然后跳转到解压结果首地址。

r0为0,;r1架构编号;r2位atags指针。r4保存解压后内核跳转地址,最后跳转到解压后的内核。

__enter_kernel:
        mov    r0, #0            @ must be 0
        mov    r1, r7            @ restore architecture number
        mov    r2, r8            @ restore atags pointer
 ARM(        mov    pc, r4        )    @ call kernel
 M_CLASS(    add    r4, r4, #1    )    @ enter in Thumb mode for M class
 THUMB(        bx    r4        )    @ entry point is always ARM for A/R classes

3. 不同算法性能对比

在一FPGA环境下测试不同解压算法的耗时:

解压类型 Gzip LZMA XZ LZO LZ4
镜像大小 Image 17966904
zImage 7320936
Image 17966904
zImage 5037832
Image 17966904
zImage 4883408
Image 17966904
zImage 79859524
Image 17966904

zImage 8538088
耗时(秒) 188 1812 1146 56 142
由上可知,在解压速度慢的FPGA环境下,LZO是最合适的算法。如果对于占用空间要求较高,XZ和LZMA比较合适。

posted on   ArnoldLu  阅读(903)  评论(2编辑  收藏  举报

相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
历史上的今天:
2018-05-20 Linux进程管理 (9)实时调度类分析,以及FIFO和RR对比实验

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示