从零写OS(四):汇编交棒给 C,最关键的一跳

前三章全是汇编。到这里,CPU 已经跑在 64 位长模式下了,但还是什么都做不了。 这一章要完成最关键的一步:把控制权从汇编交给 C。之后的内核全部用 C 写,汇编只在最必要的地方出现。 磁盘要怎么布局 512 字节的 Bootloader 放不下 C 内核,所以内核单独编译成一个二进制文件,放在磁盘的后续扇区(扇区,Sector,磁盘读写的最小单位,传统上每个 512 字节): 扇区 0(512B):Bootloader 扇区 1~8 :内核二进制 Bootloader 启动后,用 BIOS 的磁盘读取中断把扇区 1 开始的内容读到内存地址 0x10000,然后跳过去执行。 一个容易踩的坑:内核入口不在 0x10000 把内核编译好、加载到 0x10000,然后 call 0x10000——看起来没问题,实际上很可能直接崩。 用 objdump(反汇编工具,可以查看二进制文件的结构和函数地址)一查才发现:kernel_main 的实际地址是 0x10109,不是 0x10000。0x10000 开头是另一个函数 outb,跳过去执行完全是错的。 解决方案:加一个 entry.asm 作为内核最开头,内容只有一行:跳转到 kernel_main。然后在链接脚本(Linker Script,告诉链接器如何拼装各个目标文件、每段放在哪个地址)里把 entry.o 排在最前面,保证 _start 恰好落在 0x10000。 这样 Bootloader 跳到 0x10000,第一条指令就是 jmp kernel_main,稳了。 还有一个坑:栈 进入 64 位模式之后,rsp(栈顶指针寄存器)是未初始化的。C 函数一用栈就崩。 在跳入内核之前,要先设好栈: mov rsp, 0x90000 选 0x90000 是因为这块内存没有被占用,往下增长也不会覆盖内核代码。 内核编译参数 内核不能用普通方式编译,要加几个关键选项: ...

May 6, 2026 · 1 min · 大飞
京ICP备14031575号-3