最近我的闲暇时间几乎都用在了研究如何开发操作系统上,遇到了不少痛苦。这方面的材料实在太少,而更多的是理论方面的文章。我有时候在想,我有没有可能用更通俗易懂的方式表达清楚一些技术问题,让对这块有兴趣的同志少走一些弯路,因为有兴趣研究的人太少了,要像保护大熊猫一样加以保护。从我自己在网上看技术文章的经验来看,如果一篇文章前面有作者的一段真实的啰嗦,我反而更愿意看。所以我希望这段啰嗦能够刺激你看下去。如果困惑于无人讨论,可以加入到QQ群:82616767,这里面有接近200个开发操作系统的爱好者,并且比较活跃。
从我自己的学习经历,以及在群里或者论坛上的那些同志们的经历来看,保护模式是第一个绊脚石。不管是看的《自己动手写操作系统》,还是《Linux内核完全剖析》,都会遇到这一问题。许多人雄心勃勃要系统研究操作系统,甚至自己写一个伟大的操作系统,往往遇到这个问题先出师未捷身先死,以至于停下来,怀着恐惧的心理再也不想碰这块神秘的森林。继而去干点儿其他的工作,让自己游离在操作系统之上。
要解答这个问题,还是要从更遥远的历史说起。
那时候的CPU还比较简单,只有16位内存地址。一个内存地址对应一个字节(CPU一般都是按照字节来寻址,是最小的寻址单位),也就是共有2^16=64KB。在Intel的8086芯片中,为了让访问的范围更大,用了20位的地址总线,也就是实际可访问的内存空间是2^20=1MB,对当时的使用环境来说,已经足足够用了。尽管是20位的地址总线,但CPU的寄存器都是16位的,那怎么去算出指定的内存地址呢?答案就是分段机制。我们在段寄存器(假设DS)中存放一个16位的段选择符,然后在地址寄存器(假设AX)中存放一个16位的偏移,CPU将段选择符左移4位(),再加上地址寄存器,就形成了真正的20位线性地址。即:段选择符*16 + 偏移地址。我们在写代码时,一种可能的形式就是jmp DS:AX。这种CPU地址转换模式就是传说中的实模式。
随着CPU使用的普及,渐渐发现实模式有两个致命的问题。一是最多只可以访问到1MB的空间,对于一个大程序来说,这显然是不够用的。二是不同的程序都可以访问到全局的内存空间,很容易引起系统崩溃,非法改写别人的数据。
解决第一个问题很容易,我们只要将偏移地址变成32位就可以了,即使没有段选择的考虑,也可以访问到2^32=4GB的内存空间,显然对一般的程序来说足足够用。但如何解决程序间的内存访问权限控制问题呢?直观的感受是,我每访问一个地址,就判断一下是不是有这个权限。那就要一个地方描述对应地址的权限控制信息。显然,我们不可能每个地址都有一个控制信息,那实在是太浪费了,我们只能采用分块的方式。比如地址范围0x00000000——0x00001000只准A程序读写,而另一块地址只准B程序读,不准写。这些块的描述信息就称为段描述符,包括权限信息,段的起始地址,段的范围,段的其他附加属性信息等。现在又有一个新的问题,就是这些段描述符放在什么地方呢?以及怎么去访问它呢?这就需要和CPU做一个约定,比如地址0x00100000为段描述符的起始地址,每个描述符有8个字节大小,比如第一个地址是0x00100000,那第二个地址就是0x00101000。而这一堆段描述符的起始地址0x00100000就被称为GDT(全局描述表)。为了保证灵活性,这个地址不是固定的,而是由开发者指定,然后通过LGDT指令告诉CPU。当然,对于一个程序,它可能采用类似机制来管理自己的局部空间里的内存,进行类似的权限控制,那对应的描述符表就是LDT(所谓局部描述符表)。好了,这样就实现了权限控制。
等一下,还有一个问题,我们自己组装的电脑就会考虑升级硬件的问题,比如换一个更强劲的CPU,我可不想装了之后之前的操作系统都不能用了。也就是要考虑向下兼容的问题。CPU为了满足这一点,就同样采用了分段机制,和实模式的写法保持一致,如jmp 0x0000:0x00004567,但前面的段选择符的含义改掉。变成了去GDT或LDT中的索引以及基本的段权限信息。其中用一个bit表示这个段选择符是属于GDT还是LDT。这样,我们就能保证之前的程序能够继续不用修改就能运行。但也让那些直接使用32位保护模式的开发者多了一个麻烦,就是用一条指令将CPU从实模式转变为保护模式。当然,在转换之前,你要做好GDT的初始化工作,不然CPU就会算出错误的内存地址了。
保护模式的寻址方式大致是这样的:首先通过段选择符中的偏移,获取到对应的段描述符,进而获取里面的基地址,然后加上你的内存偏移地址,就是实际的内存地址。当然,这里没有考虑分页机制,那是一个类似分段的进一步的转换方式,这里就不再描述了。
至于保护模式中的权限控制,我觉得对于新手来说,可以先跳过,考虑最宽泛的权限就可以了,自己写的操作系统,就不用担心安全问题了。等到用的人多了(可能性不大),再细化权限部分。
好了,经过上面的描述,你了解清楚保护模式了吗?如果还是没有,请发邮件到sangwf#gmail.com,我来帮你进一步解答