在客户使用我司芯片,选择内核自解压算法为XZ和LZMA后,发现解压时长变得非常慢,约40S。我们拿到了一块rk3288 fire-fly的板子,也将其内核配置为XZ压缩,经过测试,RK的板子做XZ的解压只需要2s,对比我司的表现,RK速度非常之快。
传统上,ARM使用压缩内核。这样做有两个主要原因:
想要知道为何解压速度这么慢,首先就得知道内核是如何被压缩的,经过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)