8.从零开始写LINUX内核——初始化中断控制芯片

发布于:2025-08-18 ⋅ 阅读:(12) ⋅ 点赞:(0)

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 的启动信息,包括内存检测和进程初始化过程。

五、常见问题解决

  1. 汇编错误:invalid instruction suffix for `push'

    • 原因:现代 as 默认使用 64 位模式
    • 解决:所有汇编命令添加-32参数(已在上述命令中包含)
  2. 链接错误:cannot generate 32-bit executable

    • 原因:缺少 32 位库支持
    • 解决:安装gcc-multiliblibc6-dev-i386
  3. QEMU 启动后黑屏

    • 原因:引导扇区标志错误
    • 解决:检查 bootsect.s 末尾是否有.word 0xaa55

通过以上步骤,你可以在 Ubuntu 20.04 环境下成功构建并运行 Linux 0.12 内核,为进一步学习内核开发打下基础。后续可逐步研究进程管理、内存管理和文件系统等核心模块的实现。


网站公告

今日签到

点亮在社区的每一天
去签到