从零手写操作系统:让电脑按你的代码启动(附最小内核)

每个人都用过操作系统,但极少人亲手写过。当你的电脑从“运行别人的系统”变成“运行你写的系统”,那种掌控感无可替代。这篇文章带你迈出第一步:写一个能启动、能打印、能接收按键的最小内核。

一、背景:为什么值得手写操作系统

操作系统不是黑魔法,它就是一段“管理硬件、调度程序的代码”。手写操作系统的意义:

  1. 理解计算机本质:中断、内存、进程——所有概念从“抽象名词”变成“你写的代码”
  2. 面试杀手锏:懂 OS 源码的工程师和会用 OS 的工程师,是两种竞争力
  3. 掌控感:按下电源到你的 shell 出现,每一步都是你写的

二、核心原理:操作系统启动的三步(以传统 BIOS 为主线)

  • BIOS 引导:硬件自检,加载启动扇区(512 字节)到内存 0x7C00
  • Bootloader:从实模式切换到保护模式,把内核从磁盘加载到内存
  • 内核入口:初始化中断、内存、时钟,开始调度

最小内核的骨架

// 内核入口:打印一行字 + 进入死循环(最小可运行内核)
void kernel_main(void) {
    // 直接写显存(0xB8000 是 VGA 文本缓冲区)
    // 注意:在未开启分页的保护模式平坦模型中,线性地址直接映射物理地址
    char* video = (char*)0xB8000;
    const char* msg = "Hello, My OS!";
    for (int i = 0; msg[i]; i++) {
        video[i*2] = msg[i];      // 字符
        video[i*2+1] = 0x07;      // 白字黑底
    }
    while (1);  // 内核接管,永不返回
}

这段代码直接往显卡的显存地址写字符——没有库、没有框架,就靠内存地址操作,你的文字就出现在屏幕上了。

三、代码实战:Python 模拟内核启动流程(细化版)

class MinimalOS:
    def __init__(self):
        self.memory = [0]*1024         # 模拟物理内存
        self.registers = {'eip': 0}    # CPU 寄存器
        self.idt = [None]*256          # 中断描述符表(模拟)
        self.pagetable = None          # 页表(后续实现)
        self.running = False
    
    def boot(self):
        # 阶段1:BIOS 加载启动扇区到内存 0x7C00
        print("[BIOS] 加载启动扇区 → 0x7C00")
        # 阶段2:跳转内核入口
        print("[Boot] 跳转内核入口 kernel_main")
        # 阶段3:内核初始化
        self.kernel_main()
    
    def kernel_main(self):
        print("[内核] Hello, My OS!(直接写显存)")
        print("[内核] 初始化中断表 (IDT 占位)...")
        self.idt = [{'handler': None} for _ in range(256)]
        print("[内核] 初始化内存分页 (占位)...")
        self.pagetable = {'cr3': 0x1000}
        self.running = True
        return self.running

os = MinimalOS()
os.boot()
print(f"[状态] 内核运行中: {os.running}")

运行输出:

[BIOS] 加载启动扇区 → 0x7C00
[Boot] 跳转内核入口 kernel_main
[内核] Hello, My OS!(直接写显存)
[内核] 初始化中断表 (IDT 占位)...
[内核] 初始化内存分页 (占位)...
[状态] 内核运行中: True

这就是操作系统启动的最小模型:引导 → 内核入口 → 初始化 → 主循环。

四、快速体验:编译并在 QEMU 中运行(真实环境)

如果你想立刻在电脑上跑起来(不伤硬件),用以下命令:

# 1. 编译内核(使用 i386-elf-gcc,独立环境)
i386-elf-gcc -c kernel.c -o kernel.o -ffreestanding -nostdlib -fno-builtin

# 2. 链接(需要 linker.ld 指定入口地址,例如 0x100000)
i386-elf-ld -T linker.ld -o kernel.bin kernel.o

# 3. 用 QEMU 模拟运行
qemu-system-i386 -kernel kernel.bin -serial mon:stdio

若没有 i386-elf-gcc,可用 gcc -m32 替代并配合相应链接脚本。链接脚本可参考 xv6 的 kernel.ld。

五、完整系列推荐

上面展示的只是第一行内核代码。真正的挑战在于:

  • 中断管理(IDT/GDT)
  • 内存分页(Paging)
  • 进程切换(Context Switch)
  • 抢占式调度

它们环环相扣,我在 《炼丹炉:零帧起手搭建操作系统》 系列中从第一行汇编引导开始,逐行实现完整内核,配套可运行的代码和调试教程,欢迎深入。


📢 订阅方式

该系列及 66+ 技术知识库已在 IMA 知识号发布,搜索「Kruptos」即可订阅。

8 款 AI 技能(系列生产/内容管理/CMMI 受管开发/自进化 Agent 等)已上线 IMA 技能广场,搜索即装。

在这里插入图片描述


作者:Kruptos(西电毕业,13 年无线通信/DSP/嵌入式科研,现深耕 AI 与云原生)

原创内容,转载注明出处。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐