|| 版权声明:本文为博主原创文章,未经博主允许不得转载。
一、前言
今天起开始分享关于操作系统的相关知识,本人也是菜鸟一个,正处于学习阶段,这整个操作系统篇也是我边学习边总结的一些结果,希望能给正在学习或者有意向学习操作系统的童鞋带来帮助。
二、有关知识
在进入代码之前,先给大家普及一些硬件知识,如果你已经具备了这方面的知识,可以直接略过这部份。
1.计算机怎么启动操作系统的?
首先,我们思考一个问题,为什么一个硬盘安装系统之后打开计算机电源之后就能正常加载启动呢?这看起来似乎很智能,似乎计算机像活的一样会自动去硬盘中找系统代码并自行加载。其实不然,在计算机的世界里一切也都是离不开规则的。而基于这些硬件所定的规则或者协议,我们将启动代码放到协议规定的地方,这时候启动就会加载这部分代码。听起来好像有点乱,我们继续看。
2.主引导扇区
很多安装过系统的童鞋都应该知道引导盘这个东西,就是在我们启动电脑的时候可以通过bios来设定是通过U盘启动还是通过硬盘启动。对于硬盘来说,硬盘的第一个扇区是 0 面 0 道 1 扇区,或者说是 0 头 0 柱 1 扇区,这个扇区称为主引导扇区。如果计算机的设置是从硬盘启动,那么, ROM-BIOS 将读取硬盘主引导扇区的内容,将它加载到内存地址 0x0000:0x7c00 处(也就是物理地址 0x07C00),然后 jmp 跳到那里接着执行:
为什么偏偏是 0x7c00 这个地方?还不太清楚。反正当初定下这个方案的家伙已经被人说了很多坏话!!
通常,主引导扇区的功能是继续从硬盘的其他部分读取更多的内容加以执行。像 Windows 这样的操作系统,就是采用这种接力的方法一步一步把自己运行起来的。
说到这里,我们可以想象,如果我们把自己编译好的程序写到主引导扇区,不也能够让处理器执行吗?
其实这就是我们寻找的答案,这几乎是在不依赖操作系统的情况下,让我们的程序可以执行的唯一方法。但这里要注意可别拿自己的电脑乱试,系统一下就瘫痪了,我们可以通过虚拟机来进行我们的代码运行与调试。
三、配置
1.nasm
首先安装nasm,方便编译我们的代码,为了大家方便,本文后面会附上相关的下载地址。
安装过程是直接安装,一直next:
安装完成之后,我们可以启动nasm的shell,通过该shell进行相关编译工作。
2.bochs
该软件可用于创建硬盘或软盘,同时也可模拟计算机启动,可以加载我们写好的启动盘。
点击I Agree进入下面的界面:
这个界面,大家看自己的需求选择,然后点击next:
这里DLX Linux Demo是默认没选上的,安装Bochs时不妨选中“DLX Linux Demo”,便可直接参考它的配置文件。
继续next:
选择好安装位置,install就行了。
3.dd
该工具用于将我们编译后的机器码写入到启动扇区中。我这里用的是dd-0.6beta3,将附件下载后解压,拿到里面的exe文件,在cmd下cd到该目录就可以使用dd命令进行写入操作。
四、程序
说了这么多,还没见到代码,下面我们就一段代码了实现字符串的显示,由于代码是用汇编写的,所以没有汇编基础的童鞋可能需要先看看汇编的语法。
废话不多说了,直接上代码:
1 org 07c00h ;告诉计算机将代码加载到内存的07c00h
2 mov ax, cs
3 mov ds, ax ;初始化数据段ds
4 mov es, ax ;初始化附加段寄存器
5 call DispStr ;调用DispStr来显示字符串
6 jmp $ ;无限循环
7 DispStr:
8 mov ax, BootMessage ;将字符串地址传给寄存器ax
9 mov bp, ax ; 通过ES:BP来指向显示的字符串
10 mov cx, 16 ; 表示字符串的长度
11 mov ax, 01301h ;10h的13号中断,此时通过AH=13传入,AL=1,表示目标字符串仅仅包含字符,属性在BL中包含,移动光标
12 mov bx, 000ch ;BH表示视频区页数
13 mov dl, 0 ;DL表示在第几列显示(0为第一列)
14 int 10h ;10H中断
15 ret
16 BootMessage: db "Hello,OS World!"
17 times 510-($-$$) db 0 ;用times来创建字节0
18 dw 0xaa55 ;扇区结尾,写入引导程序标志位
上面的代码注释比较清晰的解释了程序的意思,这里我们在详细分析一下。
在前面我们讲到因为硬件设计的原因,在充电的时候,回去内存的07c00h处读取指令,因此,我们通过org 0700ch来讲程序加载到该地址。接着初始化了段寄存器,然后调用字符串的显示的代码部分从而将字符串显示在显示器上面。
在DispStr中,相关寄存器的操作都是为了后面的10H中断做准备,int 10h这条指令的触发将会使得ES:BP所指向的字符串进行显示。
显示完成后,原则上整个程序就结束了,但对处理器来说,它并不知道。对它来说,取指令、执行是永无止境的。程序有大小,执行无停息,它这么做的结果,就是会执行到后面非指令的数据上,然后……
问题在于我们现在的确无事可做。为避免发生问题,源程序第 6 行,安排了一个无限循环:jmp $。其中$等同于标号,你可以把它看成是一个隐藏在当前行行首的标号。
说到这里就只剩下最后两行代码了,其实这里也是可以算是一个协议来的。因为主引导扇区在系统启动过程中扮演着承上启下的角色,但并非是唯一的选择。如果硬盘的主引导扇区不可用,系统还有其他选择,比如可以从光盘和 U 盘启动。
然而,如果不试试水的深浅就一个猛子扎下池塘,这并非一个明智之举。同样地,如果主引导扇区是无效的,上面并非是一些处理器可以识别的指令,而处理器又不加鉴别地执行了它, 其结果是陷入宕机状态,更不要提从其他设备启动了。
为此,计算机的设计者们决定,一个有效的主引导扇区,其最后两个字节的数据必须是 0x55和 0xAA。否则,这个扇区里保存的就不是一些有意而为的数据。这就是最后一行代码的解释:dw 0xaa55。
根据上面的说法,这里还要解决另外的一个问题,就是如何使这两个字节正好位于 512 字节的最后。前面的代码有多少个字节我们不知道,那是由 NASM 编译器计算和跟踪的。这时倒数第二行代码就起作用了,这里通过创建数值为0的字节来补充余下的空间,其中$$是 NASM编译器提供的另一个标记,代表当前汇编节(段)的起始汇编地址。
到此,我们对于代码的分析完毕了。
五、编译与运行
这时候打开我们前面安装好的nasm shell,cd定位到我们的boot.asm的位置,然后执行nasm boot.asm -o boot.bin
然后,我们通过bochs来创建一个软盘,通过软盘来做启动盘从而启动执行我们的程序。
首先,打开bochs安装目录的bximage.exe文件
下面是打开后的界面:
然后输入1并回车,选择创建一个软盘或硬盘,进入下面的界面:
这时候会询问要创建的类型是hd还是fd,我们输入fd回车,进入下面的界面:
这个提示是要我们选择要创建的盘符的大小,我们直接回车选择默认的1.44M就行了:
到这里可以为创建的镜像输入名称,也可以用a.img作为默认名。回车后,这时会在bochs安装的目录下面多了一个a.img的文件,这个就是我们刚刚创建的镜像文件:
到此,我们创建好了软盘,下面就是将程序写入到软盘的第一个扇区,这时,我们dd工具就派上用场了,我们启动命令行,cd到dd的根目录,然后执行写入的命令:
dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
注意这里我把编译好的boot.bin文件与镜像a.img跟dd放在同一目录,不是同一目录的同学请自行修改路径。
执行完后,a.img就是我们要的启动盘了。
六、运行
运行的时候是肯定不能在真机上面运行的,这时就要用到虚拟机了,我用的是vm ware。
首先,我们需要创建一个新的虚拟机,主要的步骤如下:
选择自定义,单击下一步进入下一个界面:
这里默认就可以了,直接单击下一步:
这里选择稍后安装操作系统,下一步:
这里选择其他,然后下一步:
这里是处理器的配置,因为我们的程序都很简单,而不是玩游戏,所以选择最低配置就可以了:
选择好内存,单击下一步:
关于网络就随意选都行,反正我们暂时不会涉及网络方面的东西,单击下一步:
IO控制器,直接默认就行,下一步:
虚拟磁盘类型,选择IDE,单击下一步:
创建磁盘,单击下一步:
这里尽量选小一点的磁盘,因为我们用不上的,我们这里是通过软盘来启动的:
选择磁盘文件的位置,然后单击下一步:
单击完成,此时我们的虚拟机就创建好了。
这个时候不要急于去启动它,因为我们还有将我们刚刚做好的启动盘配置进来,作为虚拟机的启动盘。下面我们继续通过截图来说明,这样会直观一些:
这里我们选择图中红色圈住的地方,来我们刚刚创建好的虚拟机:
选择添加,来添加我们刚刚制作好的软盘:
选择软盘驱动器,单击下一步:
选择使用软盘镜像,单击下一步:
选择我们刚刚的a.img,并勾选启动时连接,单击完成。
这时候我们的准备工作就结束了,可以启动虚拟机来看看我们的工作成果了:
可以看到我们字符串Hello,OS World!显示在上面。
这个时候,心细的同学可能有疑问了,为什么显示的字符串是红色的,为什么不是白色或者其他颜色呢?要显示其他颜色也是可以的,这其实是我们写在了程序里面的,这其实是有AL与BL寄存器来决定的。
如果AL的BIT1为1,则BL表示显示属性。属性为:
|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0| BL
BIT7:背景是否闪烁。0不闪烁,1闪烁
BIT6~BIT4为背景色,分别为RGB,000为黑色,111为白色
BIT3为1,则前景色加亮,为0则不加亮
BIT2-BIT0为前景色,意义同背景色
有兴趣的同学可以尝试修改代码运行试试,这里就不一一列举 了。
七、总结
回顾上面所讲的内容,我们主要讲解了以下几点的内容:
1.如何免系统运行程序:由于硬件设计的原因,在计算机充电启动的时候会去内存的0x07c00h处读取指令,因此我们通过指令org 0x07c00h将代码加载到内存的该位置。
2.计算机如何知道要我们制作的软盘就是启动盘呢?因为在软盘的第一扇区最后的两个字节是计算机识别启动盘的位置,我们通过将这两个字节设置成0xaa55,从而告诉计算机,该软盘就是启动盘。
3.如何通过代码将字符串显示到显示器上?我们利用10H中断,该中断会去ES:BP所指向的内存地址读取内容并显示。
4.显示是样式如何设定?将AX的低八位AL的BIT1设置为1,表示要显示的是字符串,而且样式包含在BL中,而在BL中我们设置的值为0x0c,也就是二进制的0000_1100,从上面 对BL的各个位的讲解,我们知道该内容是将字符串设置为红色。
5.同时,我们也学会了利用bochs来制作镜像,另外bochs也可以用来调试,以后的文章也会用到,希望大家多多关注。
6.通过dd工具将程序写入镜像。
下面是本文中用到的相关工具的下载地址