实现一个最小的操作系统
本实验在Vmware虚拟机的Linux环境下完成。
准备工作:
- 硬件
- VMware下Linux虚拟机:Ubuntu 18.04.5 LTS
- 软件:
- 汇编编译器NASM
- 软盘绝对扇区读写工具:dd命令
VMware的安装以及Linux镜像下载、虚拟机安装均可在网上找到相关教程。
软件安装:
-
nasm:
- 在nasm官网上可以下载最新的压缩包
- 解压后进入目录,分别执行./configure、make、sudo make install
- 执行完毕后,nasm --version查看版本验证nasm是否安装成功
-
bochs:
- 在官网下载压缩包
- 解压到目录/usr/local下,分别执行./configure --enable-debugger --enable-disasm、make、sudo make install
制作操作系统
- 实现操作系统代码
#若有ORG伪指令,编译器则把其后的指令代码放到ORG伪指令指定的偏移地址
org 07c00h #伪指令,这段代码将要被加载到内存偏移地址0x7c00h处
mov ax, cs
mov ds, ax
mov es, ax #使ds和es两个段寄存器指向与cs相同的段,以便以后在进行数据操作的时候能定位到正确的位置
call DispStr #调用子程序DispStr,子程序以ret返回
jmp $ # 这里的$表示当前行被汇编后的地址
DispStr:
mov ax, BootMessage
mov bp, ax # 字符串地址
mov cx, 16 # 字符串长度
mov ax, 01301h
mov bx, 000ch
mov dl, 0
int 10h #int 10h是BIOS提供关于屏幕和显示器操作的程序。前4行用来设置参数。AH(HIGH)=13h表示调用显示字符串的子程序。(DH,DL) = 起始行列、BH = 页号、AL=01h时BL表示显示属性。
ret
BootMessage: db "Hello, OS world!"
# 这里的$$表示一个节(section)的开始处被汇编后的地址。这个程序只有一个节,所以$$表示的是程序被编译后的起始地址,也就是0x7c00。因此$-$$表示的是本行距离程序开始处的相对距离。这句话的意思是将512字节中剩下的部分赋值为0(最后两个字节是结束标志0xAA55)
times 510-($-$$) db 0
# 将最后两个字节赋值为0xaa55,让BIOS把此扇区当作引导扇区,从而将此扇区装入内存0000:7c00处得到执行。
dw 0xaa55
具体逻辑是实现一个引导程序,将引导程序加载到内存0x7c00处运行,接下来的实现由操作系统完成。这段引导程序的大小为512B,最后结尾的标志是0xAA55,少于512B的将用0进行填充。
将源程序利用nasm编译,得到镜像文件boot.bin:
nasm boot.asm -o boot.bin
接着生成虚拟软盘:
$ bximage
========================================================================
bximage
Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
$Id: bximage.cc 12690 2015-03-20 18:01:52Z vruppert $
========================================================================
1. Create new floppy or hard disk image
2. Convert hard disk image to other format (mode)
3. Resize hard disk image
4. Commit 'undoable' redolog to base image
5. Disk image info
0. Quit
Please choose one [0] 1 <<输入1,代表创建一个软盘或硬盘映像
Create image
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd <<输入fd,代表软盘
Choose the size of floppy disk image to create, in megabytes.
Please type 160k, 180k, 320k, 360k, 720k, 1.2M, 1.44M, 1.68M, 1.72M, or 2.88M.
[1.44M] <<回车
What should be the name of the image?
[a.img] <<回车
Creating floppy image 'a.img' with 2880 sectors
The following line should appear in your bochsrc:
floppya: image="a.img", status=inserted
软盘生成后,将引导扇区写入软盘:
# `bs=512`:bs是用来规划一个block的大小,如果未指定则默认是512Bytes(一个扇区的大小) `count=1`:多少个bs的意思 `conv=notrunc`:如果不用它的话,软盘映像文件a.img会被截断(truncated),因为boot.bin比a.img小。
dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
对bochs进行配置
###################################################################
# Configuration file for Bochs
###################################################################
# how much memory the emulated machine will have
megs: 32
# romimage和vgaromimage指定的文件对应的其实就是真实机器的BIOS和VGA BIOS。
# filename of ROM images
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest
# what disk images will be used
floppya: 1_44=a.img, status=inserted
#ata0-master: type=disk, path=diskc.img, mode=flat
# choose the boot disk
boot: floppy
# where do we send log message?
log: bochsout.txt
# disable the mouse
mouse: enabled=0
# enable key mapping, using US layout as default
keyboard: keymap=/usr/local/share/bochs/keymaps/x11-pc-us.map
配置后,打开虚拟机并执行:
$ bochs
========================================================================
Bochs x86 Emulator 2.6.8
Built from SVN snapshot on May 3, 2015
Compiled on May 13 2016 at 04:39:37
========================================================================
00000000000i[ ] BXSHARE not set. using compile time default '/usr/local/share/bochs'
00000000000i[ ] reading configuration from .bochsrc
------------------------------
Bochs Configuration: Main Menu
------------------------------
This is the Bochs Configuration Interface, where you can describe the
machine that you want to simulate. Bochs has already searched for a
configuration file (typically called bochsrc.txt) and loaded it if it
could be found. When you are satisfied with the configuration, go
ahead and start the simulation.
You can also start bochs with the -q option to skip these menus.
1. Restore factory default configuration
2. Read options from...
3. Edit options
4. Save options to...
5. Restore the Bochs state from...
6. Begin simulation
7. Quit now
Please choose one: [6] <<这里选择功能菜单
00000000000i[ ] installing x module as the Bochs GUI
00000000000i[ ] using log file bochsout.txt
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0
<bochs:1> c
运行结果如图所示:
实际上之前的代码实现的并不是一个完整的操作系统,而仅仅是一个最简单的引导扇区(Boot Sector)。然而不管完成的
是什么,至少是直接在裸机上运行的,不依赖于任何其他软件。所以,这与平时所编写的应用软件有本质的区别。它不是操作系统,但已经具备了操作系统的一个特性。
当计算机电源被打开时,它会先进行加电自检(POST),然后寻找启动盘,如果是选择从软盘启动,计算机就会检查软盘的0面0磁道1扇区,如果发现它以0xAA55结束,则BIOS认为它是一个引导扇区。当然,一个正确的引导扇区除了以0xAA55结束之外,还应该包含一段少于512字节的执行码。
一旦BIOS发现了引导扇区,就会将这512字节的内容装载到内存地址0000:7c00处,然后跳转到0000:7c00处将控制权彻底交给这段引导代码。到此为止,计算机不再由BIOS中固有的程序来控制,而变成由操作系统的一部分来控制。