解决方案

自己动手写操作系统(一)

seo靠我 2023-09-23 13:53:21

本系列文章将一步步实现一个简单的操作系统。实验环境是在Linux系统下通过Bochs虚拟机运行我们自己写的操作系统。

一、实验环境搭建

1. Ubuntu的安装,Windows用户可以选择在虚拟机中安装USEO靠我buntu,具体安装教程可自行搜索。

2. Bochs虚拟机的安装

在学习编写操作系统的过程中,我们需要一个虚拟机来模拟出一个虚拟的计算机硬件环境,比如cpu、内存、硬盘等,并且能够运行并且调试我们写的代SEO靠我码。Bochs很好的提供了以上所有功能,在它面前我们就像上帝一样随时可以让时间停止,”钻“到计算机内部,查看这个虚拟电脑的一切信息,这正是开发操作系统所需要的。

Ubuntu下我们可以直接运行以下命令以SEO靠我源码方式安装Bochs(注意我们之所以选择以源码方式安装,是因为通过apt install安装的Bochs是没有调试功能的)

$ sudo apt update $ sudo apt iSEO靠我nstall build-essential libx11-dev xorg-dev libgtk2.0-dev $ wget https://sourceforge.net/projSEO靠我ects/bochs/files/bochs/2.7/bochs-2.7.tar.gz $ tar zxvf bochs-2.7.tar.gz $ cd bochs-2SEO靠我.7/ $ ./configure --enable-debugger --enable-disasm --enable-debugger-gui $ make SEO靠我 $ sudo make install

./configure后面的参数便是打开调试功能的开关

到此实验环境搭建完毕。

二、简单的引导扇区汇编代码

先简单讲一下计算机的启动流程,详细启动过程可参考我SEO靠我的另一篇博客 操作系统启动过程

按下开机键后,计算机首先会运行BIOS中的代码,BIOS在进行硬件检查和初始化后,会按照设置好的启动顺序(我们在使用U盘安装系统时,经常要进入BIOS设置这个启动顺序),SEO靠我依次寻找启动设备(比如硬盘、U盘等)。然后将第一个可用的启动设备的第一个扇区载入内存0x7c00处,并把执行权限交给它。

启动设备的第一个扇区我们称之为引导扇区(MBR),共512个字节,必须以数值0xSEO靠我55及0xaa结尾。包括三部分内容:引导加载程序(Boot Loader)(前446个字节,如GRUB等)、磁盘分区表(DPT,Disk Partition Table)、分区有效性标志(55AA)。SEO靠我其中的引导加载程序负责加载启动硬盘分区中的操作系统。

在BIOS向引导程序移交执行权之前,BIOS会对处理器进行初始化,这其中就包括处理器的代码段寄存器CS和指令指针寄存器IP。当BIOS跳转至引导程序SEO靠我时,CS和IP的值分别为0x0000和0x7c00。此时的处理器处于实模式下,物理地址必须经过CS寄存器和IP寄存器转换才能得到。转换公式为:物理地址=CS<<4+IP,也就是物理地址0x7c00处。SEO靠我

BIOS由Bochs虚拟机提供,我们接下来写的就是这个512字节的引导扇区(MBR)的汇编代码。目前它并不用加载操作系统,我们只让它在屏幕上打印出经典的“hello world”即可。

首先看一下汇编代SEO靠我码:

org 0x07c00mov ax,csmov ds,axmov es,axmov ax,Messagemov bp,axmov cx, 13mov ax,0x1301mov bx,0x0002mSEO靠我ov dh,0mov dl,0int 0x10jmp $ Message: db "Hello, world!"times 510-($-$$) db 0dw 0xaa55

代码和数据是SEO靠我按汇编程序的编写顺序依次连续存放到内存的,即上面的程序在0x7c00处开始存放的是org 0x07c00的机器指令,在512字节最后放的是0xaa55数据。

BIOS程序在把引导程序加载到内存时,同时还SEO靠我创建了中断系统,在物理内存的前1KB空间初始化中断向量表,在物理内存最后256KB物理地址空间内保存中断处理程序。cpu运行完BIOS后,物理内存的布局如下:

本程序就是调用0x10号中断,在屏幕上打印SEO靠我字符串。在调用0x10号中断处理程序往显示器的屏幕上打印字符串时,所有的参数都是通过cpu中的寄存器传递的,各参数的含义如下:

寄存器ah:0x13表示向屏幕打印字符串;寄存器al:指定光标和字符的属性SEO靠我

0,表示字符的属性值保存在寄存器bl中,光标停留在字符串的首字符

1,表示字符的属性值保存在寄存器bl中,光标停留在字符串的尾字符

2,表示字符的属性值紧跟在字符之后,光标停留在字符串的首字符

3,表示字符SEO靠我的属性值紧跟在字符之后,光标停留在字符串的尾字符寄存器bl:若寄存器al的值为0或者1时,保存字符的属性值。如图所示,字符属性由一个字节大小的数据表示。 寄存器[es:bp]:保存SEO靠我字符串的首字符在数据段中的逻辑地址寄存器cx:保存字符串的长度寄存器dh,dl:字符串在屏幕上的起始坐标,其中寄存器dh为行号,寄存器dl为列号。显示器的屏幕只能显示25行字符,并且每行只能显示80个SEO靠我字符,因此dh的取指范围为0~24,dl的取指范围为0~79。

代码解析:

第1行,告诉编译器程序加载到内存的0x7c00处。

第2~4行,统一数据段寄存器DS和附加段寄存器ES的值和代码段寄存器CS一致,SEO靠我即不论数据段还是代码段,段起始地址都是0x7c00。

第5~6行,将字符串"Hello, world!"的首地址传递给寄存器bp。注意任何不被方括号[ ]括起来的标签或变量名都被认为是地址,访问标签或变SEO靠我量中的内容必须使用[ ]

第7行,将字符串的长度传递给寄存器cx

第8~11行,字符串属性设置

第12行,调用0x10号中断处理程序,在屏幕上显示字符串

第13行,使cpu进入死循环

因为cpu会不停的根据寄存SEO靠我器[cs:ip]中的逻辑地址转换后的物理地址,从物理内存读取机器指令,然后对其解析、执行。其中,当运行完一条机器指令后,cpu自动将该机器指令的下一条机器指令的偏移地址赋值给ip。当cpu运行完可执行SEO靠我文件中的最后一个机器指令后,若不采取任何措施,则cpu会将下面的数据看做机器指令,进行取指、解析、执行。因此,需要一条让cpu进入死循环的机器指令作为可执行文件的最后一条机器指令。

$表示当前行被汇编后SEO靠我的地址,$$表示程序被汇编后的开始地址,也就是0x7c00.

第15、17行,分别以字节和字的形式存放的数据

第16行,表示将0这个字节重复510-($-$$)遍,也就是在剩下的空间不停填充0,直到第51SEO靠我0个字节为止。这样加上结束标志0xaa55占用的两个字节,恰好是512个字节。

在运行代码之前我们需要将其转换成计算机能读懂的机器指令形式,这就需要编译器。我们编译c代码使用GCC,编译汇编程序使用naSEO靠我sm编译器。

把上面的代码保存成boot.asm,然后使用nasm编译一下,生成二进制可执行文件boot.bin

$ nasm boot.asm -o boot.bin

三、虚拟硬盘的制作

下面我们将制作一个SEO靠我虚拟硬盘并将已经生成的可执行文件boot.bin放到虚拟硬盘的第一个磁盘块(引导扇区MBR)中。Bochs虚拟机将使用这块“硬盘”引导启动。

首先选择合适的地方创建一个工程目录

$ mkdir projeSEO靠我ctest $ cd projectest

将可执行文件boot.bin拷贝到该工程目录中

然后在本层目录中创建一个大小为1MB的硬盘镜像文件b.img的命令如下:

$ dd if=/devSEO靠我/zero of=b.img bs=512 count=2048

dd是文件拷贝命令,其中:

if=/dev/zero:表示拷贝的源文件的路径,"/dev/zero"是一个特殊的文件,可以提供n个0(n的SEO靠我值等于bs和count参数的积)of=b.img:表示拷贝的目标文件路径。若不存在,则创建该文件bs=512:表示块大小,单位为Bcount=2048:表示拷贝的文件的块的数量。

由bs和count参数SEO靠我可知,硬盘镜像文件的大小为:2048*512B=1MB,硬盘镜像文件制作好后,将可执行文件boot.bin拷贝到硬盘镜像文件b.img(硬盘)的引导扇区的命令如下:

$ dd if=boot.bin oSEO靠我f=b.img bs=512 seek=0 conv=notrunc

其中:

seek=0:表示把可执行文件boot.bin拷贝到硬盘镜像文件b.img的引导扇区(扇区号为0)。conv=notrunc:SEO靠我表示不改变目标文件的大小,若没有该选项,则硬盘镜像文件b.img的大小会由1MB变为可执行文件boot.bin的大小512B。

这样一个写入了引导程序的“硬盘”就制作好了。

四、Bochs的使用

1. 启动SEO靠我Bochs

"硬盘”制作好后,要想启动bochs还需要一个配置文件——bochsrc.bxrc。为什么需要配置文件呢?因为你需要告诉bochs你希望的虚拟机是什么样的,比如,内存多大,使用哪个硬盘启动等SEO靠我等。在下载bochs的源码包中有一个.bochsrc,就是官方提供的配置文件示例,我们可以根据这个更改。

下面是本实验用到的bochs配置文件代码

romimage: file=/usr/local/shSEO靠我are/bochs/BIOS-bochs-latest vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest SEO靠我 ata0-master: type-disk, path="b.img" megs: 16 cpu: count=1 boot: disk

其中SEO靠我

romimage:指定bochs运行过程中使用的ROM-BIOS的路径。vgaromimage:指定bochs运行过程中使用的VGA的ROM-BIOS的路径。ata0-master:指定硬盘镜像文件SEO靠我b.img的路径。megs:指定物理内存的大小,单位为MB。cpu:指定cpu的个数,1个。boot:指定启动方式,从硬盘启动。

将上面的代码保存为bochsrc.bxrc,也存到工程目录下。

现在一切准SEO靠我备就绪,启动bochs的命令如下:

$ bochs -q -f bochsrc.bxrc

其中:

-q: 跳过bochs启动后的配置界面。-f : bochsrc.bxrc:指定配置文件的路径。如果不指定路SEO靠我径,那么Bochs将按照如下顺序在当前目录中寻找配置文件:

.bochsrc

bochsrc

bochsrc.txt

bochsrc.bxrc (windows only)

/home/.bochsrc (UnSEO靠我ix only)

/etc/bochsrc(Unix only)

运行bochs后会在终端出现bochs调试命令行,等待我们输入调试命令,这里输入c继续执行

可以看到在虚拟机中我们的引导程序已成功运行,在屏SEO靠我幕上打印出了hello world。

2. Bochs调试

常用调试命令作用b使用物理地址打断点vb使用逻辑地址打断点blist查看所有断点信息n单步执行(遇到函数跳过)s单步执行(遇到函数进入函数内部)SEO靠我c继续执行r查看所有通用寄存器的值

(eax、ebx、ecx、edx、esp、ebp、esi、edi、eip、eflags)sreg查看所有段寄存器的值u /5打印CPU接下来将执行的5条指令xp查看物SEO靠我理内存中指定物理地址的内容

xp /2bx 物理地址:打印两个字节,以十六进制格式输出。

xp /13c 物理地址:打印13个字节,以ASCII码对应的字符显示watch 变量名运行时,若某一行代码修改了SEO靠我变量,则中断,并打印修改前后的值。q退出调试继续执行
“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2