4 无知则无畏 …… 5
题也是有益的,但一上来就问这些,当然会让人无从回答。
要想给他们一个满意答复,让他们不再从旁指手画脚的话,还真得多学习,拿出点像模像 样的见解才行。但我们是初学者,没有必要去学那些麻烦的东西,费时费力且不说,当我们知 道现有操作系统在各方面都考虑得如此周密的时候,就会发现自己的想法太过简单而备受打击 没了干劲。如果被前人的成果吓倒,只用这些现有的技术来做些拼拼凑凑的工作,岂不是太没意 思了。
0 3 20 21 22 7
所以我们这次不去学习那些复杂的东西,直接着手开发。就算知道一大堆专业术语、专业理 论,又有什么意思呢?还不如动手去做,就算做出来的东西再简单,起码也是自己的成果。而且 自己先实际操作一次,通过实践找到其中的问题,再来看看是不是已经有了这些问题的解决方案, 这样下来更能深刻地理解那些复杂理论。不管怎么说,反正目前我们也无法回答那些五花八门的 问题,倒不如直接告诉在一旁指手画脚的人们:我们就是想用自己的方法做自己喜欢的事情,如 果要讨论高深的问题,就另请高明吧。
■■■■■
8 9 10 11
其实反过来看,什么都不知道有时倒是好事。正是因为什么都不知道,我们才可能会认真地 去做那些专家们嗤之以鼻的没意义的―傻事‖。也许我们大多时候做的都没什么意义,但有时也 可能会发掘出专家们千虑一失的问题呢。专家们在很多方面往往会先入为主,甚至根本不去尝试 就断定这也不行那也不行,要么就浅尝辄止。因此能够挑战这些问题的,就只有我们这种什么都 不知道的门外汉。任何人都能通过学习成为专家,但是一旦成为专家,就再也找不回门外汉的挑 战精神了。所以从零开始,在没有各种条条框框限制的情况下,能做到什么程度就做到什么程度, 碰壁以后再回头来学习相关知识,也为时未晚。
实际上笔者也正是这样一路磕磕绊绊地走过来,才有了今天。笔者没去过教授编程的学校, 也几乎没学什么复杂的理论就开始开发操作系统了。但也正是因为这样,笔者做出的操作系统与 其他的操作系统大不相同,非常有个性,所以得到了专家们的一致好评,而且现在还能有机会写 这本书,向初学者介绍经验。总地说来,笔者从着手开发直到现在,每天都是乐在其中的。 正是像笔者这样自己摸着石头过河,一路磕磕绊绊走过来的人,讲出的东西才简单易懂。不 过在讲解过程中会涉及失败的经验,以及如何重新修正最终取得成功,所以已经懂了的人看着可 能会着急。不好意思,如果碰到这种情况请忍耐一下吧。
读了这部分内容或许有人会觉得―是不是什么都不学习才是最好的啊‖,其实那倒不是。比
如工作上需要编写某些程序,或者一年之内要完成某些任务,这时没有时间去故意绕远路,所以 为了避免不必要的失败,当然是先学习再着手开发比较好。但这次我们是因为自己的兴趣而学习 操作系统的开发的,既然是兴趣,那就是按自己喜欢的方式慢慢来,这样就挺好的。
12 13 14 15 15 16
6 …… 第 0 天:着手开发之前
55
如何开发操作系统
操作系统(OS)一般打开电源开关就会自动执行。这是怎么实现的呢?一般在Windows上开
发的可执行文件(~.exe),都要在操作系统启动以后,双击一下才能运行。我们这次想要做的可 不是这种可执行程序,而是希望能够做到把含有操作系统的CD-ROM或软盘插入电脑,或者将操 作系统装入硬盘后,只要打开电源开关就能自动运行。
为了开发这样的操作系统,我们准备按照如下的步骤来进行。①②
也就是说,所谓开发操作系统,就是想办法制作一张―含有操作系统的,能够自动启动的 磁盘‖。
这里出现的―映像文件‖一词,简单地说就是软盘的备份数据。我们想要把特定的内容写入
磁盘可不是拿块磁铁来在磁盘上晃晃就可以的。所以我们要先做出备份数据,然后将这些备份数 据写入磁盘,这样才能做出符合我们要求的磁盘。
软盘的总容量是1440KB,所以作为备份数据的映像文件也恰好是1440KB。一旦我们掌握了 制作磁盘映像的方法,就可以按自己的想法制作任意内容的磁盘了。
这里希望大家注意的是,开发操作系统时需要利用Windows等其他的操作系统。这是因为 我们要使用文本编辑器或者C编译器,就必须使用操作系统。既然是这样,那么世界上第一个 操作系统又是怎么做出来的呢?在开发世界上第一个操作系统时,当然还没有任何现成的操作 系统可供利用,因此那时候人们不得不对照着CPU的命令代码表,自己将0和1排列起来,然后 再把这些数据写入磁盘(估计那个时候还没有磁盘,用的是其他存储设备)。这是一项非常艰 巨的工作。所以恐怕最初的操作系统功能非常有限,做好之后人们再利用它来开发一个稍微像点 样的操作系统,然后再用这个来开发更实用的操作系统……操作系统应该就是这样一步一步发展 过来的。
——————————
① source program,为了生成机器码所写的程序代码。可通过编译器编译成机器语言。 ② CPU能够直接理解的语言,由二进制的0和1构成。其实源代码也是由 0和1构成的(后述)。
6 操作系统开发中的困难 …… 7
由于这次大部分初学者都是Windows用户,所以决定使用Windows这个现成的操作系统, Windows95/98/Me/2000/XP中任意一个版本都可以。肯定也有人会说还是Linux好用,所以笔者也 总结了一下Linux上的做法,具体内容写在了帮助与支持①里,有需要的人请一定看一看。 另外,如果C编译器和映像文件制作工具等不一样的话,开发过程中就会产生一些细微的差 别,这很难一一解释,所以笔者就直接把所有的工具都放到附带光盘里了。这些几乎都是笔者所 发布的免费软件,它们大都是笔者为了开发后面的OSASK操作系统而根据需要自己编写的。这 些工具的源代码也是公开的。除此之外,我们还会用到其他一些免费软件,所有这些软件的功能 我们会在使用的时候详细介绍。
0 3 20 21
66
操作系统开发中的困难
22 7 8 9 10 11 12 13 14
现在市面上众多的C编译器都是以开发Windows或Linux上的应用程序为前提而设计的,几乎 从来没有人想过要用它们来开发其他的软件,比如自己的操作系统。笔者所提供的编译器,也是 以Windows版的gcc②为基础稍加改造而做成的,与gcc几乎没什么不同。或许也有为开发操作系统 而设计的C编译器,不过就算有,恐怕也只有开发操作系统的公司才会买,所以当然会很贵。这 次我们用不了这么高价的软件。
因为这些原因,我们只能靠开发应用程序用的C编译器想方设法编写出一个操作系统来。这 实际上是在硬来,所以当中就会有很多不方便的地方。
就比如说printf(―hello\\n‖);吧,这个函数总是出现在C语言教科书的第一章,但我们现在就
连它也无法使用。为什么呢?因为printf这个函数是以操作系统提供的功能为前提编写的,而我 们最开始的操作系统可是什么功能都没有。因此,如果我们硬要执行这个函数的话,CPU会发生 一般保护性异常③,直接罢工。刚开始的时候不仅是printf,几乎所有的函数都无法使用。
关于这次开发语言的选择,如果非要说出个所以然的话,其实也是因为C语言还算是很 少依赖操作系统功能的语言,基本上只要不用函数就可以了。如果用C++的话,像new/delete 这种基本而重要的运算符都不能用了,另外对于类的做法也会有很多要求,这样就无法发挥 C++语言的优势了。当然,为了使用这些函数去开发操作系统,只要我们想办法,还是能够 克服种种困难的。但是如果做到这个份上,我们不禁会想,到底是在用C++做操作系统呢,
——————————
① http://hrb.osask.jp。
② GNU项目组开发的免费C编译器,GNU C Compiler的简称。有时也指GUN开发的各种编译器的集合(GNU Compiler Collection)。
③ 电脑的CPU非常优秀,如果接到无视OS保护的指令或不可能执行的指令时,首先会保存当前状态,中断正在执行 的程序,然后调用事先设定的函数。这种机制称为异常保护功能,比如除法异常、未定义指令异常、栈异常等。 不能归类到任何异常类型中去的异常事态被称为一般保护异常。这种异常保护功能或许会让老Windows用户想起 那噩梦般的蓝屏画面,但是如果经历过操作系统开发以后,大家就会觉得这种机制实在是太有用了。
15 15 16
8 …… 第 0 天:着手开发之前
还是在为了C++而做操作系统呢。对别的语言而言这个问题会更加突出,所以这次还是决定 使用C语言,希望大家予以理解。
顺便插一句,在开发操作系统时不会受到限制的语言大概就只有汇编语言①了。还是汇编语
言最厉害②(笑)。但是如果本书仅用汇编来编写操作系统的话,恐怕没几个人会看,所以就算是 做事管前不顾后的笔者也不得不想想后果。
另外,在开发操作系统时,需要用到CPU上的许多控制操作系统的寄存器③。一般的C
编译器都是用于开发应用程序的,所以根本没有任何操作这些寄存器的命令。另外,C编译 器还具有非常优秀的自动优化功能,但有时候这反而会给我们带来麻烦。
归根到底,为了克服以上这些困难,有些没法用C语言来编写的部分,我们就只好用汇编语 言来写了。这个时候,我们就必须要知道C编译器到底是怎样把程序编译成机器语言的。如果不 能够与C编译器保持一致的话,就不能将汇编语言编写的部分与C语言编写的部分很好地衔接起 来。这可是在编写普通的C语言程序时所体会不到哦!不过相比之下,今后的麻烦可比这种好处 多得多啊(苦笑)。
同样,如果用C++来编写操作系统,也必须知道C++是如何把程序编译成机器语言的。
当然,C++比C功能更多更强,编译规则也更复杂,所以解释起来也更麻烦,我们选用C语言 也有这一层理由。总之,如果不理解自己所使用的语言是如何进行编译的,就没法用这种语 言来编写操作系统。
书店里有不少C语言、C++的书,当然也还有Delphi、Java等其他各种编程语言的书,但这么 多书里没有一本提到过―这些源代码编译过后生成的机器语言到底是什么样的‖。不仅如此,虽 然我们是在通过程序向CPU发指令的,但连CPU的基本结构都没有人肯给我们讲一讲。作为一个 研究操作系统的人,真觉得心里不是滋味。为了弥补这一空缺,我们这本书就从这些基础讲起(但 也仅限于此次开发操作系统所必备的基础知识)。
我们具备了这样的知识以后,说不定还会改变对程序设计的看法。以前也许只想着怎么写出 漂亮的源代码来,以后也许就会更注重编译出来的是怎样的机器语言。源代码写得再漂亮,如果 不能编译成自己希望的机器语言,不能正常运行的话,也是毫无意义的。反过来说,即便源代码 写得难看点儿,即便只有特定的C编译器才能编译,但只要能够得到自己想要的机器语言就没有 问题了。虽然不至于说―只要编译出了想要的机器语言,源代码就成了一张废纸‖,但从某种意 ——————————
① Assembler,与机器语言最接近的一种编程语言。过去掌握这种语言的人会备受尊敬,而现在这种人恐怕要被当作 怪人了,真是可悲啊。原本汇编语言的正式名称应该是Assembly语言,而Assembler一般指的是编译程序。不过像 笔者这样的老程序员,往往不对这两个词进行区分,统称为Assembler。 ② 读到这里,大家可能还不理解为什么这么说,越往后看就越能慢慢体会到了。
③ Register,有些类似机器语言中的变量。对CPU而言,内存是外部存储装置,在CPU内核之中,存储装置只有寄存 器。全部寄存器的容量加起来也不到1KB。