1、bzImage的生成过程
上图是内核镜像bzimage的构建过程,包括如下过程:
1、构建内核镜像vmlinux,根据在内核配置阶段生成的.config将系统核心和built-in的系统组件进行编译,最后根据指定的链接脚本arch/i386/kernel/vmLinux.lds生成vmLinux文件。
2、对vmlinux进行瘦身并进行压缩,通过gzip对vmlinux.bin进行压缩。
3、构建包含解压缩代码的vminux镜像,使用连接器ld将包含压缩的系统核心的文件piggy.o与head.o,misc.o链接生成新的文件boot/compressed/vmLinux.
4、对新生成的vmlinux再次瘦身。
5、构建内核镜像bzImage,利用内核镜像构建工具build将bootsect,setup,vmLinux.bin三个文件依次放到bzImage文件中去。bootsect是一个引导扇区,一般情况下是512字节,最后两个字节为55AA.然后填充setup部分,补足512字节。
2、内核引导过程中涉及的文件
(1)arch/i386/boot/bootsect.S
(2)arch/i386/boot/setup.S
(3)arch/i386/boot/compressed/head.S
(4)arch/i386/boot/compressed/misc.c
(5)arch/i386/kernel/head.S
(6)init/main.c
3、第一个文件(1)arch/i386/boot/bootsect.S,在另一篇i386的引导协议中说过,这一部分,不再具有引导能力,主要就是传递一些参数。所以只看一小部分源码,如下:
SETUPSECTS= 4/* default nr of setup-sectors */
BOOTSEG = 0x07C0/* original address of boot-sector */
INITSEG = DEF_INITSEG/* we move boot here - out of the way */
SETUPSEG = DEF_SETUPSEG/* setup starts here */
SYSSEG = DEF_SYSSEG/* system loaded at 0x10000 (65536) */
SYSSIZE = DEF_SYSSIZE/* system size: # of 16-byte clicks */
/* to be loaded */
ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
setup_sects:.byte SETUPSECTS
root_flags: .word ROOT_RDONLY
syssize: .word SYSSIZE
swap_dev: .word SWAP_DEV
ram_size: .word RAMDISK
vid_mode: .word SVGA_MODE
root_dev: .word ROOT_DEV
boot_flag: .word 0xAA55必须以此结尾
4、第二个文件(2)arch/i386/boot/setup.S,主要是探测系统的资源。在引导程序如GRUB装载完内核后,系统的控制全就交给此文件。此文件的主要工作是通过BIOS获得系统的配置信息,并将信息存到对应的内存位置,等待系统初始化时使用。
所做工作如下:
(1)、探测系统配置信息,并将探测的信息存到相应的内存单元中,
(2)、设置系统的全局描述表和中断描述符表。
(3)、进入保护模式
(4)、跳转到__BOOT_DS:0x100000,该地址对应于物理地址0x100000。此处正是经过压缩的内核的位置。
源码就不列出了,但列出下面这段本文件开头的注释,这段注释已经把此文件要做的工作,详细说明了。
/*
* setup.S Copyright (C) 1991, 1992 Linus Torvalds
*
* setup.s is responsible for getting the system data from the BIOS,
* and putting them into the appropriate places in system memory.
* both setup.s and system has been loaded by the bootblock.
*
* This code asks the bios for memory/disk/other parameters, and
* puts them in a "safe" place: 0x90000-0x901FF, ie where the
* boot-block used to be. It is then up to the protected mode
* system to read them from there before the area is overwritten
* for buffer-blocks.
*
* Move PS/2 aux init code to psaux.c
* ([email protected]) 03Oct92
*
* some changes and additional features by Christoph Niemann,
* March 1993/June 1994 ([email protected])
*
* add APM BIOS checking by Stephen Rothwell, May 1994
* ([email protected]
rg.au)
*
* High load stuff, initrd support and position independency
* by Hans Lermen & Werner Almesberger, February 1996
* <[email protected]>, <[email protected]>
*
* Video handling moved to video.S by Martin Mares, March 1996
* <[email protected]>*
* Extended memory detection scheme retwiddled by [email protected] (david
* parsons) to avoid loadlin confusion, July 1997
*
* Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
* <[email protected]>
*
* Fix to work around buggy BIOSes which dont use carry bit correctly
* and/or report extended memory in CX/DX for e801h memory size detection
* call. As a result the kernel got wrong figures. The int15/e801h docs
* from Ralf Brown interrupt list seem to indicate AX/BX should be used
* anyway. So to avoid breaking many machines (presumably there was a reason
* to orginally use CX/DX instead of AX/BX), we do a kludge to see
* if CX/DX have been changed in the e801 call and if so use AX/BX .
5、此两个文件主要是压缩内核镜像的解压缩。这两个文件调用gnuzip解压缩内核piggy.o解压缩并释放到逻辑地址__BOOT_DS:_PHYSICAL_START处,并跳转到该地址开始系统的初始化过程。
(3)arch/i386/boot/compressed/head.S
(4)arch/i386/boot/compressed/misc.c
6、(5)arch/i386/kernel/head.S本文件为系统建立一个初步的页表,启用分页机制;装载了新的GDT、IDT,设置栈指针寄存器指向0号进程init_task内核态的栈底。最后跳到位于文件/init/main.c的系统初始化函数start_kernel,继续进行系统的初始化工作。列出其中一段源码:
/*
* Initialize page tables. This creates a PDE and a set of page
* tables, which are located immediately beyond _end. The variable
* init_pg_tables_end is set up to point to the first "safe" location.
* Mappings are created both at virtual address 0 (identity mapping)
* and PAGE_OFFSET for up to _end+sizeof(page tables)+INIT_MAP_BEYOND_END.
*
* Warning: don't use %esi or the stack in this code. However, %esp
* can be used as a GPR if you really need it...
*/
page_pde_offset = (__PAGE_OFFSET >> 20);
movl $(pg0 - __PAGE_OFFSET), %edi
movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
movl $0x007, %eax/* 0x007 = PRESENT+RW+USER */
10:
leal 0x007(%edi),%ecx/* Create PDE entry */
movl %ecx,(%edx)/* Store identity PDE entry */
movl %ecx,page_pde_offset(%edx)/* Store kernel PDE entry */
addl $4,%edx
movl $1024, %ecx
11:
stosl
addl $0x1000,%eax
loop 11b
/* End condition: we must map up to and including INIT_MAP_BEYOND_END */
/* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp
cmpl %ebp,%eax
jb 10b
movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
#ifdef CONFIG_SMP
xorl %ebx,%ebx/* This is the boot CPU (BSP) */
jmp 3f
7、(6)init/main.c
该函数完成绝大多数的初始化,然后有0好进程init_task负责初始化过程中的全任务,包括内存管理、终端等。完成系统的初始化,将控制权交给1号进程init,该进程负责对系统剩余部分的初始化。
x86系统引导(2)的链接