vmlinux
vmlinux:$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
$(call vmlinux-modpost)
$(call if_changed_rule, vmlinux__)
$(Q) rm –f .old_version
quiet_cmd_vmlinux__ ?= LD $@
cmd_vmlinux__ ?= $(LD) $(LDFLAGS_vmlinux) –o $@ -T $(vmlinux-lds) /
$(vmlinux-init) –( $(vmlinux-main) -) $(filter-out $(vmlinux-lds)) /
$(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE, $^
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
在arch/x86/Makefile下有
head-y := arch/x86/kernel/head_$(BITS).o <= head_32.S
a) 初始化段寄存器、clear BSS、将实模式下的boot_params相关数据拷贝到保护模式下
b) 建立临时内核页表、启用分页
c) 建立进程0的内核堆栈stack_start???
d) setup_idt
e) 识别处理器、建立GDT、IDT
f) i386_start_kernel
head-y += arch/x86/kernel/head$(BITS).o <= head32.c
i386_start_kernel => start_kernel
head-y += arch/x86/kernel/head.o <= head.c
head-y += arch/x86/kernel/init_task.o <= init_task.c
struct task_struct init_task = INIT_TASK(init_task)
DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS;
init-y = init/version.o、main.o
其中main.o中定义了start_kernel()
drivers-y = drivers/ sound/ firmware/
net-y = net/
libs-y = lib/ arch/x86/lib
core-y = usr/ kernel/ mm/ fs/ ipc/ security/ crypto/ block/ arch/x86
根据上面列出的vmlinux的构造规则可以看出,源码根目录下的vmlinux就是内核从实模式进入保护模式后将要执行的代码,head-y先于init-y执行,它会启用分页,建立全局描述符表和中断描述符表,并跳转到start_kernel()函数。
start_kernel()函数是定义在init-y中的main.c函数中的。start_kernel()函数完成Linux内核的初始化工作,其中的初始化函数大多定义在core-y、libs-y、drivers-y、net-y中。可见vmlinux包含了内核主要功能并完成了内核初始化。
至此Linux内核完成启动,现在正在运行。
bzImage
在arch/x86/Makefile下
boot := arch/x86/boot/
bzImage: vmlinux
$(Q) $(MAKE) $(build)=$(boot) $(boot)/bzImage
$(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed, image)
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin /
$(ROOT_DEV) > $@
在arch/x86/boot/Makefile下
setup.elf: setup.ld $(SETUP_OBJS) FORCE
$(call if_changed, ld)
OBJCOPYFLAGS_setup.bin := -O binary
setup.bin: setup.elf FORCE
$(call, if_changed, objcopy)
SET_OBJS即由setup-y组成
setup-y = a20.o、bioscall.o、cmdline.o、copy.o、cpu.o、cpucheck.o /
early_serial_console.o、header.o、main.o、pm.o、pumjump.o…
setup-y中的文件组成了内核在实模式下执行的代码。
header.S要做的工作包括为C语言运行建立运行环境,准备好堆和栈;检查setup的signature;清楚BSS段;跳转到main执行。
main.c用来初始化硬件设备并未内核程序的执行建立环境内存检测、键盘、视频、…;调用go_to_protected_mode(),设置boot_params。
pm.c中定义了go_to_protected_mode
a) enable_a20 开始20地址线,这样就可以访问2M空间
b) reset_coprocessor 复位协处理器
c) mask_all_interrupts() 屏蔽所有中断
d) setup_idt()、setup_gdt()
e) protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params+ds()<<4)
(note:code32_start=0x100000也就是vmlinux.bin所在的位置)
pmjump.S开启保护模式,设置段寄存器,长跳转设置代码段寄存器,最后跳转到code32_start处,在bzImage中,这就是vmlinux.bin的位置,首先进入的是compressed/head_32.S(后面详述)
boot/compressed/head_32.S中的startup_32调用decompress_kernel()解压函数(misc.o)将vmlinux解压缩到1M处,jmp*%ebp跳转到解压后的vmlinux入口,就是上面源码根目录下的vmlinux,上面已经描述了后面的工作。
根据上面的分析可知,setup.bin的主要工作是为C语言运行建立运行环境,获取启动参数,检测硬件、建立临时全局描述符表,中中断描述符表,从实模式转换到保护模式并加载vmlinux。
这个加载不是直接加载,为了使得bzImage(3.1M)尽可能的小,会将vmlinux(19M)进行压缩所以在bzImage中的是vmlinux.bin(3.1M),它包含了vmlinux的压缩版,和解压函数,解压函数就在compressed/目录下。
在编译内核的时候,会把主内核(不包括setup部分)编译成vmlinux,然后make到compressed目录下,在这里会对vmlinux进行打包压缩,并把解压函数一起打包成一个vmlinux.bin。
这个过程是:
将vmlinux复制为二进制格式,并strip掉调试信息,得到的vmlinux.bin大小为16M
objcopy –R .comment –S vmlinux vmlinux.bin
利用gzip工具压缩vmlinux.bin得到vmlinux.bin.gz大小为3.1M
gzip –c vmlinux.bin > vmlinux.bin.gz
利用mkpiggy内建工具
./mkpiggy vmlinux.bin.gz > piggy.S
最后把解压函数misc.o和其他文件一起链接成compressed/vmlinux(5.1M)
ld –T vmlinux.ldds head_32.o misc.o string.o cmdline.o early_serial_console.o piggy.o –o vmlinux
回到上级目录下,Make将会执行
objcopy –O binary –R .note –R .comment –S compressed/vmlinux vmlinux.bin
这就完成了vmlinux.bin的构造,最终的vmlinux.bin大小为3.1M
将setup.bin(15K)和vmlinux.bin(3.1M) build在一起就形成了bzImage(3.1M)。
LILO调用一个BIOS例程从磁盘装入内核映像的初始部分,即将内核映像的第一个512字节从地址0x00090000开始存入RAM中,偏移0x200处开始的一条短跳转指令跳转到start_of_setup处。调用另一个BIOS例程从磁盘装载剩余的映像,并把内核映像放入从0x00100000开始的RAM中。