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