https://miniature-metal-8fb.notion.site/1289a024cc68805b8d92e033693ee121?source=copy_link
计算机上电启动:
最早16位 实模式
跳转到bios (flash) 对硬件进行自检 检测主板什么的 0x7c00
前三步是硬件自己完成的
后面的boot_loader和操作系统是我们自己写的
然后根据引导 加载引导程序到内存中运行 准备OS运行环境,然后加载OS到内存
BIOS 会将磁盘的第 0 扇区(也就是 MBR)加载到内存的 0x7C00 地址处,然后将控制权转交给它执行。
怎么判断第0个扇区有引导代码,通过0x55 0xaa
✅ 详细解释:
🔹 1. 启动流程(x86 BIOS 启动):
- 加电启动(Power On)
- BIOS 执行 POST(Power-On Self-Test)
- 搜索可启动设备(硬盘、U盘、光盘…)
- 找到启动设备后:
- 读取该设备的 第 1 个扇区(LBA 0,大小 = 512 字节)
- 把它加载到内存地址
0x0000:0x7C00
(实地址 = 0x7C00)
- 检查最后两个字节是否为
0x55AA
(有效的引导扇区签名) - 如果正确 → 跳转到
0x7C00
执行
🧠 为什么是 0x7C00
?
因为早期 IBM PC BIOS 规范规定了:
- 启动扇区必须装载到实模式内存的
0x0000:0x7C00
- 这是 8086/real-mode 下可安全使用的一块地址(下方保留给 BIOS)
📌 MBR 大小和限制:
项目 | 数值 |
---|---|
MBR 大小 | 512 字节(固定) |
启动代码 | 前 446 字节 |
分区表 | 64 字节(4×16) |
魔数签名 | 0x55AA (末尾2字节) |
✅ 示例:简单 MBR bootloader 程序
nasm
复制编辑
[BITS 16]
[ORG 0x7C00]
start:
mov ah, 0x0E
mov al, 'H'
int 0x10
mov al, 'i'
int 0x10
hang:
jmp hang
times 510 - ($ - $$) db 0
dw 0xAA55 ; BIOS 魔数
这个代码编译为 512 字节的 .bin
文件,BIOS 就能加载并执行。
✅ 总结一句话:
在 BIOS 启动模式中,BIOS 会将硬盘的 第 0 扇区(MBR)加载到 0x7C00,并从那里开始执行。
这就是你写 Bootloader 的入口点 —— 你的第一个操作系统字节就是从这里开始的!
创建可引导的启动编译
什么是引导程序?
1.检测内存容量
2.实模式-》保护模式
3.从xx加载OS,从而调到内核运行
x86上电后为什么会先进入实模式?
为了兼容8086早期OS,只能跑16位,1M内存
BIOS显示字符
BIOS中断读取磁盘
进入C语言环境并跳转到loader
我们设置0x8000的地方有一个函数,给跳转过去 没有返回值
((void (*)(void))LOADER_START_ADDR)();//是一种 C 写法的裸机跳转语句,它让你从 bootloader 跳到 loader 或内核入口去执行,类似于 jmp 指令。
把 LOADER_START_ADDR
当作一个函数地址,强制转换成 void (*)(void)
类型,然后直接调用它 → 这就跳转到了 loader 的入口处。
部分 | 解释 |
---|---|
LOADER_START_ADDR | 通常是一个整数常量(如 0x8000 ),表示 loader 被加载到的内存地址 |
(void (*)(void)) | 将地址强制转换成 “无参、无返回值函数” 的指针 |
((void (*)(void))addr)(); | 调用这个地址上的代码,就像调用一个函数一样 |
是一种 C 写法的裸机跳转语句,它让你从 bootloader 跳到 loader 或内核入口去执行,类似于 jmp
指令。
需要在vscode中的launch.jason设置这个 才不会出错
{
"description": "加载load符号文件",
"text": "add-symbol-file ./build/source/loader/loader.elf 0x8000",
"ignoreFailures": false
}
这条命令的作用是:
**你需要在 launch.json 中手动指定 add-symbol-file
命令,告诉 GDB loader 被加载到了 0x8000,**否则 GDB 无法知道 loader 的符号地址 → 调试时无法识别函数、变量、断点就会出错。
bios boot loader区别
question: boot是放加载loader的代码吗 在磁盘第一个扇区加载到0x7c00 由boot加载到loader,loader的代码就是为了加载os
ChatGPT said:
✅ 是的,你的理解已经非常接近完整正确了!我来为你 系统整理一下:boot → loader → os 的加载过程,帮助你完全搞清楚这三个阶段的角色。
✅ 一句话概括:
boot 是引导扇区代码(位于磁盘第0扇区,MBR),由 BIOS 加载到 0x7C00,
它的主要作用就是:加载并跳转到更大的第二阶段加载器(loader),
而 loader 的主要作用是:加载真正的操作系统内核(os)到内存并跳转执行。
✅ 整体启动流程图:
text
CopyEdit
[BIOS] (硬件开机)
↓ 读取硬盘第0扇区 → 加载到内存地址 0x7C00
[boot] (512字节以内的 MBR/bootloader阶段1)
↓ 加载并跳转到更复杂的 loader(通常是 2阶)
[loader] (多段程序,支持文件系统、内核加载等)
↓ 加载操作系统内核(kernel)
[OS内核] (操作系统正式启动)
✅ 各阶段详解
🔹 1. BIOS → boot(引导扇区)
- BIOS 会自动将第 0 扇区(MBR)加载到内存地址
0x7C00
- boot 是你写的“第一阶段 bootloader”
- 限制:
- 512 字节大小限制
- 最后两个字节必须是
0x55AA
才能被当作合法引导扇区
功能:尽快加载 loader(更大的程序)
🔹 2. boot → loader(第二阶段引导器)
boot 阶段非常小,所以它只做一件事:加载 loader
loader 通常位于:
- 某个指定的扇区(比如 1 号扇区开始)
- 某个简易的文件系统中(FAT12、ext2)
loader 的能力更强,比如:
- 实现磁盘读取
- 初始化分页、保护模式
- 加载文件系统中的内核文件(如
/boot/kernel.bin
)
功能:加载并跳转到操作系统内核的入口地址
🔹 3. loader → OS(内核)
- loader 将内核文件从磁盘加载到指定内存位置(如 0x100000)
- 构造内核启动参数(如 GRUB 的 multiboot header)
- 跳转到内核的入口地址(如
_start:
)
内核开始运行:设置页表 → 启动中断 → 启动第一个进程 → 欢迎来到操作系统世界!
✅ 示例流程图(你可以这样设计):
text
CopyEdit
磁盘布局:
[0] bootloader(阶段1)→ 加载 1~N 扇区的 loader
[1~10] loader → 读取 kernel 并跳转
[11~] kernel.bin 操作系统内核
✅ 举个最小例子:
你可以写一个 boot.asm(bootloader):
nasm
CopyEdit
[BITS 16]
[ORG 0x7C00]
start:
; 简单提示
mov ah, 0x0E
mov al, 'B'
int 0x10
; 加载loader扇区(假设在 LBA 1)
mov ah, 0x02 ; BIOS读
mov al, 1 ; 读1个扇区
mov ch, 0
mov cl, 2 ; LBA=1 → CHS=0,2
mov dh, 0
mov dl, 0x80 ; 硬盘
mov bx, 0x8000 ; 目标地址
int 0x13
; 跳转到 loader
jmp 0x0000:0x8000
times 510 - ($ - $$) db 0
dw 0xAA55
✅ 总结一句话:
boot 是启动的最小入口(512字节),只负责加载 loader;loader 是功能完整的加载器,加载真正的内核(OS);os 是你写的操作系统本体。
x86 中主要的内核寄存器有哪些?它们都有什么作用?
这是你写操作系统、写中断处理器、做上下文切换时必须掌握的底层知识。
✅ 一、常用通用寄存器(General Purpose Registers)
寄存器 | 位数 | 作用 |
---|---|---|
EAX | 32 | 累加器(返回值、临时运算) |
EBX | 32 | 基址寄存器(某些寻址使用) |
ECX | 32 | 计数器(循环、位移) |
EDX | 32 | 数据寄存器(I/O、高位乘除) |
ESI | 32 | 源地址指针(字符串操作) |
EDI | 32 | 目的地址指针 |
EBP | 32 | 栈基址指针(函数帧) |
ESP | 32 | 栈顶指针(栈操作) |
EIP | 32 | 指令指针(下一条执行的地址) |
EFLAGS | 32 | 标志寄存器(中断标志、条件码) |
✅ 二、段寄存器(Segment Registers)
寄存器 | 作用 |
---|---|
CS | 代码段(控制当前代码位置) |
DS | 数据段(默认数据访问) |
SS | 栈段(ESP 的段选择子) |
ES | 附加段(字符串操作) |
FS | 用户/线程专用(如 TEB) |
GS | 用户/内核专用(如 GDT base) |
🧠 段寄存器存放的是“段选择子”(Selector),用于访问 GDT/LDT 的段描述符。
✅ 三、控制寄存器(Control Registers)
这些是内核最重要的系统控制寄存器:
寄存器 | 作用 |
---|---|
CR0 | 控制 CPU 模式:启用分页、保护模式、缓存等 |
CR2 | 页错误地址(Page Fault 时自动填入) |
CR3 | 页目录基址寄存器(指向页表) |
CR4 | 扩展控制位(启用 PAE、SSE、SMEP 等功能) |
📌 重点:CR0
位字段(示例)
位 | 名称 | 含义 |
---|---|---|
0 | PE(保护模式) | 1=启用保护模式 |
31 | PG(分页) | 1=启用分页 |
2 | TS(任务切换) | 控制浮点/任务切换等 |
✅ 四、系统寄存器(与段机制有关)
寄存器 | 作用 |
---|---|
GDTR | 全局描述符表寄存器,存 GDT 的基址和长度 |
LDTR | 局部描述符表寄存器,存 LDT 的位置 |
IDTR | 中断描述符表寄存器,存 IDT 的位置 |
TR | 任务寄存器,存当前活动的 TSS 描述符 |
这些通常通过汇编指令设置,比如:
nasm
复制编辑
lgdt [gdtr]
lidt [idtr]
ltr ax ; 加载 TSS
✅ 五、调试/其他寄存器(可选了解)
寄存器 | 作用 |
---|---|
DR0 –DR7 | 硬件断点(调试用途) |
MSRs | 模型特定寄存器,控制 APIC、SMP、性能等(需要 rdmsr/wrmsr ) |
✅ 总结图(用途分类)
分类 | 寄存器 |
---|---|
通用 | EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP |
控制 | CR0, CR2, CR3, CR4 |
段机制 | CS, DS, SS, ES, FS, GS |
描述符表 | GDTR, IDTR, LDTR, TR |
执行流程 | EIP, EFLAGS |
✅ 最重要的几个你要熟练掌握:
CR0
:进入保护模式、分页CR3
:页表基址EFLAGS
:中断标志、条件标志ESP/EBP
:栈管理EIP
:指令控制GDTR/IDTR
:GDT/IDT 设置TR
:TSS 启用
总结
文章的最后 做个小总结
这一章主要讲解了计算机的启动流程,首先上电后先进入磁盘的第一个扇区,首先加载0x7c00,在检测结尾是否是魔数0x55 0xaa,检测是符合MBR规范后,,跳转并加载loader程序,最后由Loader从磁盘加载完整的内核到内存。
这一章主要讲解了计算机的启动流程。在 x86 架构中,当计算机上电后,CPU 先进入实模式,由 BIOS
执行硬件自检(POST),然后从磁盘加载第 0 扇区(即 MBR)到内存的 0x7C00 地址。加载完成后,BIOS
会检查该扇区末尾是否是合法的引导签名 0x55AA,以此判断它是否是有效的引导扇区。如果校验通过,CPU 就从 0x7C00 开始执行
bootloader 程序。 bootloader 的任务是将后续更复杂的 loader 程序加载到内存中(通常放在如 0x8000
的位置),并跳转执行。而 loader
的作用是设置好必要的操作系统运行环境,如打开保护模式、初始化分页表,最后从磁盘加载完整的操作系统内核到高地址(如
0x100000),并跳转进入内核执行。
一句话总结:计算机上电后,BIOS 会将磁盘第 0 扇区加载到 0x7C00,验证 0x55AA 魔数后跳转执行 bootloader,bootloader 加载 loader,最终由 loader 加载内核并跳转运行,完成系统启动流程。