|
Writing your own tony os
By
Krishnakumar R
Raghu and Chitkala
Part I
前言
这篇文章是为手动建立一个引导程序所编制的指导.第一部分介绍在开机之后过程的理论.这也能解释我们的计划.第二部分介绍了在开始之前你需要得到的东西,第三则是实际的程序.我们小小的程序并不能真正的引导linux,但是它可以显示器上显示一些东西.
1 背景
1.1 迷人的东西
处理器控制着计算机.在启动的时候,每个处理器都只是一个8086芯片.即使你使用的是最新奔腾,它也只有8086的性能.从这点上看,我们可以把处理器弄到声名狼藉的保护模式下运行一些软件.只有这样我们才能发挥出处理器的全部能力.
1.2 我们的角色
在初始化的时候,控制权在BIOS.这只是一组存放在ROM中的一组程序.BIOS执行开机自我检测.它检查集成在计算机上的部件是否工作(比如键盘是不是连接上了).这是在你开机后听到锋鸣器响的时候.如果所有检测都通过,BIOS选择一个启动设备.它会把设备的第一个扇区的内容拷贝到内存0x7c00的处.然后把控制权转交到这里.启动设备可能是软盘,cdrom,硬盘或者其它你所选择的设备.这里,我们将使用软盘作为启动设备.如果我们在软盘的启动扇区写入了一些代码,它就会执行.我们的角色很清楚:就是写一些程序到软盘的启动扇区.
1.3 计划
首先用8086汇编写一个小程序(别害怕,我会教你怎么写),然后把它写入到软盘的启动扇区中.为了进行拷贝,我们需要写一个C程序.用这个软盘来启动计算机,好好享受吧.
2 你应该有的东西
As86
这是汇编器,用这个工具可以把我们的汇编代码转换为目标文件.
Ld86
这是链接器.as86产生的目标代码用这个工具可以转换为真正的机器语言.机器语言将使用标准的8086格式.
Gcc
C编译器.我们需要一个C程序将我们的系统拷贝到软盘上.
一个空的软盘
这个软盘将用于存储我们的系统.它也是我们的启动设备.
好用的老版本的linux
你应该知道这是作什么的.
As86和ld86可以在大多数和发行版中找到.如果没有,你也可以从”www.cix.co.uk/~mayday”下载.
它们都在一个叫bin86的目录里.在www.linux.org/docs/ldp/howto/assemby-howto/as86.html有很好的文档可以参考.
3 1,2,3 开始!
3.1 引导片断
找个你喜欢的编辑器,然后把下面的代码敲进去.
入口点
- start:
- mov ax,#0xb800
- mov es,ax
- seg es
- mov [0],#0x41
- seg es
- mov [1],#0x1f
- loop1: jmp loop1
复制代码
这是一段as86可以理解的汇编代码.第一段指明了程序的入口点.我们期待在初始化阶段,控制权会转移到这个标签处(别忘了在start后加上”:”).第二行代码描述标签start的地址.这段程序最先被执行的就是标签start之后的那一条.
0xb800是显存地址.#号代表一个立即数.在执行了
之后,ax寄存器会存储0xb800这个值.也就是显存的地址.现在我们将这值转移到es寄存器,es表示扩展寄存器.要知道8086拥有数个段寄存器.比如代码段,数据段,扩展段等等.这也是cs,ds和es的由来.实际上,我们将显示地址放进扩展段后,所有写入扩展段的数据都将进入显存.
为了在显示器上打印出一个字符,需要向显存写入两个字节.第一个就是你要写入字符的ascii码,第二个则是字符的属性.属性指出了字符的前景色,背景色,及是否要闪烁等内容.(seg es is is actually a prefix that tells which instruction is to be executed next with reference to es segment.).所以,我们将字符A的ascii码值0x41挪到显存的第一个字节中.然后我们就需要将字符的属性挪到显存的下一个字节当中去.我们键入0x1f,代表蓝色背景色和白色字符.所以如果我们运行这个程序,就会看见在蓝色的屏幕上有一个白色的字符A.然后是一个循环.在显示了字符之后我们要么停止执行这个程序,要么让它永远循环下去.将这个文件保存为boot.s.
空闲的显存也许不会很干净,这个我会在晚些时候再解释.我假设屏幕有25行,80列.每一行需要160个字节,一半是字符,一半是字符属性.如果我们要在第三列写一些字符,那么我们需要跳过第0和第1个字节,因为它们是第一个列的字符,跳过第2,3个字节,因为它们是第二列的字符,然后在显存的第4个字节上写入ascii码,在第5个字节上写入它的属性.
3.2 将引导程序写入软盘
我们需要一个c程序来将我们的代码(OS 代码)写入软盘的第一个扇区.下面是代码:
- #include <sys/types.h> /* unistd.h needs this */
- #include <unistd.h> /* contains read/write */
- #include <fcntl.h>
-
- int main()
- {
- char boot_buf[512];
- int floppy_desc, file_desc;
-
-
- file_desc = open("./boot", O_RDONLY);
- read(file_desc, boot_buf, 510);
- close(file_desc);
-
- boot_buf[510] = 0x55;
- boot_buf[511] = 0xaa;
-
- floppy_desc = open("/dev/fd0", O_RDWR);
- lseek(floppy_desc, 0, SEEK_CUR);
- write(floppy_desc, boot_buf, 512);
- close(floppy_desc);
- }
复制代码
首先我们以只读属性将文件boot打开,然后将打开文件的文件描述符拷贝到变量file_desc中.读出文件的前510个字节,或者(如果文件没有这么大就)读入所有的内容.因为这个程序非常小,所以会是后一种情况.然后就关闭这个文件.
最后的四行是打开软盘.它使用lseek函数将指针定位到文件的开始处,然后将boot_buf内的512个字节写入软盘.函数read,write,open和lseek的文档(参考man 2)会给出足够的信息让你理解它们的参数是如何使用的.在中间的部分有两行代码,看上去有点神奇:
- boot_buf[510] = 0x55;
- boot_buf[511] = 0xaa;
复制代码
这是给BIOS的信息.如果BIOS认定一个设备是启动设备,那么这个设备应该在它的第510和511个字节处的值为0x55和0xaa.现在我们完成了.这个程序将名为boot的文件读入名为boot_buf的缓冲区.然后它改变了第510和511个字节的值后又将这些内容写入到软盘中.如果我们执行这个程序,软盘的前512个字节就包含了我们的引导程序.将文件保存为write.c.
3.3 咱们动手吧
要想做出可执行的东西来,你要在linux的提示符下面打出下面的命令:
- as86 boot.s –o boot.o
- ld86 –d boot.o –o boot
- cc write.c –o write
复制代码
首先是将汇编文件编译成一个目标文件boot.o.然后再将boot.o链接成为最终的文件boot. Ld86的-d选项是要移除所有的头文件并产生纯净的二进制.读一下as86和ld86的手册就可以解决所有的疑问.然后我们编译C程序,使它生成一个名为write的文件.
插入一张空的软盘,然后输入:
重启你的机器,进入BIOS将软盘设置为启动设备.将软盘插入软驱然后看着计算机从软盘启动.
你会看到一个’A’(白色的前景色,蓝色的背景色).这代表我们制作的软盘已经成功的启动了计算机,我们编制的引导程序已经在执行.它现在正在我们写在引导程序最后的代码中无限循环.我们现在要重新启动计算机,并拿出软盘以便让电脑进入linux.
从这里开始,我们要在我们的引导程序中插入更多的代码,证它做一些更为复杂的事,比如使用BIOS中断,进行保护模式切换等.后面的章节会指引你做更多的改进,继续吧!
注:看到这里,我真庆幸当时不顾别的意见给自己的机器安装了软驱.我知道要做上面的实验即使没有软驱也一样可以,但是会浪费一些时间,呵呵.看来硬件是不是过时,是不是太过于古老,要看你想要它来做什么. |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|