2.从零开始写LINUX内核—导扇区与 Setup 程序开发

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

AT&T 语法引导扇区与 Setup 程序开发:Ubuntu 20.04 全流程实现(含工具链与镜像构建)

在操作系统启动流程中,引导扇区(bootsect)是 BIOS 加载的第一道程序,而 setup 程序则负责后续初始化工作。本文将基于 Ubuntu 20.04 环境,使用 AT&T 语法开发完整的引导扇区与 setup 程序组合,详细介绍工具链配置、镜像构建流程及 QEMU 运行验证,为内核开发提供基础启动框架。

一、开发环境与工具链详情

1. 核心工具链清单及版本(Ubuntu 20.04 验证通过)

工具名称 作用说明 验证版本示例
as GNU 汇编器(支持 AT&T 语法) 2.34
ld GNU 链接器(生成二进制可执行文件) 2.34
gcc C 编译器(编译镜像拼接工具) 9.4.0
qemu-system-i386 x86 架构模拟器(运行启动镜像) 1:4.2-3ubuntu6.30
qemu-img QEMU 镜像管理工具 1:4.2-3ubuntu6.30
dd 磁盘数据复制工具(制作镜像) 8.30
xxd 二进制文件查看工具(验证扇区) 2.34
objdump 反汇编工具(调试二进制文件) 2.34
vim/nano 文本编辑器(编写代码) 18:12:44 / 4.8

2. 工具链安装与验证

(1)一键安装所有工具

bash

sudo apt update && sudo apt install -y \
binutils gcc qemu-system-x86 qemu-utils \
util-linux xxd vim nano
(2)工具链完整性检查

通过脚本验证环境是否满足开发需求:

bash

# 创建检查脚本
cat > check_boot_env.sh << 'EOF'
#!/bin/bash

# 检查核心工具是否安装
tools=(
    "as" "ld" "gcc" "qemu-system-i386"
    "dd" "xxd" "objdump" "vim"
)

missing=0
echo "===== 工具链版本检查 ====="
for tool in "${tools[@]}"; do
    if command -v "$tool" &> /dev/null; then
        version=$("$tool" --version 2>&1 | head -n 1 | awk '{print $NF}' | sed 's/[(),]//g')
        echo "[OK] $tool: $version"
    else
        echo "[ERROR] $tool 未安装"
        missing=1
    fi
done

# 检查汇编器对16位代码的支持
echo -e "\n===== 汇编功能检查 ====="
cat > test.s << 'END'
.code16
.global _start
_start:
    movw %ax, %bx
END

if as -32 -o test.o test.s 2>&1; then
    echo "[OK] 16位汇编支持正常"
else
    echo "[ERROR] 16位汇编支持异常"
    missing=1
fi
rm -f test.s test.o

if [ $missing -eq 1 ]; then
    echo -e "\n请重新安装缺失工具"
else
    echo -e "\n[SUCCESS] 开发环境就绪"
fi
EOF

# 执行检查
chmod +x check_boot_env.sh
./check_boot_env.sh

运行脚本后若显示 [SUCCESS] 开发环境就绪,则可进入后续开发。

二、QEMU 运行配置详解

QEMU 是模拟 x86 硬件并运行启动镜像的核心工具,针对引导扇区与 setup 程序的配置如下:

1. 基础运行命令

bash

qemu-system-i386 -fda linux.img -boot a -vga std

参数说明:

  • -fda linux.img:指定软盘镜像文件(引导扇区需位于镜像起始位置)。
  • -boot a:设置从软盘(A 盘)启动,符合 BIOS 启动顺序规范。
  • -vga std:使用标准 VGA 显示模式,确保字符输出正常。

2. 调试增强配置

如需观察 BIOS 加载过程或调试输出,可添加调试参数:

bash

# 显示 BIOS 启动日志与中断调用信息
qemu-system-i386 -fda linux.img -boot a -vga std -debugcon stdio -D qemu_log.txt

  • -debugcon stdio:将调试信息输出到终端,可查看引导扇区加载地址等细节。
  • -D qemu_log.txt:将调试日志保存到文件,便于后续分析。

三、引导扇区与 Setup 程序开发全流程

1. 编写 AT&T 语法汇编代码

(1)引导扇区程序(bootsect.s)

功能:自迁移至安全地址、清屏、加载 setup 程序并跳转执行。

nasm

/* 引导扇区:512字节,BIOS加载的第一个程序 */
.code16                     ; 指定16位实模式
.text
.global _start

/* 常量定义 */
SETUPLEN  = 4               ; setup程序占用扇区数(4×512=2048字节)
BOOTSEG   = 0x07C0          ; BIOS加载引导扇区的段地址
INITSEG   = 0x9000          ; 引导扇区迁移后的目标段地址
SETUPSEG  = 0x9020          ; setup程序在内存中的加载段地址
ROOT_DEV  = 0x0000          ; 根设备标识(可根据实际设备修改)

_start:
    jmpl    $BOOTSEG, $start2  ; 初始化CS寄存器为BOOTSEG

start2:
    /* 将自身从0x07C0:0000迁移到0x9000:0000 */
    movw    $BOOTSEG, %ax     ; AX = 源段地址(BOOTSEG)
    movw    %ax, %ds          ; DS = 源段(数据段指向源地址)
    movw    $INITSEG, %ax     ; AX = 目标段地址(INITSEG)
    movw    %ax, %es          ; ES = 目标段(附加段指向目标地址)
    xorw    %si, %si          ; SI = 0(源地址偏移量)
    xorw    %di, %di          ; DI = 0(目标地址偏移量)
    movw    $256, %cx         ; CX = 256(重复次数,每次移动2字节,共512字节)
    rep                       ; 重复执行movsw直到CX=0
    movsw                     ; 从[DS:SI]复制1字到[ES:DI],自动递增SI和DI

    /* 跳转到迁移后的新地址继续执行 */
    ljmp    $INITSEG, $go     ; CS=INITSEG,IP=go

go:
    /* 初始化段寄存器与栈指针 */
    movw    %cs, %ax          ; AX = 当前代码段(INITSEG)
    movw    %ax, %ds          ; DS = 代码段(数据访问基准)
    movw    %ax, %es          ; ES = 代码段(附加数据访问)
    movw    %ax, %ss          ; SS = 代码段(栈段基准)
    movw    $0xFF00, %sp      ; SP = 0xFF00(设置栈顶地址)

ok_load_setup:
    /* 清屏(BIOS中断0x10,功能0x06) */
    movw    $0x0600, %ax      ; AH=0x06(上滚窗口),AL=0x00(清屏模式)
    movw    $0x0700, %bx      ; BH=0x07(空白区域属性:黑底白字)
    movw    $0x184F, %cx      ; 窗口范围:(0,0)到(24,79)(全屏)
    int     $0x10             ; 调用BIOS视频服务

    /* 打印"Setup has been loaded" */
    movw    $msg, %ax         ; AX = 字符串偏移地址
    movw    %ax, %bp          ; BP = 字符串地址(BIOS中断要求)
    movw    $0x1301, %ax      ; AH=0x13(显示字符串),AL=0x01(光标跟随)
    movw    $0x000C, %bx      ; BH=0x00(页0),BL=0x0C(红底白字)
    movw    $21, %cx          ; 字符串长度(21字符)
    xorw    %dh, %dh          ; DH=0(行号)
    xorw    %dl, %dl          ; DL=0(列号)
    int     $0x10             ; 调用BIOS视频服务

load_setup:
    /* 读取setup程序到内存(BIOS中断0x13,功能0x02) */
    movw    $0x0000, %dx      ; DH=0(磁头号),DL=0(驱动器号:软盘)
    movw    $0x0002, %cx      ; CH=0(柱面号),CL=2(扇区号:从第2扇区开始)
    movw    $0x0200, %bx      ; ES:BX = 0x9020:0000(setup加载地址)
    movb    $SETUPLEN, %al    ; AL=4(读取扇区数)
    movb    $0x02, %ah        ; AH=0x02(读磁盘扇区)
    int     $0x13             ; 调用BIOS磁盘服务
    jnc     read_done         ; 无错误则跳转至read_done
    /* 读取失败时重置磁盘并重试 */
    xorw    %ax, %ax          ; AH=0x00(重置磁盘)
    int     $0x13
    jmp     load_setup

read_done:
    ljmp    $SETUPSEG, $0     ; 跳转到setup程序入口

msg:
    .ascii  "Setup has been loaded"  ; 引导扇区提示字符串
    .org    508                     ; 定位到508字节处
    .word   ROOT_DEV                ; 根设备标识(2字节)
    .word   0xAA55                  ; 引导扇区有效标志(BIOS识别用)
(2)Setup 程序(setup.s)

功能:验证引导流程、打印运行状态并进入循环。

nasm

/* setup程序:2048字节,引导扇区的后续执行程序 */
.code16                     ; 16位实模式
.text
.global _start_setup

_start_setup:
    /* 初始化段寄存器 */
    movw    %cs, %ax          ; AX = 当前代码段(SETUPSEG)
    movw    %ax, %ds          ; DS = 代码段(数据访问)
    movw    %ax, %es          ; ES = 代码段(附加数据访问)

    /* 打印"setup is running"(BIOS中断0x10) */
    movw    $setup_msg, %ax   ; AX = 字符串偏移地址
    movw    %ax, %bp          ; BP = 字符串地址
    movw    $0x1301, %ax      ; AH=0x13(显示字符串),AL=0x01(光标跟随)
    movw    $0x000C, %bx      ; BH=0x00(页0),BL=0x0C(红底白字)
    movw    $16, %cx          ; 字符串长度(16字符)
    movb    $3, %dh           ; DH=3(行号:第3行)
    movb    $0, %dl           ; DL=0(列号)
    int     $0x10             ; 调用BIOS视频服务

hang:                        ; 无限循环(防止程序跑飞)
    jmp     hang

setup_msg:
    .ascii  "setup is running"      ; setup提示字符串
    /* 填充至2048字节(4扇区) */
    .fill   2048-(.-_start_setup), 1, 0

2. 编写镜像拼接工具(build.c)

功能:将引导扇区与 setup 程序按顺序拼接为完整镜像。

c

运行

/* 镜像拼接工具:组合bootsect和setup为可启动镜像 */
#include <unistd.h>   // 包含read、write、close系统调用
#include <fcntl.h>    // 包含open函数及文件打开模式定义

int main(void)
{
    char buf[2048];    // 缓冲区(最大适配setup程序大小)
    int fd;            // 文件描述符

    /* 读取引导扇区(512字节)并输出 */
    fd = open("bootsect", O_RDONLY);  // 打开bootsect文件
    read(fd, buf, 512);               // 读取512字节(引导扇区大小)
    write(1, buf, 512);               // 写入标准输出
    close(fd);                        // 关闭文件

    /* 读取setup程序(2048字节)并输出 */
    fd = open("setup", O_RDONLY);     // 打开setup文件
    read(fd, buf, 2048);              // 读取2048字节(4扇区)
    write(1, buf, 2048);              // 写入标准输出
    close(fd);                        // 关闭文件

    return 0;                         // 程序正常退出
}

3. 编写自动化构建脚本(Makefile)

功能:定义编译、链接、镜像生成的完整流程。

makefile

# 工具链定义(适配i386架构)
AS      := as --32              # 32位汇编器(兼容16位代码)
LD      := ld -m elf_i386       # i386架构链接器
LDFLAGS := -Ttext 0x0 -s --oformat binary  # 链接选项:二进制输出,无符号表

# 默认目标:生成最终镜像
all: linux.img

# 生成linux.img:通过build工具拼接bootsect和setup
linux.img: build bootsect setup
    ./build > linux.img  # 拼接输出为镜像文件

# 生成引导扇区二进制文件
bootsect: bootsect.o
    $(LD) $(LDFLAGS) -o $@ $<  # 链接bootsect.o为二进制

# 编译引导扇区汇编代码
bootsect.o: bootsect.s
    $(AS) -o $@ $<  # 汇编生成目标文件

# 生成setup程序二进制文件(指定入口点)
setup: setup.o
    $(LD) $(LDFLAGS) -e _start_setup -o $@ $<  # -e指定入口为_start_setup

# 编译setup程序汇编代码
setup.o: setup.s
    $(AS) -o $@ $<  # 汇编生成目标文件

# 编译镜像拼接工具
build: build.c
    gcc -o $@ $<  # 编译C程序

# 清理中间文件与目标文件
clean:
    rm -f *.o bootsect setup build linux.img

# 运行镜像(使用QEMU)
run:
    qemu-system-i386 -fda linux.img -boot a -vga std

4. 编译与验证流程

(1)编译生成镜像

bash

# 执行Makefile构建流程
make

执行成功后,目录下将生成 linux.img 镜像文件。

(2)验证文件完整性

bash

# 验证引导扇区大小(必须为512字节)
ls -l bootsect | awk '{print "bootsect大小:" $5 "字节"}'

# 验证setup程序大小(必须为2048字节)
ls -l setup | awk '{print "setup大小:" $5 "字节"}'

# 验证引导扇区标志(最后两字节应为0xAA55)
xxd -s 510 -l 2 bootsect
(3)运行镜像

bash

# 启动QEMU运行镜像
make run

成功运行后,QEMU 窗口将先显示 Setup has been loaded(引导扇区输出),随后显示 setup is running(setup 程序输出),表明整个启动流程正常。

四、常见问题解决

  1. 引导扇区无法被 BIOS 识别
    • 检查引导扇区大小是否为 512 字节(超出则 BIOS 忽略)。
    • 验证末尾是否存在 0xAA55 标志

网站公告

今日签到

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