从零开始写OS:手把手实现引导加载程序
你还在为操作系统启动流程感到困惑吗?BIOS如何找到引导扇区?GRUB如何加载内核?本文将带你从零开始,通过实战案例详解引导加载程序的工作原理,最终让你能独立编写一个简单的OS引导器。
读完本文你将学到:
- BIOS/UEFI启动流程的核心步骤
- Multiboot规范的实战应用
- 汇编与C++的协作方式
- 使用QEMU测试引导程序的技巧
引导加载程序的核心作用
引导加载程序(Bootloader)是计算机开机后运行的第一个软件,它负责将操作系统内核加载到内存并移交控制权。在How-to-Make-a-Computer-Operating-System项目中,引导流程通过汇编代码start.asm实现,该文件位于x86架构目录下,是整个OS的起点。
BIOS到内核的启动流程
计算机启动过程可分为四个阶段,以下是简化版流程图:
当你按下电源按钮后,BIOS会首先进行硬件自检,然后从硬盘的第一个扇区(MBR)读取引导程序。在本项目中,我们采用GRUB引导器配合自定义汇编代码的方案,相关配置文件位于sdk/bootdisk/boot/grub/grub.conf。
Multiboot规范实战
项目采用GRUB支持的Multiboot规范,在start.asm中定义了必要的头部信息:
%define MULTIBOOT_HEADER_MAGIC 0x1BADB002
%define MULTIBOOT_HEADER_FLAGS 0x00000003
%define CHECKSUM -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
align 4
multiboot_header:
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd CHECKSUM
这段代码定义了GRUB识别的魔术数和校验和,确保引导器能正确识别并加载我们的内核。完整实现可查看start.asm的16-19行。
关键汇编代码解析
引导程序的核心逻辑在_start标签处开始:
_start:
jmp start
start:
push ebx ; 将Multiboot信息结构指针压栈
; 调用全局构造函数
static_ctors_loop:
mov ebx, start_ctors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_ctors
jb .body
call kmain ; 跳转到C++内核入口
; 调用全局析构函数
static_dtors_loop:
mov ebx, start_dtors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_dtors
jb .body
cli ; 关闭中断
hlt ; 停机
这段代码完成三项关键任务:传递Multiboot信息、初始化全局构造函数、跳转到C++内核入口kmain。当内核执行完毕后,还会调用全局析构函数并安全停机。
编译与测试流程
要编译并测试引导程序,只需执行项目根目录下的构建脚本:
# 克隆项目仓库
git clone https://link.gitcode.com/i/451ca083d7682a403aa553b483cfa8af
# 进入项目目录
cd How-to-Make-a-Computer-Operating-System
# 编译内核
cd src && make
# 使用QEMU运行
../sdk/qemu.sh
其中sdk/qemu.sh脚本负责创建虚拟环境并加载生成的内核镜像,这是测试引导程序的便捷方式。
常见问题解决
- 引导失败:检查linker.ld中的内存布局是否正确
- 内核无法加载:确认Multiboot头部定义是否符合规范
- QEMU启动报错:检查sdk/diskimage.sh生成的磁盘镜像是否正确
扩展学习资源
- 完整引导流程:Chapter-1/README.md
- x86架构细节:src/kernel/arch/x86/x86.h
- 内核初始化代码:src/kernel/core/kernel.cc
通过本文的步骤,你已经了解了引导加载程序的核心原理和实现方式。下一步可以深入研究内存管理或进程调度,这些内容在后续章节中有详细讲解。记得收藏本项目,关注更新,让我们一起从零构建完整的操作系统!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



