红联Linux门户
Linux帮助

Linux内核映像的生成过程

发布时间:2016-05-11 15:32:02来源:linux网站作者:孙明保

本人水平相当有限,不当之处,欢迎指正。


在各种各样使用Linux的计算机系统中,系统启动的早期阶段,通常都会有这样一个环节,boot loader程序将内核映像加载到内存中,然后跳转到映像的起始位置开始执行。

接下来,内核就跑起来了。

那么,这个内核映像到底是个什么东东呢。本文就来简单介绍一下Linux内核映像的生成过程。

本文依据的内核版本 2.6.32


(一)  编译内核源码得到原始的内核目标文件kernel.o

这其实也就是真正意义上的内核了。系统中实际运行着的内核,其实就是他了。

这是一个elf格式的目标文件。

这个elf文件的入口代码在一个汇编文件中,一般叫head.S,用来实现最初的初始化,然后将执行流程转移到统一的内核初始化入口函数start_kernel(C代码)。

对于arm而言,入口就是arch\arm\kernel\head.S中的ENTRY(stext)。

对于32位X86,入口就是arch\X86\kernel\head_32.S中的ENTRY(startup_32),然后转到C函数i386_start_kernel,再转到start_kernel。

内核目标文件的文件名,在不同架构的makefile中叫法可能不尽相同。我们这里就叫他 kernel.o 好了。
不同架构下的具体文件名,参见arch\xxx\boot\compressed\Makefile

另外,我们下文中所使用的文件名,也可能与makefile中的命名不一致。
不是不想一致,而是不同架构makefile中的命名确实不尽相同。
因此,我们这里为了表达的方便,就重新统一命名啦。

好了,kernel.o虽然是真正的内核,但他不是一下子就能运行起来的。就像火箭的发射需要发射架一样。

kernel.o要想跑起来,也需要再搭几个支架。

接下来的事情,就是一步步地在搭这个架子。


(二)  用objcopy工具给 kernel.o 减肥,得到kernel.bin

经过objcopy处理(其实就是二进制化)后,得到只剩下纯代码与数据的文件kernel.bin(raw binary格式)。

注意,kernel.bin已经不是elf文件了。他里面只有纯粹的指令与数据。

只所以这样做,是因为启动阶段的系统,软件环境很简陋,没有代码来分析elf文件的结构。

要加载一个程序,就是将他拷到内存中某个位置,然后跳到该位置执行。

因此,程序必须经过精心的链接,再去除所有不需要的信息,只留下纯粹的指令与数据。

要将一个elf文件二进制化,通过命令“objcopy -O binary  src_file  dst_file”即可。


(三)  用gzip压缩 kernel.bin 得到 piggy.gz


(四)  创建piggy.o用于包含piggy.gz文件的全部数据

piggy.o是一个elf文件,他内部包含了piggy.gz文件的全部数据。

piggy.o的目的,就是为了将piggy.gz作为数据与内核映像的其他部分进行链接。

在arm下,通过对arch\xxx\boot\compressed\piggy.S文件的编译,实现了piggy.o的创建。

在i386下,通过arch\i386\boot\compressed\vmlinux.scr链接脚本以及

arch\i386\boot\compressed\Makefile中类似如下的依赖关系,直接链接得到了piggy.o

$(obj)/piggy.o: $(src)/vmlinux.scr $(obj)/piggy.gz


(五)  将piggy.o及其他东东链接成vmlinux

将piggy.o 与一些外围代码head.o、misc.o、链接成vmlinux。

外围代码,主要用于解压内核,并将执行流转移到内核的入口代码处执行。

因此,对于更上层的引导者来说,vmlinux是一个可以自解压并自行运行的内核。

对于32位x86来说,外围代码head.o、misc.o对应于arch\x86\boot\compressed下面,分别对应于head_32.S,misc.c

head_32.S是外围代码的入口,他调用misc.c中的解压例程,将内核解压,得到真正的内核,然后跳转到内核入口代码执行。


(六)  生成最终的内核映像文件

a) 对于arm来说,步骤如下:

用objcopy工具给 vmlinux 减肥,得到zImage

vmlinux是elf文件,经过objcopy处理后,得到只剩下代码与数据的文件zImage(raw binary格式)。

注意,zImage已经不是elf文件了。

b) 对于x86来说,步骤与arm略有不同。

用objcopy工具给 vmlinux 减肥,得到vmlinux.bin(raw binary格式)。

再将bootsect(raw binary格式) 、setup(raw binary格式)与vmlinux.bin拼接到vmlinux.bin前面,得到bzImage。

其中,bootsect对应于arch\i386\boot\bootsect.S,setup对应于arch\i386\boot\setup.S。

这两个文件有什么用呢?

(1) bootsect.S

其实是用于写到软盘的0扇区中(512字节),用于从软盘启动Linux内核。在2.6的内核中,这个文件已经不起作用了。

因此,2.6的内核中如果跑到他,他就简单打印一点错误信息,告诉用户需要由boot loader加载内核。

然后让用户按任意键重启。

而boot loader(例如,grub)在加载内核后,并没有跳转到内核映像起始位置执行,而是跳过了前面的512字节,即跳过了bootsect.S的内容。

(2) setup.S

一般来说,如果是从arm u-boot环境启动。u-boot会将单板基本硬件信息放到一个特定位置,然后内核可以由此获取硬件信息。

而对于pc机来说,则是由setup.S来获取(通过bios)基本硬件信息,放到合适的位置,然后内核再由此获取硬件信息。

如果安装内核的话,新编译的bzImage将被拷贝到类似/boot/vmlinuz-2.6.18-194.el5这样的路径,后者的路径则有可能同时再被更新到/boot/vmlinuz软链接中。这样一来,/boot/vmlinuz总是指向系统中最新安装的内核。


本文永久更新地址:http://www.linuxdiyf.com/linux/20540.html