Linux2.6.16的启动过程

阅读报告——Linux2.6.16的启动过程
                                                              
从按下启动按钮,到登陆界面的出现,这期间就是 Linux 的启动过程。显然这期间有两个重要的任务要完成:初始化各种硬件、载入 kernel 映像并有 kernel 接管系统。研究 Linux 的启动过程对嵌入式开发有着不言而喻的重要性——因为这期间是和硬件关系最紧密的时候,是移植 kernel 到不同硬件平台的第一个关键环节。参考 Kernel 2.6.16 源代码和《 Understanding the Linux Kernel 3th Edition 》,总结出如下步骤:
1.      BIOS启动部分
Reset 引脚一旦有信号, cpu 就开始执行一段在 rom 中的只读代码,完成一些基本的硬件检测,所以被称为 BIOS(Basic Input/Output System) 。这些都是以中断形式提供的服务,只能运行在实模式。 BIOS 把检测硬件的结构打印到屏幕的同时存在一些 table 里面,最后根据配置从启动介质里面读取第一个扇区的内容到 RAM 0x00007c00处,并跳转到那里执行。通常情况,就是我们的磁盘的第一个扇区,也即传统上的MBR,存放的代码是一个BootLoader。
2.BootLoader
顾名思义, BootLoader 就是负责引导操作系统的。对 IBM PC 来说, LILO Grub 是最流行的两个 bootloader ,并且后者更加高级,因为它能够识别多种文件系统。通常说来,一个 sector 是放不下一个 bootloader 的,故而分成两部分:以 LILO 为例,放在 MBR 的一部分把自己拷贝到 0x00096a00,并且在0x00098000 0x000969ff之间建立一个实模式下的堆栈,把第二部分拷贝到0x00096c00处,并跳转到那执行。第二部分代码允许用户选择一个操作系统来启动,或者直接启动默认的操作系统。如果是Linux操作系统,LILO使用BIOS提供的服务,把kernel Image的前面几个扇区(bootsect.S和Setup.S)的内容拷贝到0x00090000,然后把Image的其余部分(被压缩)拷贝到0x00010000或者0x00100000,最后一步跳转到0x00090200处执行,也就是Setup.S的第一条指令。可以看到使用BootLoader之后,kernelbootsec.S提供的功能被BootLoader替代了。
3.setup.S(arch\i386\boot\)
这个文件里面的汇编代码的主要作用是重新初始化一些硬件设备,并且为 kernel 的运行准备必要的环境。
a)     BIOS 里面取出物理内存的描述,并保存在一块安全的内存区域里。
b)     设定键盘的速度。
c)     初始化显卡。
d)     初始化硬盘。
e)     检查 PS/2  总线。
f)     检查高级电源管理 (APM)
g)     初始化 GDT IDT
lidt idt_48   # load idt with 0,0
lgdt  gdt_48  # load gdt
h)         切换到保护模式
movw $1, %ax # protected mode (PE) bit
lmsw %ax      # This is it!
i)           跳转到 startup_32
.byte 0×66, 0xea # prefix + jmpi-opcode
code32:
.long 0×1000 # will be set to 0×100000
# for big kernels
.word __BOOT_CS
 
这段代码表明上是数据,实际上构造了一条跳转指令 jmpi  0×100000,__BOOT_CS
4.startup_32:(arch/i386/boot/compressed/head.S)
正如名字所示,这些汇编代码是在 32 位的保护模式下执行。首先它用初始化段寄存器 DS,ES,FS,GS:
movl $(__BOOT_DS),%eax
       movl %eax,%ds
       movl %eax,%es
       movl %eax,%fs
       movl %eax,%gs
       lss stack_start,%esp
紧接着把 BSS 的内容用 0 填充:
xorl %eax,%eax        # 置eax为0
    movl $_edata,%edi     #_edata 为起始地址
    movl $_end,%ecx       #_end 为结束地址
    subl %edi,%ecx        # 循环次数
    cld
     rep                   # 置BSS区域为0
    调用 decompress_kernel 函数 (arch/i386/boot/compressed/misc.c) 来解压内核,最好跳转到解压之后的内核所在的 0x00100000处执行。这个地方的代码以arch/i386/kernel/head.S开始。
5.Head.S(arch/i386/kernel/head.S)
这个文件也是以 startup_32 标号开始第一条指令 。完成一下内容:
a)     初始化 gtdr 、段寄存器和内核里面的 BSS
b)         建立一个内核页表,并把 PGD 的地址存入 cr3 ,然后使能 paging 单元。
movl $swapper_pg_dir-__PAGE_OFFSET,%eax   #PGT 地址
movl %eax,%cr3                            # 存入 cr3
/* set the page table pointer.. */
movl %cr0,%eax
orl $0×80000000,%eax                       # cr0 PG
movl %eax,%cr0                           /* ..and set paging (PG) bit */
c)         初始化 esp lss stack_start,%esp
d)         调用 setup_idt()函数。这个函数也在这个文件里面,每一个interrupt只是打印一句话就返回。
e)          调用 init/main.c start_kernel 函数。
6.start_kernel(init/main.c):
到现在为止, kernel 进入 c 语言的函数, start_kernel 需要完成内核的所有初始化工作,包括:
a)         setup_arch()(arch\i386\kernel\setup.c)->paging_init()(arch\i386\mm\init.c)
->pagetable_init().
b)         初始化 schedule: sched_init();
c)         初始化 memory zones: build_all_zonelists();
d)         初始化伙伴系统: page_alloc_init();
e)          处理命内核参数。
f)          初始化中断,异常,软中断: trap_init() init_IRQ(),softirq_init();
g)         初始化系统时间和日期: time_init();
h)         初始化 slab 算法: kmem_cache_init();
i)           初始化 pid hash 表: pidmap_init();
j)           初始化信号系统: signals_init();
k)         最后调用 rest_init() 函数。
7.rest_init(init/main.c)
a)          首先创建 1 号进程 init
kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
b)         然后调用 schedule() 。这样 init 函数就得到执行,根据配置,在 init 函数里面最终会执行文件系统的某一个可执行文件。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值