内核的创建流程与代码分析

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.okernel/ built-in.omm /built-in.o, fs/built-in.o, ipc/built-in.o, security/built-in.o built-in.o, security/built-in.ocrypto/built-in.oblock/ 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.alib/ built-in.o

 

drivers-y       := drivers/ sound/

drivers-y       := $(patsubst %/, %/built-in.o, $(drivers-y))

依赖文件包括:lib/lib.alib/ built-in.o

 

 

net-y      := net/

net-y      := $(patsubst %/, %/built-in.o, $(net-y))

 

依赖文件包括:net/ built-in.o

 

 

 

2: compress下的vmlinuxbzImage生成过程分析

 

vmlinux是内核的执行文件,在X86中我们通常使用bzImage,通过分析makefilearch/x86/boot/compressed/Makefile)来分析从vmlinuxbzImage 的生成过程。

 

$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE

       $(call if_changed,objcopy)

解析:通过objcopy来把vmlinux生成vmlinux.binobjcopy把一种目标文件中的内容复制到另一种类型的目标文件中. 

 

$(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.binvmlinux.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链接成vmlinuxhead_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

解析:

buildarch/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.     启动分析

1BIOS引导过程分析

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中,这个引导程序也可以被替换。

通常LILOGRUB会显示一个已安装操作系统的列表

按照用户的选择(或者按照缺省项)装载目标操作系统运行

可能装载操作系统指定的启动代码运行(嵌套的情况)可能直接装载操作系统内核来运行。

Ø  显示“Loading…”

Ø  header内容被装载到RAM0x90000

Ø  操作系统内核的其他内容被装载到

Ø  对于小内核:0x10000(即64K处),称为低装载

Ø  对于大内核:0x100000(即1M处),称为高装载

Ø  跳转到0x90200处运行,因为Ox90200512字节是针对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.  2startup_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进程时初始化完成。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值