Linux源码笔记(2.6.26版本) --- 内核启动过程

174 阅读3分钟
  • 直接跳过从加电、boot loader、硬盘MBR这些过程
  • 在使用启动装载器(LILO、GRUB等)将内核载入物理内存之后,通过跳转语句跳转到内存中适当的位置,来调用 arch/x86/boot/header.S

具体过程如下图所示

image.png


文字版描述

setup()汇编函数 ---> (arch/x86/boot/header.S)
  1. 磁盘控制器重置
  2. 堆栈设置
  3. 段配置
  4. 签名检查、BSS清零 image.png
main()C函数 ---> (arch/x86/boot/main.c)
  1. 将启动头复制到zeropage中,启动头在前面header.S的hdr标号处 调用copy_boot_params函数
    image.png 此处的hdr在(arch/x86/boot/header.S中)
    image.png
  2. 初始化堆栈
  3. 告诉BIOS在什么CPU模式运行
  4. 检测内存布局
  5. 设置键盘重复延时和速率 (当用户一直按下一个键盘超过一定时间,键盘设备就反复的向CPU发送相应键盘码)
  6. 查询MCA(微通道总线)信息
  7. 查询IST(intel speedStep)信息
  8. 设置视频模式
  9. go_to_protected_mode() 做最后的事情并调用保护模式
go_to_protected_mode()C函数 ---> (arch/x86/boot/pm.c)
  1. 离开实模式之前禁用中断
  2. 将内核复制到最终的安放位置
  3. 置位8042键盘控制器的A20引脚.是在80286系统引入, 为的是与古老的8088微处理器物理地址兼容. 在切换到保护模式之前必须将A20置位, 否则每个物理地址第21位会被CPU看作0
  4. 重置协处理器
  5. 屏蔽PIC(可编程中断控制器)中所有中断
  6. 实模式转换到保护模式
startup_32()汇编函数 ---> (arch/x86/boot/compressed/head_32.S)
  1. 初始化段寄存器和临时堆栈
  2. 用0填充_edata和_end符号标识的内核未初始化数据区
  3. 调用decompress_kernel()解压内核 并显示done.\nBooting the kernel.\n解压成功 如果内核映像是低装载的,解压后内核放在物理地址0x100000处, 如果是高装载的,则放在压缩映像之后的临时缓冲区中.
  4. 解压的内核映像包含另一个startup_32()函数,使用相同的名字不会产生问题,因为这两个函数会跳到自己的起始物理地址去执行. 跳转到0x100000物理地址另一个startup_32函数
startup_32()汇编函数 ---> (arch/x86/kernel/head_32.S)
  1. 将段寄存器初始化为最终值
  2. 将内核bss段填充为0
  3. 初始化包含在swapper_pg_dir的临时页表,并初始化pg0,以使线性地址一致映射同一物理地址
  4. 将页全局目录的地址存放在cr3寄存器中,并通过设置cr0寄存器的PG位启用分页
  5. 为进程0建立内核态堆栈
  6. 调用setup_idt()用空的中断处理程序填充IDT
  7. 从BIOS中获得的系统参数传递和传递给操作系统的参数放入第一个页框中
  8. 识别处理器型号
  9. 用GDT和IDT表的地址填充gdtr和idtr寄存器
  10. 跳转到i386_start_kernel
i386_kernel()C函数 ---> (arch/x86/kernel/head32.c)

start_kernel()

start_kernel() C函数 ---> (init/main.c)
  1. sched_init() 初始化调度程序
  2. build_all_zonelists() 初始化内存管理区
  3. page_alloc_init() 初始化伙伴系统分配程序
  4. trap_init() 和init_IRQ()完成IDT初始化
  5. time_init() 初始化系统日期和时间
  6. rest_init() 其中启动了kernel_init和kthread内核线程