不当之处,尽请大家斧正。
一. 将内核映像加载到内存
1) pc / grub环境
a)bios初始化结束后,根据用户的配置,选择第一个启动设备,例如第一块硬盘,将其0扇区(即MBR)的内容(512字节)拷贝到内存中,然后执行其中的内容。
b)这512字节的内容,负责将grub加载到内容中,将控制权交给grub。
c) grub根据配置(/boot/grub/grub.conf),将bzImage(例如/boot/vmlinuz-2.6.18-194.el5)加载到内核中,
同时将initrd文件(例如/boot/initrd-2.6.18-194.el5.img)也加载到内核中。
然后grub执行bzImage起始处的内容。
2) arm / u-boot环境
a)cpu根据硬件的配置,按一定的顺序。依次尝试从sd/tf card、nand flash、spi flash等设备的起始位置(即boot sector)加载前4K字节的数据到内存中执行。
b)这前4K字节的内容,负责将u-boot加载到内容中,将控制权交给u-boot。
c) u-boot根据配置(环境变量),将zImage加载到内核中(如果是nand/nor flash,一般很简单,zImage就在flash的某个区间中,u-boot不需要支持文件系统的访问)。
同时将initrd文件也加载到内核中(同样,如果是nand/nor flash,也就简单了,initrd文件就在flash的某个区间中,u-boot不需要支持文件系统的访问)。
然后u-boot执行zImage起始处的内容。
二、内核映像究竟是什么
上一步,已经进行到boot loader执行内核映像的环节了。
zImage或bzImage,就是所谓的内核映像。
他是通过如下步骤生成的:
1) 编译得到原始的内核目标文件kernel.o
这个文件的文件名,在不同架构的makefile中叫法可能不尽相同。我们就叫他 kernel.o 好了。
这是一个elf格式的文件。
具体文件名,参见arch\xxx\boot\compressed\Makefile
另外,我们下文中所使用的文件名,也可能与makefile中的命名不一致。
不是不想一致,而是不同架构makefile中的命名确实不尽相同。
因此,我们这里为了表达的方便,就重新统一命名啦。
2) 用objcopy工具给 kernel.o 减肥,得到kernel.bin
经过objcopy处理后,得到只剩下内核代码与数据的文件kernel.bin。
注意,kernel.bin已经不是elf文件了。
3) 用gzip压缩 kernel.bin 得到 piggy.gz
4) 创建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
5) 将piggy.o及其他东东链接成vmlinux
将head.o、misc.o、piggy.o 链接成vmlinux。
对于arm来说,除了head.o、misc.o,可能还会将其他一些用到的东东链接进去。
6) 生成最终的内核映像文件
a) 对于arm来说,步骤如下:
用objcopy工具给 vmlinux 减肥,得到zImage
vmlinux是elf文件,经过objcopy处理后,得到只剩下代码与数据的文件zImage。
注意,zImage已经不是elf文件了。
三、内核映像的执行
a) 对arm而言
zImage起始处的内容就是head.o中的代码。
head.o(对应arch\arm\boot\compressed\head.S)中包含一些初始化代码,进行一些系统级的处理工作。
然后head.o调用misc.o(对应arch\arm\boot\compressed\misc.c)中的解压函数,将原始内核kernel.bin解压出来。
解压完成后,就跳转到kernel.bin的起始处运行。
kernel.bin的起始处的内容,就是arch\arm\kernel\head.S。
这是一个汇编代码写的文件,ENTRY(stext)是这段代码的入口。
这段代码进行一系列的初始化工作,最后调用start_kernel函数(位于init\main.c中),进入了C代码的世界。