引导加载程序
1.1 什么是引导加载程序?
加载引导程序是计算机或设备启动时的关键步骤,它负责从硬件初始化到操作系统内核加载的全过程。简单来说,就是让设备从“通电”状态进入“可运行操作系统”的状态。
常见的引导加载程序有很多,例如:
- GRUB2
- Syslinux
- U-Boot
- systemd-boot
为什么 muxOS 选择 GRUB2?
在操作系统的开发中,一个常见的讨论是:
是否需要从零编写引导加载程序?
这其实是风格问题,可以重头开始,直接编写引导扇区,再写一个最小内核,然后再构建。也可以跳过,直接采取现成的引导加载程序。(关于自己开发程序是否有价值还是浪费时间,这点值得讨论)。
但是在实践中,这一阶段成本非常高,且与内核核心机制关联较弱。
muxOS的目标是:
优先实现一个具备基本进程管理和系统调用能力的内核
所以在技术路线的选择上做出了取舍。
GRUB 工作流程
读 grub.cfg → 找到 kernel.elf → 验证 multiboot magic → 加载到 1MB → 跳到 _start → 调用 kernel_main(magic, mbi)。
关于 GRUB 详细的介绍文档,请参考 GRUB - OSDev 维基 多重启动规范版本 0.6.96
VGA
muxOS 封装了一些基础函数以用于正确显示 VGA 画面。
1 | // vga.h |
关于介绍 VGA 的详细文档,请参考 文本模式光标 - OSDev 维基 VGA 硬件 - OSDev 维基
GDT
2.1 什么是 GDT?
一张描述“内存段”的表,CPU 用它来解释地址和权限
在 IA-32 和 x86-64 架构上,更准确地说,在**保护模式或长模式下,中断服务例程和大量内存管理通过描述符表来控制。每个描述符存储 CPU 可能在某个时刻需要的单个对象(例如服务例程、任务、代码或数据块)的信息。例如,如果你尝试向段寄存**器加载新值,CPU 需要执行安全和访问控制检查,以确认你是否有权访问该特定内存区域。检查完成后,有用的值(如最低和最高地址)会缓存在不可见的CPU寄存器中。
介绍
muxOS 采取 GDT 平坦模式将内存划分为受保护区域,以及分页来保护内存。
| Index | 描述 | Base | Limit | Access | Flags | 特权级 | 说明 |
|---|---|---|---|---|---|---|---|
| 0 | Null Descriptor | 0x00000000 | 0x00000000 | 0x00 | 0x00 | - | 必须为空(CPU 保留) |
| 1 | Kernel Code | 0x00000000 | 0xFFFFFFFF | 0x9A | 0xCF | Ring 0 | 内核代码段(可执行、可读) |
| 2 | Kernel Data | 0x00000000 | 0xFFFFFFFF | 0x92 | 0xCF | Ring 0 | 内核数据段(可读写) |
| 3 | User Code | 0x00000000 | 0xFFFFFFFF | 0xFA | 0xCF | Ring 3 | 用户代码段(可执行、可读) |
| 4 | User Data | 0x00000000 | 0xFFFFFFFF | 0xF2 | 0xCF | Ring 3 | 用户数据段(可读写) |
| 5 | TSS (占位) | 0x00000000 | 0x00000000 | 0x00 | 0x00 | - | 任务状态段(尚未初始化) |
关于 GDT 的详细介绍,请参考 GDT 教程 - OSDev 维基
IDT
3.1 什么是 IDT?
一张“中断/异常 → 处理函数”的映射表
中断描述符表(Interrupt Descriptor Table,IDT) 是保护模式下的重要数据结构,用于将中断或异常向量与其对应的处理程序关联起来。IDT 是一个包含 256 个描述符的数组,每个描述符占用 8 字节,存储了中断处理程序的地址及相关属性。
3.2 介绍
muxOS 在目前的开发版本 (截至 2026/4/26) 中,实现了以下处理:
| 向量号 | 类型 | 名称 | 处理函数 | 说明 |
|---|---|---|---|---|
| 0x00 | Exception | Divide Error | isr0 | 除零异常(CPU自动触发) |
| 0x06 | Exception | Invalid Opcode | isr6 | 无效指令异常 |
| 0x0D | Exception | General Protection | isr13 | 通用保护错误(权限/段错误) |
| 0x0E | Exception | Page Fault | isr14 | 页错误(分页机制核心异常) |
| 0x21 | Hardware IRQ | Keyboard Interrupt | keyboard_handler | 键盘中断(IRQ1) |
| 0x20 | Hardware IRQ | Timer Interrupt | irq0_stub | 时钟中断(IRQ0,调度核心) |
| 0x80 | Software Int | System Call | syscall_stub | 用户态系统调用入口(int 0x80) |
关于 IDT 详细介绍, 请参考中断描述符表 - OSDev 维基
PIC
4.1 什么是PIC ?
它负责把“外设中断”转发给 CPU,并决定优先级
PIC(Programmable Interrupt Controller)是早期 x86 架构中用于管理外设中断的硬件模块,其中最经典的实现是Intel 8259
4.2 介绍
muxOS 启动时首先对 PIC 进行重新映射(remap),避免与 CPU 异常向量冲突:
- IRQ0–IRQ7 → 0x20–0x27
- IRQ8–IRQ15 → 0x28–0x2F
以及处理了键盘中断的情况,处理流程如下
- 1.键盘按下(硬件触发)
- 2.IRQ1(外设中断请求)
- 3.Intel 8259 PIC 进行中断转发
- 4.映射为 CPU 中断向量 INT 0x21
- 5.CPU 根据 IDT 查询 IDT[0x21]
- 6.跳转执行 keyboard_handler()
- 7.读取键盘扫描码并处理输入
- 8.调用 pic_eoi(1) 通知 PIC 该中断已完成
关于 PIC 8259 的介绍,请参考 PIC 8259 - OSDev 维基