从0写自己的操作系统(1) boot->loader引导程序

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 启动):

  1. 加电启动(Power On)
  2. BIOS 执行 POST(Power-On Self-Test)
  3. 搜索可启动设备(硬盘、U盘、光盘…)
  4. 找到启动设备后:
    • 读取该设备的 第 1 个扇区(LBA 0,大小 = 512 字节)
    • 把它加载到内存地址 0x0000:0x7C00(实地址 = 0x7C00
  5. 检查最后两个字节是否为 0x55AA(有效的引导扇区签名)
  6. 如果正确 → 跳转到 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)

寄存器位数作用
EAX32累加器(返回值、临时运算)
EBX32基址寄存器(某些寻址使用)
ECX32计数器(循环、位移)
EDX32数据寄存器(I/O、高位乘除)
ESI32源地址指针(字符串操作)
EDI32目的地址指针
EBP32栈基址指针(函数帧)
ESP32栈顶指针(栈操作)
EIP32指令指针(下一条执行的地址)
EFLAGS32标志寄存器(中断标志、条件码)

✅ 二、段寄存器(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 位字段(示例)

名称含义
0PE(保护模式)1=启用保护模式
31PG(分页)1=启用分页
2TS(任务切换)控制浮点/任务切换等

✅ 四、系统寄存器(与段机制有关)

寄存器作用
GDTR全局描述符表寄存器,存 GDT 的基址和长度
LDTR局部描述符表寄存器,存 LDT 的位置
IDTR中断描述符表寄存器,存 IDT 的位置
TR任务寄存器,存当前活动的 TSS 描述符

这些通常通过汇编指令设置,比如:

nasm
复制编辑
lgdt [gdtr]
lidt [idtr]
ltr ax      ; 加载 TSS


✅ 五、调试/其他寄存器(可选了解)

寄存器作用
DR0DR7硬件断点(调试用途)
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 加载内核并跳转运行,完成系统启动流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值