CSDN博客

img uuty

走进程序世界的田园 ——引导扇区释疑

发表于2004/11/17 22:16:00  3271人阅读

分类: 文章

《程序员》04年第7期试读文章“程序世界的田园”系列之一
  编辑部的故事  |  联络  |  聚合   |  登录
  10 篇文章  |   1 篇收藏   |   114 个评论   |   1 个Trackbacks

走进程序世界的田园
——引导扇区释疑


什么?又是酱鸡翅?!你眉头皱起——公司的工作餐怎么就不能出点新的花样?吹着空调还会出汗的大热的天却还要忍受这份油腻,此刻的你显然更需要西芹百合!这就如同你曾深深痴迷的这份工作,其实你是多么怀念在黑色背景上敲出一行命令之后屏幕哗哗卷动的景象,多么自豪于曾经用汇编在屏幕上绘出的彩色的图案,而如今,虚拟机,API等等新鲜玩意让你总感觉仿佛想亲吻女友的手时却亲到了手套,没有亲密感,也许,你不仅仅需要Java、.NET这样的高楼大厦,你同样需要清新的田园,比如你每天都在使用却可能一知半解的——计算机如何引导。
那么,现在就让我们一起走进这田园,看看计算机究竟是如何引导的。

计算机的启动过程

如果你买来新的计算机,硬盘上还是一片空白的时候,你按下power键,仍然可以看到许许多多字符和图案,这显然不是操作系统的一部分,而是BIOS的程序在工作。当然,最后你能看到一行字,提示你插入引导盘。是的,BIOS在寻找一个可供引导的磁盘,找到之后,便会加载盘上的引导模块,并交出控制权,将接力棒传递给操作系统。
我们考虑最简单最易学习的情况,那就是软盘。
如果你将一张非引导磁盘插入软驱的话,BIOS仍然会报错,提示你插入一张系统盘,这说明BIOS并非来者不拒全部试图加载执行的,那么它选择的标准是什么呢?实际上很简单,它会去检查软盘的0面0磁道1扇区(大小为512字节),如果发现它以55AA结束,则BIOS认为它是一个引导扇区,也就是我们说的Boot Sector。当然,一个正确的Boot Sector只有55AA这个结束标志是没有意义的,它还应该包含一段少于512字节的执行码,以便能被放在一个扇区内并正确运行。
一旦BIOS发现了Boot Sector,它就会将这512字节的整个扇区内容装载到内存的0:7c00处,然后跳转到0:7c00处将控制权彻底交给这段引导代码。到此为止,计算机不再有BIOS中固有的程序来控制,而变成由操作系统的一部分来控制。

马上实践——一个最小的引导扇区

准备工作:
硬件
一台计算机(Windows操作系统)
一张空白软盘
软件
汇编编译器NASM。最新版本可以在此链接处获得:http://sourceforge.net/projects/nasm。(此刻你可能会有疑问,为什么是NASM,而不是MASM或者TASM,对于这一点我稍候再来解释)
软盘绝对扇区读写工具。其实你完全可以自己写一个,用CreateFile和WriteFile这两个API就搞定了,非常容易。我就是这样做的,省去了在网上寻找工具的时间。
最好有一个虚拟机比如VirtualPC,可以在试验的时候不必重启自己的计算机。


代码
我们来看这一段代码:
org 07c00h  ;告诉编译器程序加载到7c00处
jmp $  ;无限循环
times 510-($-$$) db 0 ;填充剩下的空间,使生成的二进制代      码恰好为512字节
dw  0aa55h   ;引导扇区需要以55AA结束

在对程序进行解释之前,为了尽快看到效果有初步的感性认识,请先随我来做以下操作:
首先用NASM编译一下:

nasm boot.asm -o boot.bin


我们就得到了一个512字节大小的boot.bin,使用软盘绝对扇区读写工具将这个文件写到一张空白软盘的第一个扇区,好了,这张软盘已经是一个引导盘了。
然后把它放到你的软驱中重新启动计算机,或者使用VirtualPC模拟启动过程,从软盘引导,你看到了什么?
答案是什么令人惊喜的结果也没有出现,这倒容易理解,因为我们的程序第一个语句就是一个死循环。我们除了让程序停滞在那里,其余什么也没做。
这显然并不令人满意,我们得看到些效果才行,让我们将代码稍作修改:

org 07c00h   ; 告诉编译器程序加载到7c00处
mov ax, 0b800h
mov es, ax   ; 设置 es 以便直接写显存
mov byte [es:0], 'a' ; 在显存第一个字节写入字符‘a’
mov byte [es:1], 0ch ; 在显存第二个字节写入十六        进制值C,表示黑底红字
jmp $   ; 无限循环
times 510-($-$$) db 0 ; 填充剩下的空间,使生成的        二进制代码恰好为512字节
dw  0aa55h    ; 引导扇区需要以55AA结束

我们在程序无限循环之前插入了四行,目的是让我们的引导程序能显示一个红色的字符‘a’。插入的这四行比较易懂,效果是在B800:0000处写入了两个字节:'a'和0Ch。我们知道,B800:0000恰好是显存的首地址。
同样的方法编译,写入磁盘并重启,你看到了什么?
你看到红色的字符a了!
多么令人激动啊,这表明我们的程序正确运行了,进一步,这说明我们自己编写的引导扇区试验成功了!
如果你用的是VirtualPC,出现的应该是这样的景象(局部):

这简直是太妙了,因为有了这样的开头,就意味着你可以在此基础上做出任意的扩展,甚至写出自己的操作系统!这一步迈出来的确并不难,但却具有历史意义!

为什么是NASM
你可能感到很奇怪,为什么居然有人用NASM这样东西,而不是你从前使用的MASM或者TASM,实际上这有点涉及到个人喜好,但是事实是,我从开始无意中接触到NASM开始,就决定从此彻底抛弃MASM了。因为它具备以下几个主要特点:
1、代码清晰,避免了MASM中容易混淆的语法。
这项特点在NASM多个细节都有体现,这里我仅举两例。
第一,在NASM中,任何不被方括号[]括起来的标签或变量名都将被认为是地址,访问标签中的内容必须使用[]。所以,mov ax, Message将会把Message对应字符串的首地址传给寄存器ax。又比如:
如果有:foo dw 1则mov ax, foo将把foo的地址传给ax,而mov bx, [foo]将把bx的值赋成为1。
实际上,在NASM中,变量和标签是一样的,也就是说,

foo dw 1 ≡ foo: dw 1

而且你会发现,offset这个东东在NASM也是不需要的。因为不加方括号时表示的就是offset。
我个人认为这是NASM的一大优点,要地址就不加方括号,也不必额外的offset,想要访问地址中的内容就必须加上方括号,代码规则非常鲜明,一目了然。
第二,既然所有标签都是地址,使得NASM具有另外一个特点,就是不记忆变量类型,所以在给变量赋值的时候,必须加上赋值的类型,比如:

mov byte [var1], 'a'

2、 可以在不同平台中使用
如果你想学习一次就可以在不同平台下使用的话,NASM几乎是唯一的选择。如果你想进行完全的代码移植,NASM是完美的工具。因为不管在Dos,Windows还是Linux,NASM都是可用的,而且用法完全相同。
3、 免费
可能这项特性已经不足以吸引你的眼球,但的确是它的一个可爱的特性。
本文不是专门的NASM介绍文章,但是我认为它的确是一个值得推荐的工具,尤其是,如果你不想仅仅了解引导扇区的写法,而是在此基础上深究下去,进行操作系统的研究,我保证你会越来越体会到NASM这一工具的优点。

关键代码解释

上面两段代码的注释已经写得比较清晰,在这里对几个问题着重强调一下。
1、org的使用
org的作用是告诉编译器,这个程序将来被加载到内存的哪个位置。我们在稍后的例子中会使用到常量,编译器就是以org指定的这个地址为基准来确定常量的地址。
2、关于$和$$
$表示当前行被汇编后的地址。这好像不太好理解,不要紧,我们把刚刚生成的二进制代码文件反汇编来看看:

ndisasmw -o 0x7c00 boot.bin >> a.asm

打开a.asm,你会发现这样一行:

00007C09  EBFE              jmp short 0x7c09

$在这里的意思原来就是0x7c09(在加载到内存之后)。
那么$$表示什么?它表示一个节(section)的开始处被汇编后的地址。在这里,我们的程序只有一个节,所以$$实际上就表示程序被编译后的开始地址,也就是0x7c00。
在写程序的过程中,“$-$$”可能会被经常用到,它表示本行距离程序开始处的相对距离。现在,你应该明白510-($-$$)表示什么意思了吧?times  510-($-$$) db 0表示将0这个字节重复510-($-$$)遍,也即在剩下的空间中不停地填充0,直到程序有510字节为止,这样,加上结束标志55AA占用的两个字节,恰好是512个字节。
3、 55AA还是AA55
初学者经常被这个问题搞得非常头痛,总也搞不清楚到底谁在前谁在后,其实归根到底还是没把本质弄明白。
IBMPC的原则是“高位在高字节”。举个例子,如果有一个DWORD类型的数0x12345678放在内存中,看起来会是这样:

L ———> H
78 56 34 12

因为78处在数字的低位,于是也会被放到内存的低位。
这里有一点需要思考一下,就是计算机只知道数字,不知道类型,所以,从内存的某个地址取出一个数,必须在指明类型的情况下才是有意义的,比如已知有这样的内存映像:

L ———> H
78 56 34 12

若想取出一个BYTE,你会得到0x78;若想取出一个WORD,你会得到0x5678;若想取出一个DWORD,你会得到0x12345678。
回头看看我们的代码:

dw  0aa55h

我们指定把0aa55h这个WORD类型数字放在引导扇区最末端,aa处在数字的高位,会被放到内存的高位,于是它在内存中的映像应该是:

L———>H
55 aa

很简单,也很明了不是吗?

再作扩充——一个变一行

只显示一个字符显然是不够的,我们想要更进一步的成就感,比如显示一个字符串。可是如果显示每一个字符都要两行代码来实现的话,难免显得笨拙而低效。是的,你一定想到了,我们可以使用BIOS中断。
请看代码:

org 07c00h   ; 程序会被加载到7c00处,所以需要这一句
 mov ax, cs
 mov ds, ax
 mov es, ax
 Call DispStr   ; 调用显示字符串例程
 jmp $   ; 无限循环
DispStr:
 mov ax, BootMessage
 mov bp, ax   ; ES:BP = 串地址
 mov cx, 16   ; CX = 串长度
 mov ax, 01301h  ; AH = 13,  AL = 01h
 mov bx, 000ch  ; 页号为0(BH = 0) 黑底
         红字(BL = 0Ch,高亮)
 mov dl, 0
 int 10h   ; int 10h
 ret
BootMessage:   db    " Hello, OS world!"
 times 510-($-$$) db   0 ;填充剩下的空间,使生成的二
      进制代码恰好为512字节
 dw  0aa55h   ; 引导扇区需要以55AA结束

这段代码看上去长了许多,但实际上主体框架只有5行(从第2行到第6行),其中调用了一个显示字符串的子程序。程序的第2、3、4行是三个mov指令,使ds和es两个段寄存器指向与cs相同的段,以便在以后进行数据操作的时候能定位到正确的位置。第5行调用子程序显示字符串,然后jmp $让程序无限循环下去。
我们来试验一下,编译,写入磁盘,启动:

成功!
来来来,下面泡一杯咖啡,然后靠在椅背上静静欣赏一下自己的成果吧,让你的屏幕暂时停在这一刻。这是一件多么有趣的作品!虽然我们的代码很短,却已经涉及到了如此多的技术细节,我们甚至使用了BIOS中断,在中断例程的帮助下,我们几乎是无所不能的,想象一下吧,最振奋人心的一点是,你可以进行磁盘操作,将更多的程序加载到内存中并且执行,这意味着你真的已经可以在这个小东西的基础上一点点扩充,甚至建造操作系统的大厦!

之所以这是田园,因为这里离泥土最近

你可能很久都没有过如此透彻地了解一件事,就好像你又看到泥土中生长出绿色的植物。这是一种回归自然的感觉,那么,就请尽情享受这一刻爽快的感受吧,忘掉Java,.NET,还有那讨厌的酱鸡翅。

发表于 2004年07月27日 4:49 PM

评论

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 11:20 AM 野比
不知道那个绝对扇区读写程序哪里有下?
希望提供连接~
文章不错,我很喜欢。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 12:20 PM 微软小子
晕~程序员杂志上的文章。。白买了。。。汗。。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 12:32 PM t608
晕~程序员杂志上的文章。。白买了。。。汗。。


# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 12:55 PM garbage
白买了,,乱搞~

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 5:16 PM 77
靠!不就一篇,上面几位叫什么叫

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 7:05 PM 野比
嘎嘎,多放几篇~
等我回去写一个那个程序的……

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-28 10:46 PM 果蜾
呵呵,不懂的东西叫知识。好文章

# 可操作性的文章最受欢迎 2004-07-29 8:17 AM Andy Pan
这是我近期最喜欢的一篇技术文章,也是我认为最适合《程序员》定位的文章,我个人觉得《程序员》有一些技术文章过于抽象,集中在很多模式以及开发理论上,虽然编程技巧的文章有“雕虫小技”之嫌,但是此类可操作性强的文章很适合《程序员》杂志80%的读者。深入浅出,易于学习,对杂志读者很重要。拥有这些的作者也体现了《程序员》杂志是中国开发类杂志的第一品牌。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 8:45 AM java
看了这篇文章,这期的程序员,咱是不会买了。怎么办得象电脑报这样的入门刊物

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 9:16 AM ft
to java: 看了这篇文章,这期的程序员,咱是不会买了。怎么办得象电脑报这样的入门刊物
这位仁兄看来是高手!入门刊物的东西不知道你懂得透彻不透彻?
不过买不买倒是你的权利。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 9:39 AM bob
无所谓的文章一篇,看看也就是了,何必要大鼓地说呢,搞的大家真的以为是什么“妙文”,《程序员》也太没有眼光了,这样的文章最多是瞄一眼罢了,何必要说什么“妙文”。太幽默了。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 9:50 AM notyy
好文

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 10:17 AM 吃人
說實在的 這個實在是沒有技術含量.....
會點匯編的了解boot block架構的大概都可以寫出來這樣的東西..
這個是比較低層的東西了.....
太簡單...應該再深再深...... 再擴展再擴展...
而不是簡單的一個低層應用

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 10:52 AM Hehe
这篇文章的惟一用处就是让我 想起十几年前的事情

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 1:00 PM 态度决定一切
每个程序员都有自己的要求,有人java .net 有人 asm,这都不是重要的,写好自己的东东才是重要的。

# 各取所需而已 2004-07-29 4:36 PM jacky
对于boot我还是非常不了解,所以这篇文章给我增长见识,感谢作者。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 5:30 PM ascender
推荐文章应该更有档次才行吧?

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 5:49 PM 小贵子
以上功能能否用C语言实现?

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-29 6:14 PM kwest
晕,就那么一点启动代码....IBM兼容机..呵呵....

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-30 1:04 AM icelover
我觉得这篇文章讲的非常好,比起那些抽象的冬冬更能吸引人

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-30 8:55 AM 春困
好文,我喜欢

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-30 9:23 AM captain
是的,非常怀念产DOS时代,一个一切由自已说了算的时代。。。
但是自由是用代价换来的。如果从底层开发,需要更长的周期,更多的测试,当然,更多的Bug是不可避免的了:)
所以,DOS适合有兴趣的人自已玩。。。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-31 11:55 AM ylhyh
我觉得是不错,我喜欢DOS
不喜欢只写代码而不关心系统的程序员

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-07-31 8:11 PM welman
城市的喧嚣不适合田园的牧歌,权当消遣吧..........

但确实不错,是吧.

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-01 10:36 PM FOCL
我就觉得很好,楼上的 “无所谓的文章一篇,看看也就是了,何必要大鼓地说呢,搞的大家真的以为是什么“妙文”,《程序员》也太没有眼光了,这样的文章最多是瞄一眼罢了,何必要说什么“妙文”。太幽默了。 ” ,你的观点是不是太偏激了?!

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-01 10:39 PM JACKY2
好喜欢这种文章阿!
一个好的程序员不会说这种文章不好吧,就算自己懂了,也可以惠于后来人吧!
超喜欢!!

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-04 12:40 PM 于渊

首先谢谢大家对这篇文章的关注,也感谢《程序员》和CSDN提供了这样一个机会让大家可以一起讨论。

在七月号的杂志中这篇文章的下面可以看到一篇《“程序世界的田园”系列文章序》,里面表达了一些我对操作系统的看法,同时也告诉大家,其实这篇文章仅仅是一个系列的第一篇,它以简单的内容和轻松的口吻开始,目的是引起大家对操作系统技术的一点兴趣和关注,在今后的几篇文章中,我会涉及一些稍稍深入点的内容,当然,读者中肯定有非常多的人对这些问题的研究要深入和广泛的多,也请大家能不吝赐教,多提意见和建议。

八月号的杂志已经可以买到了吧,在这一期中刊登了系列文章的第二篇,内容是关于存储管理的,请大家在阅读文章之后能继续给予一些指点。我的EMail是:forrestyu@126.com。对于你的来信我会非常感谢。


# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-04 12:52 PM webspider
绝知此事要躬行!
文章虽然简单,但还没有碰到有人用如此欢快节奏的小品文方式把它写出来。程序世界当中本来就没什么难题,只要大家一点一点的去做,注重积累。国内,只是有太多的人眼睛总是盯最流行的东西,从VC到Java,再到后来的.net,什么东西都浅尝辄止,其不知在流沙上面是建不了高楼的。这也是国外那些最牛的人都是汇编年代出来的人,对于他们来说细节上没有难度(对很多玩时髦的人来说,细节是地狱),只要他们有想法,他们就能实现。
在炎热的、浮躁的夏季,我喜欢这种清凉的短文,也需它对我当前的Java项目没有很直接的帮助,但它该表了我的一些思想,影响是长远的。
在此感谢作者!

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-04 1:06 PM webspider
有些人埋怨简单!
是的,现在有现成的Linux内核代码,
有几个人不被那如山的代码吓倒?在面对如此多的代码,除了感叹作者的牛之外,留给我们的更多的是挫折感,还留下了什么?莫非还有些感叹吧。这就是当时我的感受。
作者可谓用心良苦!
用这种简单的方式从浩如烟海的操作系统中精选出这些简单的东西,让我感受操作系统的美妙,而又不被吓爬在地上。

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-04 3:03 PM nkscorpion
我近期正对计算机的“启动->OS”好奇,这篇文章给了一个很好的指导。在此,感谢作者为我们奉献了该文;也感谢CSDN给我一个看到该文的机会!继续关注...

# 回复:2004年7月《程序员》试读文章“程序世界的田园”系列之一 2004-08-05 9:03 AM eming
谢谢作者,俺长见识了!

# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-08-08 12:50 AM flair
写软盘
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char boot_buf[512];
int floppyd, fd;
fd = open("./bootfile", O_RDONLY);
read(fd, boot_buf, 510);
close(fd);
boot_buf[510] = 0x55;
boot_buf[511] = 0xaa;
floppyd = open("/dev/fd0", O_RDWR);
lseek(floppyd, 0, SEEK_CUR);
write(floppyd, boot_buf, 512);
close(floppyd);
}


# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-08-08 3:34 PM 于渊
我写软盘用的程序已经放在这个页面上:http://forrestyu.vicp.net/Download.htm,感兴趣的可以下载。
这个主页并不是24小时有效的,中午12点到凌晨0点可访问的概率最大。

# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-08-16 9:30 PM apemancsdn
关于写磁盘的绝对扇区的方法, 请大家参看我的blog:
http://blog.csdn.net/apemancsdn
这篇文章上的代码我全部实现了, 如果需要代码的朋友可以给我发mail: tigger_211@sina.com

# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-08-18 11:52 AM ycl
学习 ing

# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-08-23 4:31 PM guest
大凡能让大多数人增长见识的
就是好文章!

# 回复:《程序员》04年第7期试读文章“程序世界的田园”系列之一 2004-11-04 12:12 AM yanqlv
我试过了,这么着就可以把那个512字节文件写到软盘引导区上:
d:/>debug boot.bin
-w 100 0 0 1
-q



发表评论

标题:
大名:
网址:
评论 
   
阅读全文
0 0

相关文章推荐

img
取 消
img