从零写OS内核-第二篇:Hello World 解析

你以为“Hello World”只是打印一句话? 在操作系统内核里,它背后藏着启动流程、内存布局和硬件协议的秘密。

上一篇,我们成功让自己的内核在 QEMU 上输出了 “Hello from the kernel!”。 看起来很简单,对吧? 但你有没有想过:

为什么代码要放在
1MB 地址

为什么需要一个奇怪的
linker.ld
文件?
BIOS(或 GRUB)到底做了什么?
为什么不能直接
printf

今天,我们就来 拆解这个“Hello World”内核 ,揭开 x86 裸机启动的底层逻辑。


🧩 一、BIOS:PC 启动的第一步

在 x86 PC 世界里,一切始于 BIOS (Basic Input/Output System)。

当按下电源键:

  1. CPU 从
    地址 0xFFFFFFF0
    开始执行(这是复位向量)。
  2. BIOS 代码(固化在主板 ROM 中)被加载运行。
  3. BIOS
    自检硬件
    (内存、磁盘、键盘等)。
  4. 然后,BIOS
    加载引导扇区
    (512 字节)到内存
    0x7C00
    ,并跳转执行。

⚠️ 但现代系统很少直接用 BIOS 引导自定义内核——因为太麻烦! 所以我们通常借助 GRUB (一个更强大的 bootloader),它支持 Multiboot 协议 ,能直接加载 ELF 格式的内核。

✅ GRUB 的作用 :

解析内核 ELF 文件
将代码段、数据段放到指定内存位置
设置好 CPU 状态(已处于保护模式)
跳转到
_start
入口

这就是为什么我们的内核不需要处理“实模式→保护模式”转换—— GRUB 已经帮我们搞定了!


🔗 二、链接脚本(Linker Script):内核的“户型图”

编译后的代码不会自动知道该放哪。 链接器(Linker) 负责把 .text (代码)、 .data (初始化数据)、 .bss (未初始化数据)等段拼接到一起。

而 linker.ld 就是告诉链接器:“请按我说的方式布局内存”。

来看我们用的链接脚本:

🔍 逐行解析:

1️⃣ . = 1M;

表示
加载地址(VMA)从 1MB(0x100000)开始

为什么是 1MB?

  • 早期 PC 的
    实模式
    只能访问 1MB 以下内存(地址线 20 位)。
  • 1MB 以上是“扩展内存”,
    保护模式才能使用
  • GRUB 默认将内核加载到 1MB 以上,避免与 BIOS 数据冲突。

💡 注意:这不是物理限制,而是 历史约定 + GRUB 默认行为 。

2️⃣ .text BLOCK(4K) : ALIGN(4K)

.text

段(代码)按
4KB 对齐

为什么对齐?

  • 便于后续启用
    分页(Paging)
    (x86 页大小为 4KB)
  • 提升内存访问效率
  • 某些硬件要求对齐

3️⃣ (.multiboot)

把所有目标文件中的
.multiboot
段放在这里。
这个段包含
Multiboot 头
(Magic Number 等),GRUB 会扫描它来识别可引导内核。

4️⃣ 其他段( .rodata , .data , .bss )

按顺序排列,符合常规程序内存布局:

  • 代码 → 只读数据 → 已初始化数据 → 未初始化数据

    .bss

    不占 ELF 文件空间,但会在内存中预留(链接器自动清零)


🧱 三、内存布局图(简化版)

✅ 栈为什么单独设在高地址? 避免和代码/数据段冲突。我们的 boot.asm 手动分配了 16KB 栈空间( .bss 中的 stack_top )。


🛠️ 四、为什么不能用
printf

因为:

没有 C 标准库

(libc):
printf
是库函数,依赖操作系统提供的“系统调用”(如
write
)。
没有文件描述符、没有终端抽象

:我们的“输出”是直接写
VGA 显存(0xB8000)
,属于
裸硬件操作

所以,内核开发中:

所有 I/O 都要自己实现(串口、显存、磁盘)
不能使用动态内存(没有
malloc
,除非自己写内存管理器)
不能使用异常、全局构造函数等高级特性

📌 内核是“站在硬件之上的第一层软件”,它 自己就是操作系统的一部分 ,不能依赖操作系统!


🌱 五、下一步:从 Hello World 到真正的内核

现在你已经理解了:

BIOS/GRUB 如何启动内核
链接脚本如何控制内存布局
为什么输出要直接操作显存

接下来,你可以尝试:

在链接脚本中加入
.eh_frame
(避免 GCC 优化报错)
添加
堆(heap)
支持动态内存
实现
串口输出
(比 VGA 更可靠,适合调试)
配置
GDT(全局描述符表)
,正式接管 CPU 状态

最后修改: 2026年7月1日

作者

评论

发表评论

您的邮箱地址不会被公开。