i.MX6ULL终结者Linux内核启动(一):Linux内核自解压过程

Linux内核有两种映像格式:一种是非压缩内核,叫Image,另一种是它的压缩版本,叫zImage。zImage是Image经过压缩形成的,所以它的大小比Image小。但为了能使用zImage,必须在它的开头加上解压缩的代码,将zImage解压缩之后才能执行,因此它的执行速度比Image要慢一些。

内核压缩和解压缩代码都在目录kernel/arch/arm/boot/compressed,编译完成后将产生head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o这几个文件,head.o是内核的头部文件,负责初始设置;misc.o将主要负责内核的解压工作,它在head.o之后;piggy.gzip.o是一个中间文件,其实是一个压缩的内核(kernel/vmlinux),只不过没有和初始化文件及解压文件链接而已;vmlinux是没有(zImage是压缩过的内核)压缩过的内核,就是由piggy.gzip.o、head.o、misc.o组成的,而decompress.o是为支持更多的压缩格式而新引入的。

在uboot完成系统引导将Linux内核加载到内存之后,调用do_bootm_linux(),这个函数将跳转到kernel的起始位置。如果kernel没有被压缩,就可以启动了。如果kernel被压缩过,则要进行解压,在压缩过的kernel头部有解压程序。压缩过的kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又调用arch_decomp_setup()进行设置,然后调用gunzip()将内核放于指定的位置。

函数decompress_kernel实现的功能:解压缩代码位于kernel/lib/inflate.c,inflate.c是从gzip源程序中分离出来的,包含了一些对全局数据的直接引用,在使用时需要直接嵌入到代码中。gzip压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码, 在解压时需要一个至少为32K字节的解压缓冲区,它定义为window[WSIZE]。inflate.c使用get_byte()读取输入文件,它被定义成宏来提高效率。输入缓冲区指针必须定义为inptr,inflate.c中对之有减量操作。inflate.c调用flush_window()来输出window缓冲区中的解压出的字节串,每次输出长度用outcnt变量表示。在flush_window()中,还必须对输出字节串计算CRC并且刷新crc变量。在调用gunzip()开始解压之前,调用makecrc()初始化CRC计算表。最后gunzip()返回0表示解压成功。在内核启动时一般会看到这样的输出:
UncompressingLinux…done, booting the kernel.

当然有的内核没有这样的输出,是没有这一条打印语句。i.MX6ULL终结者Linux内核启动(一):Linux内核自解压过程