Linux内核引导过程解析:从电源启动到内核初始化
linux-insides-zh Linux 内核揭秘 项目地址: https://gitcode.com/gh_mirrors/li/linux-insides-zh
计算机启动的底层原理
当按下电源按钮时,计算机开始了一段精密的启动过程。主板首先向电源发送信号,电源准备好后会向主板发送"电源正常"信号。收到这个信号后,主板开始初始化CPU,CPU将所有寄存器重置为预定值。
在x86架构中,CPU复位后的寄存器初始状态为:
- IP(指令指针):0xfff0
- CS(代码段选择器):0xf000
- CS基址:0xffff0000
此时CPU运行在实模式(Real Mode)下,这是x86处理器为了兼容早期8086处理器而保留的工作模式。
实模式内存寻址机制
实模式采用段式内存管理,物理地址计算公式为:
物理地址 = 段寄存器值 × 16 + 偏移量
例如CS:IP为0x2000:0x0010时:
0x2000 × 16 + 0x0010 = 0x20010
这种模式下CPU只能访问1MB内存空间,通过A20地址线控制可以访问更多内存。
BIOS启动过程
CPU执行的第一条指令位于0xFFFFFFF0地址,这个地址被映射到主板ROM中。ROM中的代码是BIOS的入口点,BIOS会:
- 进行硬件自检(POST)
- 初始化硬件设备
- 按照配置顺序查找可启动设备
- 加载并执行启动设备的引导扇区代码
引导扇区与MBR结构
在硬盘设备上,BIOS会查找主引导记录(MBR),它位于第一个扇区(512字节)中:
- 前446字节:引导代码
- 随后64字节:分区表
- 最后2字节:魔数0x55AA(标识有效引导扇区)
一个简单的引导扇区示例(汇编语言):
[BITS 16]
[ORG 0x7c00]
boot:
mov al, '!'
mov ah, 0x0e
int 0x10
jmp $
times 510-($-$$) db 0
db 0x55
db 0xaa
这段代码会在屏幕上显示一个感叹号,然后进入无限循环。
GRUB2引导加载程序
现代Linux系统通常使用GRUB2作为引导加载程序,它的工作流程是:
- BIOS加载并执行GRUB2的第一阶段代码(boot.img)
- boot.img加载并执行core image(diskboot.img)
- core image加载GRUB2内核和文件系统驱动
- GRUB2读取配置文件并显示启动菜单
- 用户选择后,GRUB2加载并启动选中的操作系统
内核启动协议
引导加载程序需要按照Linux内核启动协议准备内核运行环境,包括:
- 设置正确的段寄存器值
- 初始化堆栈
- 清零BSS段
- 填充内核头部的必要字段
内核头部定义(header.S)包含关键信息:
.globl hdr
hdr:
setup_sects: .byte 0
root_flags: .word ROOT_RDONLY
syssize: .long 0
vid_mode: .word SVGA_MODE
root_dev: .word 0
boot_flag: .word 0xAA55
内核设置代码执行
内核设置代码从_start标签开始,主要完成以下工作:
- 统一所有段寄存器值
- 设置堆栈指针
- 检查魔数签名(0x5A5AAA55)
- 清零BSS段
- 跳转到main()函数(C代码入口)
堆栈设置需要考虑三种情况:
- SS寄存器已经是期望值(0x10000)
- SS不是期望值但可以使用堆栈(CAN_USE_HEAP标志置位)
- SS不是期望值且不能使用堆栈
进入C语言环境
完成所有准备工作后,通过call指令跳转到main()函数,这是内核中第一个执行的C代码,位于arch/x86/boot/main.c。
main()函数将进行更复杂的初始化工作,包括:
- 内存检测
- 设置视频模式
- 初始化控制台
- 检测CPU特性
- 解压内核映像等
至此,计算机完成了从按下电源键到开始执行内核C代码的完整引导过程。这个过程涉及硬件初始化、固件操作、引导加载程序和内核设置代码的精密配合,为操作系统正常运行奠定了基础。
linux-insides-zh Linux 内核揭秘 项目地址: https://gitcode.com/gh_mirrors/li/linux-insides-zh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考