解决实模式的内存地址映射问题的方法
解决实模式的内存地址映射问题的方法
最近时间紧了,工作忙了,进度也降下来了……无奈 大学时期没有多做点事情真是遗憾
闲话不说,进入正题。
昨天用 nasm重写linux0.11的bootsect.s,又有了一些新的体会。
看过赵博的书的人都知道,bootsect.s在BIOS加电后被载入0x07c00这个地方,然后它把自
己复制到0x90000,把setup.s复制到0x90200(紧挨着0x90000的bootsect.s),同时设栈
顶指针为0x9FF00,最后把system模块从磁盘读到0x10000处。
最初看这段代码的时候没什么体会,当时心想linus你也太小气了,分这么点内存。但当我
重写这段代码准备大显身手的时候发现我错了,错得太离谱。思考再三,对于下面这三个数
字0x90000、0x90200、0x10000我几乎不打算改变,这几个数字选得太好了,好的几乎很难
再有更好的方案去替代!
让我们看看linus是怎么选出这些数字来的吧。首先我在网上查到以下资料(出处不明,并
且原文有乱码,我做了适当修改):
***********************************************************
80386内存的寻址
这篇文章将会着重介绍内存寻址的不同方法。首先,我将从范围为00000000-000FFFFF的
1M内存的地址使用分配方式的描述开始。然后我将试图探讨1MB-4GB范围内存寻址的问题。
1M内存
当我们说到1M内存时,我们将谈到实模式,一种分段式内存模式,不具有32位寻址和内
存分页的功能。
低端内存的特定区域都保留做特殊用途并必须进行正确处理。为了说明这一点,我将列
出1M内存的映射的实例。
00000 003FF IVT Interrupt Vector Table (中断向量表)
00400 004FF BDA BIOS Data Area (BIOS数据区)
00500 00501 PRTSCR 1st byte is Print Screen Status Byte (第一字节是打印屏幕状
态字节)
00501 9xxxx OS OS specific (操作系统专用区)
07C00 07CFF BOOT Where Boot Sectors are loaded by the BIOS (BIOS装载引导扇
区的区域)
9xxxx 9FFFF EBDA Extended BIOS Data Area, varies in size (at least 1KB) (扩
展BIOS数据区,大小不等,不小于1KB)
A0000 AFFFF VIDEO Used by the Video Adapter (Graphics Mode) (用于彩色显示适
配器)
B0000 B7FFF VIDEO Used by the Video Adapter (Monochrome) (用于黑白显示适配器
)
B8000 BFFFF VIDEO Used by the Video Adapter (Textmode) (用于文本模式显示适配
器)
C0000 C7FFF VIDEO Used by the Video BIOS (用于显示适配器BIOS)
C8000 EFFFF ROM May be used by adapter ROMS or as memory mapped I/O (可用
于适配器ROM或内存映射式I/O)
F0000 FFFFF BIOS System BIOS, 32K (starts at F8000) or 64K (系统BIOS,32k<从
F8000开始>或者64K)
内存的映射随系统不同而不同,但大部分都和上例相类似。最有可能出现不同的是范围
为C0000-FFFFF的内存。
如果EBDA存在,那么它的尺寸大小将被在BDA内的偏移地址为13H处的一个WORD的变量指定
,单位为KB.操作系统能够使用的内存最多可达到这里所指定的数量,但使用这些内存的程
序必须随程序提供共享这些内存的方法,否则将是无用的。有一些其他的程序可能会用到
这些内存,如病毒和其他先于OS驻留内存的软件。或借助ROM或通过引导扇区。
***********************************************************
我们知道,实模式下的内存段并不是全部可用的,其中有许多段被IBM PC所预留。
据上表,我们可以看见在实模式极为有限的1M寻址空间内,只有0x00501~0x9XXXX可以被OS
自由使用,其余都被重要的设备所占用。
再让我们回头看看那三个数字:0x90000 0x90200 0x10000,它们意味了什么??
首先这个0x90000,它已经比较接近可用内存极限值0x9FFFF了(不考虑EBDA的话),将放
在这里的东西都是什么呢?是bootsect和setup模块,这两个模块合起来不会超过5个扇区
(2.5KB),这片空间是足够用了。这两个模块还需要有堆栈,linus设到了0x9FF00。这个
位置基本上是可以最大利用有限的内存地址空间并且具有较好的对齐地址(比如用0x9FFFF
的话效果堪忧= =)。那么这么一来,不管setup将来怎么扩大,这片空间也够它用了,并
且还能容下一个堆栈。
关于0x9FF00和EBDA稍后再提。
0x90200这个数字解释起来比较容易,bootsect长度恒为512字节(为什么?去查查sector
这个单词吧= =),移动后位置在0x90000,那么紧接着他的就是0x90200,setup就选择安
家在这里,和bootsect在同一个段限内,这样他们就可以共享同一个堆栈了。
0x10000,system选择暂住在这里,看看这个位置,它前面就是0x07c00,那里放着
bootsect的旧数据。在向前就是0x00501那一个孤零零的字节,再往前就是保留地址了。从
数字上来看,0x10000具有很好的对齐,计算很方便,它有效的利用了有限的内存空间。
system放在这里就意味着从0x10000-0x90000,都可以放置内核代码!0x10000和0x90000的
绝妙配合从这里就可以看得出了。
说到这里我不得不说一说system模块的移动问题,在0.11版最后linus将它移动到了物理地
址0以争取那64K的空间,代价是覆盖掉BIOS,这在那个时代是可以理解的,物理地址0也带
来计算上的方便。但是后来的的linux为了使用V86模式,就没有再这么做了(基于同样的原
因,0x9FF00的栈顶位置也被放弃了)。
还有必须要说的就是2.4.33.4中,上述三个数字0x90000,0x90200,0x10000都没有发生改
变!可见这个方案的优点还是很多的。
变了的那个0x9FF00到底是怎么回事??2.4.33.4采用如下代码:
movw %ax,%ds#%axand%esalreadycontainINITSEG
movw %ax,%ss
movw %di,%sp #putstackatINITSEG:0x4000-12.