Ubuntu 20.04 环境下开发 Linux 0.12 内核全指南
一、开发环境搭建与工具链配置
1. 核心工具链安装
Linux 0.12 内核开发需要特定版本的工具链以确保兼容性,以下是在 Ubuntu 20.04 下的安装命令:
bash
# 安装基础编译工具
sudo apt update && sudo apt install -y \
binutils gcc-multilib g++-multilib \
qemu-system-i386 make xxd \
vim git libncurses5-dev
# 验证关键工具版本(Linux 0.12 兼容版本)
as --version | head -n1 # 需显示 2.34 或兼容版本
ld --version | head -n1 # 需显示 2.34 或兼容版本
qemu-system-i386 --version | head -n1 # 4.2 及以上版本
2. 源码获取与目录结构
bash
# 创建工作目录
mkdir -p ~/linux0.12-dev && cd ~/linux0.12-dev
# 获取 Linux 0.12 源码(官方归档)
wget https://mirrors.edge.kernel.org/pub/linux/kernel/historic/linux-0.12.tar.gz
tar -zxvf linux-0.12.tar.gz
mv linux-0.12 linux-0.12-src
# 目录结构说明
cd linux-0.12-src
ls -l
# boot/:引导相关代码(bootsect.s、setup.s)
# kernel/:内核核心代码
# fs/:文件系统实现
# lib/:库函数
# Makefile:顶层构建文件
二、引导系统开发(boot/ 目录)
1. 引导扇区(bootsect.s)
引导扇区是系统启动的第一个程序,位于磁盘第一个扇区(512 字节):
asm
/* bootsect.s - Linux 0.12 引导扇区
* 功能:加载 setup 程序并跳转执行
*/
.code16 /* 16位实模式 */
.text
.global _start
BOOTSEG = 0x07c0 /* BIOS加载地址 */
INITSEG = 0x9000 /* 重定位目标地址 */
SETUPSEG = 0x9020 /* setup程序加载地址 */
SETUPLEN = 4 /* setup程序占用扇区数 */
_start:
jmpi $BOOTSEG, $start2 /* 跳转到正确地址 */
start2:
/* 初始化段寄存器 */
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0xff00, %sp /* 设置栈指针 */
/* 清屏操作 */
movw $0x0600, %ax
xorw %bx, %bx
movb $0, %ch
movb $0, %cl
movb $24, %dh
movb $79, %dl
int $0x10
/* 显示引导信息 */
movw $msg1, %ax
movw %ax, %bp
movw $0x1301, %ax
movw $0x000c, %bx
movw $25, %cx
movb $0, %dh
movb $0, %dl
int $0x10
/* 移动自身到0x90000 */
movw $BOOTSEG, %ax
movw %ax, %ds
movw $INITSEG, %ax
movw %ax, %es
xorw %si, %si
xorw %di, %di
movw $256, %cx
rep
movsw
jmpi $INITSEG, $go
go:
/* 加载setup程序 */
movw $0x0000, %dx
movw $0x0002, %cx
movw $0x0200, %bx
movb $SETUPLEN, %al
movb $0x02, %ah
int $0x13
jnc ok_load_setup
/* 读盘错误处理 */
xorw %ax, %ax
int $0x13
jmp go
ok_load_setup:
/* 跳转到setup程序 */
jmpi $SETUPSEG, $0
msg1:
.ascii "Loading Linux 0.12..."
.org 510
.word 0xaa55 /* 引导标志 */
2. 编译引导扇区
bash
# 进入boot目录
cd ~/linux0.12-dev/linux-0.12-src/boot
# 汇编生成二进制
as -32 -o bootsect.o bootsect.s
ld -m elf_i386 -Ttext 0x7c00 -s --oformat binary -o bootsect bootsect.o
# 验证大小(必须512字节)
ls -l bootsect | awk '{print $5 " bytes (should be 512)"}'
三、Setup 程序开发(setup.s)
Setup 程序负责实模式到保护模式的过渡:
asm
/* setup.s - Linux 0.12 系统设置程序
* 功能:收集硬件信息、初始化GDT、切换到保护模式
*/
.code16
.text
.global _start_setup
INITSEG = 0x9000
SETUPSEG = 0x9020
_start_setup:
/* 初始化段寄存器 */
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
/* 收集硬件信息 */
# 获取光标位置
movb $0x03, %ah
xor %bh, %bh
int $0x10
movw %dx, (0)
# 获取内存大小
movb $0x88, %ah
int $0x15
movw %ax, (2)
# 获取显示模式
movb $0x0f, %ah
int $0x10
movw %bx, (4)
movw %ax, (6)
/* 显示setup运行信息 */
movw $msg, %ax
movw %ax, %bp
movw $0x1301, %ax
movw $0x000c, %bx
movw $16, %cx
movb $3, %dh
movb $0, %dl
int $0x10
/* 准备进入保护模式 */
cli
/* 移动内核到低地址 */
movw $0x0000, %ax
cld
do_move:
movw %ax, %es
addw $0x1000, %ax
cmpw $0x9000, %ax
jz end_move
movw %ax, %ds
xorw %di, %di
xorw %si, %si
movw $0x8000, %cx
rep
movsw
jmp do_move
end_move:
/* 加载GDT */
lgdt gdt_48
/* 初始化8259A中断控制器 */
call empty_8042
movb $0xd1, %al
outb %al, $0x64
call empty_8042
movb $0xdf, %al
outb %al, $0x60
call empty_8042
/* 切换到保护模式 */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
/* 跳转到32位内核 */
.byte 0x66, 0xea
.long 0x00000000
.word 0x0008
empty_8042:
.word 0x00eb, 0x00eb
inb $0x64, %al
testb $2, %al
jnz empty_8042
ret
/* GDT定义 */
gdt:
.word 0, 0, 0, 0
.word 0x07ff, 0x0000, 0x9a00, 0x00c0
.word 0x07ff, 0x0000, 0x9200, 0x00c0
gdt_48:
.word 0x800
.word 512 + gdt, 0x9
msg:
.ascii "setup running"
.fill 2048 - (.-_start_setup), 1, 0
编译命令:
bash
as -32 -o setup.o setup.s
ld -m elf_i386 -Ttext 0x0 -s --oformat binary -e _start_setup -o setup setup.o
四、内核编译与运行
1. 修改 Makefile(适配 Ubuntu 20.04)
需要调整顶层 Makefile 以确保兼容现代工具链:
bash
# 编辑顶层Makefile
vim ~/linux0.12-dev/linux-0.12-src/Makefile
# 修改以下内容:
# CC = gcc → CC = gcc -m32
# LD = ld → LD = ld -m elf_i386
# 增加ASFLAGS = -32
2. 完整编译流程
bash
# 回到源码根目录
cd ~/linux0.12-dev/linux-0.12-src
# 清理并编译
make clean
make Image
# 生成软盘镜像
dd if=/dev/zero of=linux0.12.img bs=1440k count=1
dd if=boot/bootsect of=linux0.12.img bs=512 count=1 conv=notrunc
dd if=boot/setup of=linux0.12.img bs=512 count=4 seek=1 conv=notrunc
dd if=Image of=linux0.12.img bs=512 count=2875 seek=5 conv=notrunc
3. 使用 QEMU 运行
bash
qemu-system-i386 -fda linux0.12.img -boot a -vga std -no-reboot
成功运行后,QEMU 窗口将显示 Linux 0.12 的启动信息,包括内存检测和进程初始化过程。
五、常见问题解决
汇编错误:invalid instruction suffix for `push'
- 原因:现代 as 默认使用 64 位模式
- 解决:所有汇编命令添加
-32
参数(已在上述命令中包含)
链接错误:cannot generate 32-bit executable
- 原因:缺少 32 位库支持
- 解决:安装
gcc-multilib
和libc6-dev-i386
QEMU 启动后黑屏
- 原因:引导扇区标志错误
- 解决:检查 bootsect.s 末尾是否有
.word 0xaa55
通过以上步骤,你可以在 Ubuntu 20.04 环境下成功构建并运行 Linux 0.12 内核,为进一步学习内核开发打下基础。后续可逐步研究进程管理、内存管理和文件系统等核心模块的实现。