操作系统实验 | 基础环境搭建

Overview

这个学期有OS的系统实践课程,在此记录下我的实验步骤,方便各位同学学习。这篇主要是环境搭建的过程,包括bochs 3.0qemu的环境搭建

Bochs-3.0

安装

在官网下载源码,链接

tar -zxvf bochs-3.0.tar.gz
cd bochs-3.0/
Bash

安装依赖

sudo apt install libx11-dev libc6-dev build-essential xorg-dev libgtk2.0-dev libreadline-dev libsdl2-dev
Bash

编写配置文件

./configure --with-sdl2 --enable-debugger --enable-all-optimizations --enable-readline --enable-debugger-gui --enable-x86-debugger --enable-a20-pin --enable-fast-function-calls
Bash

--enable-debugger--gdb-stub不能同时开启

安装

sudo make all-clean
sudo make -j 20
sudo make install
Bash

安装成功

制作一个可启动的软盘

首先写入以下代码

org 07c00h
mov ax, cs
mov ds, ax
mov es, ax 
call DispStr 
jmp $
DispStr:
    mov ax, BootMessage
    mov bp, ax 
    mov cx, 16
    mov ax, 01301h 

    mov bx, 000ch 
    mov dl, 0 
    int 10h 
    ret 

BootMessage: db 'Hello, World! 123', 0
times 510 - ($ - $$) db 0
dw 0xaa55
ASM

然后编译源码

nasm boot.asm -o boot.bin       # 生成引导文件
nasm boot.asm -o boot.com       # 生成com文件
Bash

没有nasm的需要自行安装sudo apt install nasm

制作一张虚拟软驱

bximage
bximage> 0
bximage> fd
bximage> 1.44M
bximage> a.img
Bash

写引导盘

dd if=boot.bin of=a.img bs=512 count=1
Bash

  • if=boot.bin → 输入文件
  • of=a.img → 输出文件(目标软盘镜像)
  • bs=512 count=1 → 只写 1 个扇区(512 字节)
  • conv=notrunc → 不截断 a.img 的大小

这样 a.img 就变成了一个可以引导的软盘镜像。

编写bochsrc

bochsrc相当于bochs的配置文件,里面写的是虚拟机的硬件参数:内存大小、CPU类型、加载哪个软盘/硬盘镜像,用什么GUI等等

在这里我列出我自己写的bochsrc文件

megs: 16
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest.bin

floppya: 1_44=a.img, status=inserted
boot: a

log: bochsout.txt
Bash

其中romimagevgaromimage可以使用以下命令查找

sudo find / -name 'BIOS-bochs-latest'
sudo find / -name 'VGABIOS-lgpl-latest.bin'
Bash

当然如果你虚拟机文件很多,其实也可以在/usr下找

a.img是我们先前创建好的软盘镜像

运行

bochs -f ./bochsrc          # 引导过一次后续好像就不需要引导了
Bash

但是要注意,如果你是vmware虚拟机,不需要关心调试的问题,但是如果是wsl还需要再启动时添加一个参数

bochs -debugger           # 立即开启调试窗口
Bash

然后就可以开心的调试了,要不是这个代码是i8086的,gdb原生不支持,直接qemu启动了

qemu

前面的操作都一样,我们从制作一个可启动的软盘开始

制作一个可启动的软盘

先写入asm文件

org 07c00h
mov ax, cs
mov ds, ax
mov es, ax 
call DispStr 
jmp $
DispStr:
    mov ax, BootMessage
    mov bp, ax 
    mov cx, 16
    mov ax, 01301h 

    mov bx, 000ch 
    mov dl, 0 
    int 10h 
    ret 

BootMessage: db 'Hello, World! 123', 0
times 510 - ($ - $$) db 0
dw 0xaa55
ASM

然后制作软盘

nasm -f bin boot.asm -o boot.bin
dd if=/dev/zero of=boot.img bs=512 count=2880
dd if=boot.bin of=boot.img conv=notrunc
Bash

qemu运行

qemu-system-i386 -fda boot.img
Bash

调试运行

qemu-system-i386 -fda boot.img -s -S
Bash

  • -s = 开启 gdbserver,监听在 :1234
  • -S = 启动后暂停 CPU,不会立即运行

起一个gdb

gdb
pwndbg> target remote :1234
pwndbg> b *0x7c00
pwndbg> c
Bash

但是无法识别i8086的汇编

实验内容

删除0xaa55

结果

首先放结果,No bootable device

原因

0xaa55是引导扇区的魔术签名(Boot Signature),这是BIOS识别有效引导扇区的关键标识。

  1. BIOS引导检查机制
    • BIOS在启动时会检查第一个扇区(引导扇区)的最后两个字节
    • 这两个字节必须是0x55 0xAA(小端格式存储)
    • 如果没有这个签名,BIOS会认为这不是一个有效的引导扇区
  2. 引导扇区结构要求
    • 引导扇区必须是512字节
    • 前510字节是代码和数据
    • 最后2字节必须是0xaa55
  3. 没有0xaa55的后果
    • BIOS不会将控制权交给你的代码
    • 系统会继续寻找其他可引导设备
    • 最终显示”No bootable device found”

可以在输出中发现

依次找了Hard Disk -> Floppy -> DVD/CD -> ROM -> network,最后返回了No bootable device

为什么要jmp $

jmp $相当于一个无限循环,表示跳转到当前位置,这样做有两个目的:

  • 程序需要打印hello world,防止打印完成之后被后续的内容刷新掉
  • 避免执行垃圾代码,如果没有这个循环,CPU会继续执行后面的数据区域,把数据当作指令执行,那系统就跑飞了,容易造成损坏

可以发现当程序执行到这里会一直执行跳转

修改后

会将后续的数据区域当作代码执行

如何让输出过程执行100次

思路就是设置一个cx计数器循环并设置一个行号dh,同时更新字符串末尾,添加计数器

具体代码如下,已在具体位置写入注释便于理解

org 07c00h
mov ax, cs
mov ds, ax
mov es, ax 

mov cx, 100
mov dh, 0          ; 初始化行号
mov si, 0          ; 初始计数器
loop1: 
call UpdateCounter       ; 在字符串末尾添加计数器
call DispStr 
inc dh             ; 行号加1
cmp dh, 25         ; 检查是否超过屏幕行数
jl no_reset        ; 如果没超过,继续
mov dh, 0          ; 重置到第0行
no_reset:
loop loop1
jmp $            


UpdateCounter:
    push ax
    push bx
    push cx
    push dx

    ; 计算当前计数器的十位和个位
    mov ax, si
    mov bx, 10
    xor dx, dx
    div bx            ; ax = si / 10, dx = si % 10

    ; 更新字符串的个位数字
    add dl, '0'      ; 将个位数字转换为字符
    mov [BootMessage + 27], dl

    ; 更新字符串的十位数字
    cmp ax, 0        ; 检查十位是否为0
    je skip_tens     ; 如果为0,跳过
    add al, '0'      ; 将十位数字转换为字符
    mov [BootMessage + 26], al
    jmp update_done
skip_tens:
    mov byte [BootMessage + 28], ' '  ; 个位数时十位显示空格
update_done:
    inc si           ; 计数器加1
    pop dx
    pop cx
    pop bx
    pop ax
    ret

DispStr:
    push ax 
    push bp 
    push cx
    push bx
    push dx

    mov ax, BootMessage
    mov bp, ax 
    mov cx, 28
    mov ax, 01301h 

    mov bx, 000ch 
    mov dl, 0          ; 列号保持0(最左边)
    ; dh已经在主循环中设置了行号
    int 10h

    pop dx
    pop bx
    pop cx
    pop bp
    pop ax
    ret 

BootMessage: db 'Hello, World! 123, index:   ', 0
times 510 - ($ - $$) db 0
dw 0xaa55
ASM

总结

这次实验主要是基础实验环境的搭建,为后续实验打下基础,同时熟悉bochsqemu的基本使用方法

评论

  1. sishijiu
    Windows Edge
    2 月前
    2025-10-07 18:04:29

    太有说法了

  2. 赞赞赞赞赞赞
    Android Chrome
    2 月前
    2025-10-10 20:50:40

    大佬

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇