1. 顶层Makefile分析
核心代码:
all: vmlinux
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
# vmlinux is built from the objects selected by $(vmlinux-init) and(vmlinux-main)
vmlinux-init := $(head-y) $(init-y)
head-y := arch/x86/kernel/head_$(BITS).o
head-y += arch/x86/kernel/head$(BITS).o
head-y += arch/x86/kernel/init_task.o
***************************************
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
从上可以发现vmlinux-init所依赖的文件有arch/x86/kernel/head_32.o,arch/x86/kernel/head32.o,arch/x86/kernel/init_task.o,init/built-in.o
分析vmlinux-main部分
代码:
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
依赖文件包括:usr/ built-in.o,kernel/ built-in.o,mm /built-in.o, fs/built-in.o, ipc/built-in.o, security/built-in.o built-in.o, security/built-in.o,crypto/built-in.o,block/ built-in.o。
libs-y := lib/
libs-y := $(libs-y1) $(libs-y2)
libs-y1:= $(patsubst %/, %/lib.a, $(libs-y))
libs-y2:= $(patsubst %/, %/built-in.o, $(libs-y))
依赖文件包括:lib/lib.a,lib/ built-in.o。
drivers-y := drivers/ sound/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
依赖文件包括:lib/lib.a,lib/ built-in.o。
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y))
依赖文件包括:net/ built-in.o
2: compress下的vmlinux和bzImage生成过程分析
vmlinux是内核的执行文件,在X86中我们通常使用bzImage,通过分析makefile(arch/x86/boot/compressed/Makefile)来分析从vmlinux到bzImage 的生成过程。
$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
解析:通过objcopy来把vmlinux生成vmlinux.bin。objcopy把一种目标文件中的内容复制到另一种类型的目标文件中.
$(obj)/vmlinux.relocs: vmlinux $(obj)/relocs FORCE
$(call if_changed,relocs)
解析:使用relocs命令把vmlinux的重定位信息提取出来保存到vmlinux.relocs中。
vmlinux.bin.all-y := $(obj)/vmlinux.bin
vmlinux.bin.all-$(CONFIG_RELOCATABLE)+=
$(obj)/vmlinux.relocs
quiet_cmd_relocbin = BUILD $@
cmd_relocbin = cat $(filter-out FORCE,$^) > $@
解析:把vmlinux.bin和vmlinux.relocs合并到vmlinux.bin.all-y里面。
$(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE
$(call if_changed,relocbin)
解析:生成vmlinux.bin.all
$(obj)/: $(obj)/vmlinux.bin.all FORCE
$(call if_changed,gzip)
解析:把vmlinux.bin.all压缩成vmlinux.bin.gz。
$(obj)/piggy.o:$(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
$(call if_changed,ld)
解析:把/vmlinux.bin.gz文件链接成piggy.o文件。
$(obj)/vmlinux:$(src)/vmlinux_$(BITS).lds$(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/piggy.o FORCE
$(call if_changed,ld)
@:
解析:把head_32.o,misc.o, piggy.o链接成vmlinux。head_32.o,misc.o是处理重定位,以及内核解压工作的。
在arch/x86/boot/Makefile中这段代码把vmlinux-àvmlinux.bin
cmd_arch/x86/boot/bzImage := arch/x86/boot/tools/build -b arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin CURRENT > arch/x86/boot/bzImage
解析:
用build把arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin拼接成bzImage
bzImage |
顶层目录下的vmlinux |
Vmlinux.bin |
Setup.bin |
Head.S |
Vmlinux.bin |
Piggy.o |
head_32.o |
,misc.o
|
为了更直观的了解内核的构造过程。我更改了配置文件。可以看一下Make是如何运作的。
CHK include/linux/version.h
CHK include/linux/utsrelease.h
CALL scripts/checksyscalls.sh
CHK include/linux/compile.h
VDSOSYM arch/x86/vdso/vdso32-int80-syms.lds
VDSOSYM arch/x86/vdso/vdso32-sysenter-syms.lds
VDSOSYM arch/x86/vdso/vdso32-syms.lds
LD arch/x86/vdso/built-in.o
GZIP kernel/config_data.gz
IKCFG kernel/config_data.h
CC kernel/configs.o
LD kernel/built-in.o
CC fs/cramfs/inode.o
CC fs/cramfs/uncompress.o
LD fs/cramfs/cramfs.o
LD fs/cramfs/built-in.o
LD fs/built-in.o
LD vmlinux.o
MODPOST vmlinux.o
WARNING: modpost: Found 1 section mismatch(es).
To see full details build your kernel with:
'make CONFIG_DEBUG_SECTION_MISMATCH=y'
GEN .version
CHK include/linux/compile.h
UPD include/linux/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/x86/boot/compressed/vmlinux.bin
GZIP arch/x86/boot/compressed/vmlinux.bin.gz
LD arch/x86/boot/compressed/piggy.o
LD arch/x86/boot/compressed/vmlinux
OFFSETS arch/x86/boot/offsets.h
AS arch/x86/boot/header.o
CC arch/x86/boot/version.o
LD arch/x86/boot/setup.elf
OBJCOPY arch/x86/boot/setup.bin
OBJCOPY arch/x86/boot/vmlinux.bin
BUILD arch/x86/boot/bzImage
2. 启动分析
1:BIOS引导过程分析
CPU加电后,将会初始化程序指针到某个约定好的地址上取指令
运行,在这个地指处,往往安排了启动相关的代码,例
如BIOS或者reset向量处理入口。
Ø 初始化寄存器;CS:IP = 0xfffffff0, in ROM
Ø ROM 就是BIOS
Ø BIOS启动内容
Ø POST(上电自检)
Ø 初始化硬件设备
Ø 搜索一个操作系统来启动
Ø 根据配置,操作系统可以在软盘/硬盘/CD ROM上
Ø 把对应设备的第一个扇区的内容(bootloader或部分)拷贝到RAM(0x7c00)
Ø 跳转到0x7c00处执行
Ø BIOS调用Bootloader把操作系统内核映像装载到RAM中
硬盘的第一个扇区:主引导记录MBR, Master Boot Record
MBR存储该硬盘的分区表+ 一小段引导程序
这个引导程序用来装载OS所在分区的第一个扇区(boot
loader)的内容到RAM中,这个引导程序也可以被替换。
通常LILO或GRUB会显示一个已安装操作系统的列表
按照用户的选择(或者按照缺省项)装载目标操作系统运行
可能装载操作系统指定的启动代码运行(嵌套的情况)可能直接装载操作系统内核来运行。
Ø 显示“Loading…”
Ø header内容被装载到RAM的0x90000
Ø 操作系统内核的其他内容被装载到
Ø 对于小内核:0x10000(即64K处),称为低装载
Ø 对于大内核:0x100000(即1M处),称为高装载
Ø 跳转到0x90200处运行,因为Ox90200前512字节是针对floopy启动的,现在的内核是不支持软盘启动的。所以要跳过去。
3. 上段程序对数据段寄存器CS寄存器进行了设置跳转到main(void),该函数部分定义如下:
4. void main(void)
5. {
6. /* First, copy the boot header into the "zeropage" */
7. copy_boot_params();
8.
9. /* End of heap check */
10. init_heap();
11.
12. /* Make sure we have all the proper CPU support */
13. if (validate_cpu()) {
14. puts("Unable to boot - please use a kernel appropriate "
15. "for your CPU./n");
16. die();
17. }
18.
19. /* Tell the BIOS what CPU mode we intend to run in. */
20. set_bios_mode();
21.
22. /* Detect memory layout */
23. detect_memory();
24.
25. /* Set keyboard repeat rate (why?) */
26. keyboard_set_repeat();
27.
28. /* Query MCA information */
29. query_mca();
30. query_ist();
31.
32. /* Query APM information */
33. /* Set the video mode */
34. set_video();
35.
36. /* Do the last things and invoke protected mode */
37. go_to_protected_mode();
38. }
39. 该函数复制位于第一个扇区的参数,系统硬件参数和其他一些参数,最后调用go_to_protected_mode()从实模式进入保护模式。下面就保护模式startup_32进行分析。
40.
41.
42.
43. 2:startup_32分析
44.
45.
46. .section .text.head,"ax",@progbits
47. ENTRY(startup_32)
48. /* test KEEP_SEGMENTS flag to see if the bootloader is asking
49. us to not reload segments */
50. testb $(1<<6), BP_loadflags(%esi)
51. jnz 2f
52. lgdt pa(boot_gdt_descr)
53. movl $(__BOOT_DS),%eax
54. movl %eax,%ds
55. movl %eax,%es
56. movl %eax,%fs
57. movl %eax,%gs
58. cld
59. xorl %eax,%eax
60. movl $pa(__bss_start),%edi
61. movl $pa(__bss_stop),%ecx
62. subl %edi,%ecx
63. shrl $2,%ecx
64. rep ; stosl
65. /*
66.
67. movl $pa(boot_params),%edi
68. movl $(PARAM_SIZE/4),%ecx
69. cld
70. rep
71. movsl
72. movl pa(boot_params) + NEW_CL_POINTER,%esi
73. andl %esi,%esi
74. jz 1f # No comand line
75. movl $pa(boot_command_line),%edi
76. movl $(COMMAND_LINE_SIZE/4),%ecx
77. rep
78. movsl
79. boot_gdt_descr:
80. .word __BOOT_DS+7
81. .long boot_gdt - __PAGE_OFFSET
82.
83. movb ready, %cl
84. movb $1, ready
85. cmpb $0,%cl # the first CPU calls start_kernel
86. je 1f
87. movl $(__KERNEL_PERCPU), %eax
88. movl %eax,%fs # set this cpu's percpu
89. jmp initialize_secondary # all other CPUs call initialize_secondary
90. 1:
91. #endif /* CONFIG_SMP */
92. jmp i386_start_kernel
93.
94.
95. 以上是startup_32的部分代码,在这段代码中我们进行了除cs外所有段寄存器的重新设置,并且准备好了页面映射所需要的页目录(swapper_page_dir)和页表(pg0),并对页表进行了初始化。页表初始化完成之后又定义了中断描述附表,最后跳转到start_kernel。
96.
97.
98. 3内核启动 start_kernel分析
99.
100.
101. 系统在进入start_kernel之前为他的执行建立起了一个环境,内核的初始化从start_kernel开始,其部分定义如下:
102.
103. linux/init/main.c
104.
105. asmlinkage void __init start_kernel(void)
106. {
107. char * command_line;
108. extern struct kernel_param __start___param[], __stop___param[];
109.
110. smp_setup_processor_id();
111.
112. /*
113. * Need to run as early as possible, to initialize the
114. * lockdep hash:
115. */
116. unwind_init();
117. lockdep_init();
118. debug_objects_early_init();
119. cgroup_init_early();
120.
121. local_irq_disable();
122. early_boot_irqs_off();
123. early_init_irq_lock_class();
124.
125. /*
126. * Interrupts are still disabled. Do necessary setups, then
127. * enable them
128. */
129. lock_kernel();
130. tick_init();
131. boot_cpu_init();
132. page_address_init();
133. printk(KERN_NOTICE);
134. printk(linux_banner);
135. setup_arch(&command_line);
136. mm_init_owner(&init_mm, &init_task);
137. setup_command_line(command_line);
138. unwind_setup();
139. setup_per_cpu_areas();
140. setup_nr_cpu_ids();
141. smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
142.
143.
144. 通过分析这些函数,可以了解到这些函数对内核的各个模块进行了初始化包括内存中断等,并且调用kernel_thread()建立了新的进程继续初始化。当建立init进程并运行init进程时初始化完成。