BasicOS - A Minimal Operating System
Overview
心血来潮,准备写一个操作系统,记录一下学习过程。主要参考著名的《恐龙书》。从bootloader开始,逐步实现一个最小的操作系统。
既然是操作系统,那么就避免不了汇编语言。之后会使用C语言来实现。最后如果有余力的话,会使用Rust来实现。
环境
- Ubuntu 24.04
- qemu
- nasm
- gcc
- make
安装环境
sudo apt-get update
sudo apt-get install nasm qemu gcc gcc-multilib
安装debug工具
sudo apt-get install xxd gdb
GitHub地址
https://xxxxx。xxx/toronto-andrew/BasicOS.git
先写一个最简单的bootloader
BIOS (Basic Input/Output System) 我们每个人都最熟悉了。当然,新的计算机都使用 UEFI(Unified Extensible Firmware Interface)了。
UEFI 和单纯使用 BIOS 的区别在于加载内核的方式、准备工作和高级功能等。这里为了方便起见,我们暂时只考虑 BIOS(因为最简单)。
电脑刚一开机,BIOS 的作用是告诉计算机从哪里加载操作系统到内存。于是,有人规定了,操作系统应当放在存储设备最开始的 512 字节(例如磁盘第 0 柱面第 0 磁头第 0 扇区)。这个区域就是我们的引导扇区。也就是说,操纵系统运行的第一行代码就是在引导扇区中。
然而,一台计算机可能有多个存储设备,BIOS 依然不知道哪个设备存储了引导扇区。但不知道谁又规定了,引导扇区的最后两个字节必须是 0xaa55。于是,BIOS 只需要遍历所有存储设备,检查他们的第 511 和 512 字节是否是 0xaa55。如果是,就说明找到了操作系统的位置,把这一段数据加载到内存中,然后跳转到这段代码的第一个字节开始执行。
因此,对于手动编写一个引导扇区来说,只需要:
1 首先把最后两个字节设置为 0xaa55;
2 然后从第一个字节开始写上想要的代码;
3 最后把其它的字节填充为 0,补满 512 字节。
代码如下boot.asm:
[bits 16] ; 告诉汇编器我们是在 16 位下工作
jmp $ ; $ 表示当前地址,跳转到当前地址就是死循环
times 510-($-$$) db 0 ; $ 表示当前地址,$$ 表示当前段的开始地址
; 510-($-$$) 计算出当前位置到 510 字节的距离,然后全部填充为 0
dw 0xaa55 ; 最后两个字节是 0xaa55
编译
nasm boot.asm -f bin -o boot.bin
运行
qemu-system-x86_64 boot.bin
你会看到窗口中显示 Booting from Hard Disk...,然后它就开始执行死循环了。