+
27
-

回答

用 Go 编写简单操作系统

Go 标准编译器依赖运行时,不太适合裸机开发。以下是两种方案:

方案一:使用 TinyGo(推荐)

1. 环境准备

# 安装 TinyGo
wget https://github.com/tinygo-org/tinygo/releases/download/v0.30.0/tinygo_0.30.0_amd64.deb
sudo dpkg -i tinygo_0.30.0_amd64.deb

# 安装必要工具
sudo apt install nasm qemu-system-x86 xorriso

2. 引导加载程序 (boot.asm)

; boot.asm - 16位引导扇区
[BITS 16]
[ORG 0x7C00]

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00

    ; 加载内核到内存
    mov bx, 0x1000      ; 加载到 0x1000
    mov ah, 0x02        ; 读取扇区
    mov al, 20          ; 读取 20 个扇区
    mov ch, 0           ; 柱面 0
    mov cl, 2           ; 从扇区 2 开始
    mov dh, 0           ; 磁头 0
    int 0x13

    ; 跳转到保护模式
    jmp 0x1000:0x0000

times 510-($-$$) db 0
dw 0xAA55               ; 引导签名

3. Go 内核代码 (kernel.go)

package main

// 禁用 Go 运行时
//go:build tinygo

const (
    VGA_WIDTH  = 80
    VGA_HEIGHT = 25
    VGA_MEMORY = 0xB8000
)

type Color uint8

const (
    Black      Color = 0
    Blue       Color = 1
    Green      Color = 2
    Cyan       Color = 3
    Red        Color = 4
    Magenta    Color = 5
    Brown      Color = 6
    LightGray  Color = 7
    White      Color = 15
)

var (
    row    uint8
    column uint8
    color  uint8
)

//go:export _start
func _start() {
    row = 0
    column = 0
    color = makeColor(White, Blue)

    clearScreen()
    print("Hello from Go OS!\n")
    print("This is a simple OS written in Go\n")
    print("Running in VirtualBox!\n")

    // 无限循环,防止系统退出
    for {
        // CPU 休眠
        halt()
    }
}

func makeColor(fg, bg Color) uint8 {
    return uint8(fg) | uint8(bg)<<4
}

func clearScreen() {
    vgaBuffer := (*[VGA_WIDTH * VGA_HEIGHT]uint16)(uintptr(VGA_MEMORY))
    blank := uint16(' ') | uint16(color)<<8

    for i := 0; i < VGA_WIDTH*VGA_HEIGHT; i++ {
        vgaBuffer[i] = blank
    }
    row = 0
    column = 0
}

func print(s string) {
    for i := 0; i < len(s); i++ {
        putChar(s[i])
    }
}

func putChar(c byte) {
    if c == '\n' {
        column = 0
        row++
        if row >= VGA_HEIGHT {
            row = 0
        }
        return
    }

    vgaBuffer := (*[VGA_WIDTH * VGA_HEIGHT]uint16)(uintptr(VGA_MEMORY))
    index := int(row)*VGA_WIDTH + int(column)
    vgaBuffer[index] = uint16(c) | uint16(color)<<8

    column++
    if column >= VGA_WIDTH {
        column = 0
        row++
        if row >= VGA_HEIGHT {
            row = 0
        }
    }
}

//go:inline
func halt() {
    asm("hlt")
}

// 编译器需要的符号
//go:export memset
func memset(ptr uintptr, val byte, size uintptr) {
    for i := uintptr(0); i < size; i++ {
        *(*byte)(unsafe.Pointer(ptr + i)) = val
    }
}

//go:export memcpy
func memcpy(dst, src uintptr, size uintptr) {
    for i := uintptr(0); i < size; i++ {
        *(*byte)(unsafe.Pointer(dst + i)) = *(*byte)(unsafe.Pointer(src + i))
    }
}

4. 链接脚本 (linker.ld)

ENTRY(_start)

SECTIONS
{
    . = 0x1000;

    .text : {
        *(.text)
    }

    .rodata : {
        *(.rodata)
    }

    .data : {
        *(.data)
    }

    .bss : {
        *(.bss)
    }
}

5. 构建脚本 (build.sh)

#!/bin/bash

# 编译引导加载程序
nasm -f bin boot.asm -o boot.bin

# 编译 Go 内核
tinygo build -o kernel.elf \
    -target=none \
    -opt=z \
    -no-debug \
    -panic=trap \
    -scheduler=none \
    -gc=none \
    -ldflags="-T linker.ld" \
    kernel.go

# 提取二进制
objcopy -O binary kernel.elf kernel.bin

# 创建磁盘镜像
dd if=/dev/zero of=os.img bs=512 count=2880
dd if=boot.bin of=os.img conv=notrunc
dd if=kernel.bin of=os.img bs=512 seek=1 conv=notrunc

echo "构建完成!使用以下命令测试:"
echo "qemu-system-x86_64 -drive format=raw,file=os.img"
echo "或导入到 VirtualBox"

6. VirtualBox 配置

# 创建虚拟机
VBoxManage createvm --name "GoOS" --register
VBoxManage modifyvm "GoOS" --memory 128 --boot1 disk --nic1 nat
VBoxManage storagectl "GoOS" --name "IDE" --add ide
VBoxManage storageattach "GoOS" --storagectl "IDE" --port 0 \
    --device 0 --type hdd --medium os.img

方案二:Go + Multiboot

使用 GRUB 引导(更简单)

// 使用 Multiboot 规范
package main

const MULTIBOOT_HEADER_MAGIC = 0x1BADB002

//go:section .multiboot
var multibootHeader = [...]uint32{
    MULTIBOOT_HEADER_MAGIC,
    0x00000000,
    -(MULTIBOOT_HEADER_MAGIC + 0x00000000),
}

func main() {
    // 你的 OS 代码
}

关键注意事项

内存管理:需要自己实现分配器

中断处理:需要设置 IDT

驱动程序:键盘、磁盘等需要自己写

调试困难:建议先用 QEMU 测试

测试运行

# 构建
chmod +x build.sh
./build.sh

# QEMU 测试
qemu-system-x86_64 -drive format=raw,file=os.img

# VirtualBox 运行
VBoxManage startvm "GoOS"

这是一个最小化示例,实际操作系统需要实现更多功能(内存管理、进程调度、文件系统等)。建议先从简单的"Hello World"开始,逐步添加功能。

网友回复

我知道答案,我要回答