linux内核压缩生成过程

发布时间:2024年01月22日

问题描述

在客户使用我司芯片,选择内核自解压算法为XZ和LZMA后,发现解压时长变得非常慢,约40S。我们拿到了一块rk3288 fire-fly的板子,也将其内核配置为XZ压缩,经过测试,RK的板子做XZ的解压只需要2s,对比我司的表现,RK速度非常之快。

为什么要使用压缩后的内核?

传统上,ARM使用压缩内核。这样做有两个主要原因:

  1. 它可以节省闪存或其他保存内核的存储介质上的空间,而内存就是金钱。
  2. 加载速度更快,因为解压缩运行所需的时间比从存储介质(例如闪存)传输未压缩图像所需的时间短。

内核压缩 zImage的生成方式

想要知道为何解压速度这么慢,首先就得知道内核是如何被压缩的,经过uboot引导后又是如何走到解压的。

linux内核在编译初期会被编译为vmlinux或者vmlinuz这样的ELF可执行程序,这个就是原始的未经任何处理加工的原版内核elf文件。再经过objcopy后得到可烧录的镜像格式Image(过程一),原则上Image就可以直接被烧录到Flash上进行启动执行(类似于u-boot.bin)。再经过相应的压缩算法的压缩,变为压缩后的Image(过程二)。在这个压缩后Image的基础上,在其头部增加一段解压缩代码,从而形成zImage(过程三)。
在这里插入图片描述
对于过程一,在kernel/arch/arm/Makefile中有如下规则:

targets := Image zImage xipImage bootpImage uImage

$(obj)/Image: vmlinux FORCE
	$(call if_changed,objcopy)

if_changed是后面命令调用的基础,当这条规则被使用时它将检查哪些文件需要更新,或命令行被改变。后面这种情况将迫使重新编译编译选项被改变的执行文件。if_changed的目标对象必须列举在$(targets)中,否则命令行检查将失败,目标一直会编译。
objcopy这个命令定义在kernel/scripts/Makefile.lib中

quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@

对于过程二,会调用到kernel/arch/arm/compressed目录下的Makefile

$(obj)/compressed/vmlinux: $(obj)/Image FORCE
	$(Q)$(MAKE) $(build)=$(obj)/compressed $@

kernel/arch/arm/compressed/Makefile下有如下规则:

OBJS	+= misc.o decompress.o

targets       := vmlinux vmlinux.lds piggy_data piggy.o \
		 lib1funcs.o ashldi3.o bswapsdi2.o \
		 head.o $(OBJS)

$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
		$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
		$(bswapsdi2) $(efi-obj-y) FORCE
	@$(check_for_multiple_zreladdr)
	$(call if_changed,ld)
	@$(check_for_bad_syms)

$(obj)/piggy_data: $(obj)/../Image FORCE
	$(call if_changed,$(compress-y))

$(obj)/piggy.o: $(obj)/piggy_data

先将Image经过压缩,变为piggy_data,这里调用了$(compress-y),compress-y是在哪里赋值的?
还是在这一级别的Makefile中,有:
CONFIG_KERNEL_XXX这个宏在defconfig中配置,这里我们配置为XZ,则CONFIG_KERNEL_XZ=y

compress-$(CONFIG_KERNEL_GZIP) = gzip
compress-$(CONFIG_KERNEL_LZO)  = lzo
compress-$(CONFIG_KERNEL_LZMA) = lzma
compress-$(CONFIG_KERNEL_XZ)   = xzkern
compress-$(CONFIG_KERNEL_LZ4)  = lz4

再将piggy_data编译为piggy.o,然后和其他.o文件一块链接起来(其中包括了解压缩的代码misc.o,decompress.o,),依赖的链接脚本就是vmlinux.lds!
来瞅瞅vmlinux.lds:
这里规定了各个段编译后的所在地址,和先后位置,还有一些帮助重定位的段GOT等,重定位部分还理解不透彻,后续有机会再学习,出一版文章。


OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    /*
     * Discard any r/w data - this produces a link error if we have any,
     * which is required for PIC decompression.  Local data generates
     * GOTOFF relocations, which prevents it being relocated independently
     * of the text/got segments.
     */
    *(.data)
  }

  . = TEXT_START;
  _text = .;

  .text : {
    _start = .;
    *(.start)
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.glue_7t)
    *(.glue_7)
  }
  .rodata : {
    *(.rodata)
    *(.rodata.*)
  }
  .piggydata : {
    *(.piggydata)
  }

  . = ALIGN(4);
  _etext = .;

  .got.plt		: { *(.got.plt) }
  _got_start = .;
  .got			: { *(.got) }
  _got_end = .;

  /* ensure the zImage file size is always a multiple of 64 bits */
  /* (without a dummy byte, ld just ignores the empty section) */
  .pad			: { BYTE(0); . = ALIGN(8); }
  _edata = .;

  _magic_sig = ZIMAGE_MAGIC(0x016f2818);
  _magic_start = ZIMAGE_MAGIC(_start);
  _magic_end = ZIMAGE_MAGIC(_edata);

  . = BSS_START;
  __bss_start = .;
  .bss			: { *(.bss) }
  _end = .;

  . = ALIGN(8);		/* the stack must be 64-bit aligned */
  .stack		: { *(.stack) }

  .stab 0		: { *(.stab) }
  .stabstr 0		: { *(.stabstr) }
  .stab.excl 0		: { *(.stab.excl) }
  .stab.exclstr 0	: { *(.stab.exclstr) }
  .stab.index 0		: { *(.stab.index) }
  .stab.indexstr 0	: { *(.stab.indexstr) }
  .comment 0		: { *(.comment) }
}

好了,到此之后,vmlinux已经创建出来,退回到上一级Makefile,可以找到这样一条规则:
这里再次执行了objcopy,创建出了zImage。

$(obj)/zImage:	$(obj)/compressed/vmlinux FORCE
	$(call if_changed,objcopy)
文章来源:https://blog.csdn.net/weixin_43604927/article/details/134553545
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。