可以被GRUB启动的简单内核

1. 准备开发环境

apt install build-essential nasm grub-pc-bin xorriso

2. 项目结构

myos/
├── boot/
│   ├── boot.asm    # 汇编启动代码
│   └── grub.cfg    # GRUB配置文件
├── kernel/
│   └── main.c      # 主内核代码
├── linker.ld
└── Makefile        # 构建脚本

3. 引导代码 (boot/boot.asm)

boot.asm
; boot.asm - 多引导头定义
section .multiboot
align 4

; 多引导头定义
multiboot_header:
    dd 0x1BADB002              ; 魔数
    dd 0x00                    ; 标志
    dd -(0x1BADB002 + 0x00)    ; 校验和

global start
extern kernel_main             ; 声明在C代码中定义的主函数

section .text
start:
    ; 设置栈指针
    mov esp, stack_top
    
    ; 调用C内核
    call kernel_main
    
    ; 如果内核返回,进入无限循环
    cli
    hlt

section .bss
align 16
stack_bottom:
    resb 16384 ; 16KB栈空间
stack_top:

4. 内核 (kernel/main.c)

main.c
// VGA文本模式缓冲区地址
volatile unsigned short *vga_buffer = (unsigned short *)0xB8000;
const int VGA_COLS = 80;
const int VGA_ROWS = 25;

// 清屏函数
void clear_screen() {
    for (int i = 0; i < VGA_COLS * VGA_ROWS; i++) {
        vga_buffer[i] = (unsigned short)0x0F00 | ' ';
    }
}

// 打印字符串函数
void print_string(const char *str, int row, int col) {
    int index = row * VGA_COLS + col;
    for (int i = 0; str[i] != '\0'; i++) {
        vga_buffer[index++] = (unsigned short)0x0F00 | str[i];
    }
}

// 内核主函数
void kernel_main() {
    clear_screen();
    print_string("Hello from MyOS Kernel!", 12, 30);
    
    // 无限循环
    while (1);
}

5. GRUB配置文件 (boot/grub.cfg)

set timeout=0
set default=0

menuentry "MyOS" {
    clear
    multiboot /boot/myos.bin
    boot
}

6. Maklefile

# 目标名称
KERNEL := myos.bin
ISO := myos.iso

# 工具链
ASM := nasm
CC := gcc
LD := ld
GRUB_MKRESCUE := grub-mkrescue

# 编译选项
ASMFLAGS := -f elf32
CFLAGS := -m32 -ffreestanding -nostdlib -Wall -Wextra -c
LDFLAGS := -m elf_i386 -T linker.ld -nostdlib

# 源文件
BOOT_SRC := boot/boot.asm
KERNEL_SRC := kernel/main.c
OBJS := boot/boot.o kernel/main.o

all: $(ISO)

$(ISO): $(KERNEL)
	mkdir -p isodir/boot/grub
	cp $(KERNEL) isodir/boot/$(KERNEL)
	cp boot/grub.cfg isodir/boot/grub
	$(GRUB_MKRESCUE) -o $(ISO) isodir
	rm -rf isodir

$(KERNEL): $(OBJS)
	$(LD) $(LDFLAGS) -o $(KERNEL) $(OBJS)

boot/boot.o: $(BOOT_SRC)
	$(ASM) $(ASMFLAGS) -o $@ $<

kernel/main.o: $(KERNEL_SRC)
	$(CC) $(CFLAGS) -o $@ $<

clean:
	rm -f $(OBJS) $(KERNEL) $(ISO)

run: $(ISO)
	qemu-system-x86_64 -cdrom $(ISO)

.PHONY: all clean run

7. 链接器脚本 (linker.ld)

ENTRY(start)

SECTIONS {
    . = 1M;

    .text : ALIGN(4K) {
        *(.multiboot)
        *(.text)
    }

    .rodata : ALIGN(4K) {
        *(.rodata)
    }

    .data : ALIGN(4K) {
        *(.data)
    }

    .bss : ALIGN(4K) {
        *(COMMON)
        *(.bss)
        *(.stack)
    }
}

工作原理

  1. 引导过程
    • GRUB加载内核时,会查找多引导头
    • 找到后,GRUB将控制权交给我们的汇编启动代码
    • 汇编代码设置栈并调用C内核
  2. 内核功能
    • 清屏并在屏幕中央显示"Hello from MyOS Kernel!"
    • 使用VGA文本模式缓冲区直接写入显存