mcpField
O
os-dev-scratch

os-dev-scratch

github.com/mohitmishra786开发269305 次安装18 周前更新
代码仓库

关于 os-dev-scratch

面向操作系统学习者和内核开发者的从零构建 OS 指南,涵盖启动、内存、处理中断与上下文切换,并以 xv6-RISC-V 作为参考。

主要功能

  1. 01梳理 BIOS、UEFI 与引导阶段
  2. 02设置 x86-64 长模式与页表
  3. 03实现 IDT、中断处理和控制器配置
  4. 04编写串口与键盘驱动
  5. 05构建物理和虚拟内存管理
  6. 06实现上下文切换并用 QEMU 测试

适用场景

  1. 01学习从上电到内核入口的启动过程
  2. 02实现 x86-64 保护模式或长模式切换
  3. 03编写内存分配器和页表管理器
  4. 04在 QEMU 中测试中断与内核

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill os-dev-scratch

安装后在 Claude Code 中输入/os-dev-scratch确认已加载。

详细介绍

从零开始开发操作系统

用途

指导 Agent 从零构建一个最小操作系统:引导加载程序阶段(BIOS/GRUB 与 UEFI/limine)、使用 GDT 和页表设置 64 位长模式、IDT 和中断处理程序、PIC/APIC 配置、基本键盘和串口驱动、物理和虚拟内存管理器、上下文切换,并以 xv6-RISC-V 作为参考架构。

适用场景

  • 学习操作系统如何从通电启动到 main()
  • 实现 x86-64 上的保护模式/长模式切换
  • 编写物理内存分配器(位图)和页表管理器
  • 处理定时器、键盘和缺页中断
  • 使用 QEMU -kernel 和交叉编译器 x86_64-elf-gcc 进行测试
  • 将 xv6 中的概念移植到自定义 x86 或 RISC-V 内核

工作流

1. 引导阶段概览
BIOS path (legacy)
├── BIOS POST
├── MBR (512 bytes) → boot sector loads stage2
├── GRUB/multiboot → loads kernel ELF
└── kernel entry (_start)

UEFI path (modern)
├── UEFI firmware
├── EFI bootloader (limine, systemd-boot)
├── Loads kernel + initrd from ESP
└── kernel entry (handoff with memory map)
2. 工具链设置
# Cross-compiler for bare metal
brew install x86_64-elf-gcc x86_64-elf-binutils   # macOS
# or build from source / apt install gcc-x86-64-elf

x86_64-elf-gcc --version

# QEMU for testing
qemu-system-x86_64 --version

链接器脚本要点:

/* linker.ld */
ENTRY(_start)
SECTIONS {
    . = 0x100000;          /* 1MB — typical kernel load address */
    .text : { *(.text .text.*) }
    .rodata : { *(.rodata .rodata.*) }
    .data : { *(.data .data.*) }
    .bss : { *(.bss .bss.*) }
}
x86_64-elf-gcc -ffreestanding -nostdlib -c kernel.c -o kernel.o
x86_64-elf-ld -T linker.ld kernel.o -o kernel.elf
3. Multiboot/limine 引导
# QEMU direct kernel boot (no disk)
qemu-system-x86_64 \
  -kernel kernel.elf \
  -serial stdio \
  -m 128M \
  -no-reboot -no-shutdown

# With limine (UEFI)
qemu-system-x86_64 \
  -bios /usr/share/ovmf/OVMF.fd \
  -drive file=disk.img,format=raw \
  -serial stdio
4. 长模式设置
Protected mode (32-bit) → enable PAE → setup 4-level page tables → enable long mode
// Minimal GDT entry (64-bit flat segments)
struct gdt_entry {
    uint16_t limit_low;
    uint16_t base_low;
    uint8_t  base_mid;
    uint8_t  access;
    uint8_t  granularity;
    uint8_t  base_high;
} __attribute__((packed));

// Page table setup (4KB pages, identity map first 1GB)
uint64_t pml4[512] __attribute__((aligned(4096)));
uint64_t pdpt[512] __attribute__((aligned(4096)));
uint64_t pd[512] __attribute__((aligned(4096)));

void setup_paging(void) {
    for (int i = 0; i < 512; i++)
        pd[i] = (i * 0x200000) | 0x83;  // 2MB huge pages
    pdpt[0] = (uint64_t)pd | 0x03;
    pml4[0] = (uint64_t)pdpt | 0x03;
    __asm__ volatile("mov %0, %%cr3" :: "r"(pml4));
}
5. IDT 和中断处理程序
struct idt_entry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t  ist;
    uint8_t  type_attr;
    uint16_t offset_mid;
    uint32_t offset_high;
    uint32_t zero;
} __attribute__((packed));

// ISR stub (assembly) → common handler → dispatch by vector
void interrupt_handler(struct trap_frame *frame) {
    if (frame->vector == 14)  // page fault
        handle_page_fault(frame->cr2, frame->error_code);
    else if (frame->vector == 33)  // keyboard IRQ remapped
        keyboard_handler();
}
# Test page fault
# QEMU monitor: info registers
6. PIC 和 APIC
// Legacy PIC remapping (8259)
// Remap IRQ 0-15 to vectors 32-47
outb(0x20, 0x11); outb(0xA0, 0x11);
outb(0x21, 0x20); outb(0xA1, 0x28);  // vector offsets
// ...

// Modern: use APIC/IOAPIC (ACPI MADT parsing)
// LAPIC timer for preemption
7. 串口和键盘驱动
// COM1 serial (0x3F8)
void serial_putc(char c) {
    while ((inb(0x3F8 + 5) & 0x20) == 0);
    outb(0x3F8, c);
}

// PS/2 keyboard scancode → ASCII lookup table
void keyboard_handler(void) {
    uint8_t scancode = inb(0x60);
    char c = scancode_to_ascii[scancode];
    if (c) serial_putc(c);
    outb(0x20, 0x20);  // EOI to PIC
}
qemu-system-x86_64 -kernel kernel.elf -serial stdio
# printk output appears in terminal
8. 物理内存管理器
// Bitmap allocator over usable RAM regions
// From multiboot memory map or UEFI memory map
#define PAGE_SIZE 4096
uint8_t *frame_bitmap;
uint64_t total_frames;

uint64_t alloc_frame(void) {
    for (uint64_t i = 0; i < total_frames; i++) {
        if (!test_bit(frame_bitmap, i)) {
            set_bit(frame_bitmap, i);
            return i * PAGE_SIZE;
        }
    }
    return 0;  // OOM
}
9. 上下文切换
struct context {
    uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp;
    uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
    uint64_t rip;
};

void switch_context(struct context *old, struct context *new);
// Assembly: save callee-saved regs to old, restore from new, ret to new->rip

先实现协作式调度;之后再添加定时器 IRQ 抢占。

10. xv6-RISC-V 参考
git clone https://github.com/mit-pdos/xv6-riscv
cd xv6-riscv && make qemu
xv6 组件x86 等效项
kernel/vm.c页表管理
kernel/trap.cIDT/中断分发
kernel/proc.c上下文切换、调度器
kernel/plic.cPIC/APIC 中断控制器
user/usys.pl系统调用桩

常见问题

现象原因解决方法
启动时发生三重故障GDT/IDT 或栈无效启用中断前先设置栈
QEMU 黑屏未配置串口输出-serial stdio;提前调用 serial_init
内核发生缺页地址未映射对内核进行恒等映射;检查 CR3
IRQ 始终不触发PIC 掩码设置错误或未加载 IDT执行 lidt;在 PIC 中取消屏蔽 IRQ
定时器不走LAPIC 未初始化解析 ACPI;校准 LAPIC 定时器
链接器重定位错误加载地址错误使 linker.ld 与引导加载程序的预期一致

相关技能

  • skills/low-level-programming/assembly-x86 — 用于 ISR 桩的 x86-64 汇编
  • skills/low-level-programming/assembly-riscv — xv6-RISC-V 参考 ISA
  • skills/platform/riscv-privileged — RISC-V 陷阱处理和页表
  • skills/virtualization/qemu-kvm — 内核开发用 QEMU 参数
  • skills/kernel/kernel-internals — 这些概念在 Linux 中的实现
  • skills/low-level-programming/linux-kernel-modules — 基础运行正常后,进一步学习 Linux

评论

支持 Markdown