(
)操作系统必须知道系统物理内存的数量,才能够有效的使用和管理这些物理内存。所以在booting阶段,我们必须通过某种手段来检测和获取物理内存的总量。
获取物理内存容量的方法主要分为两大类:一类是通过BIOS软中断方式获取,另一类是通过读取北桥芯片内存控制器相关寄存器获取。
第一类方法的优点是历史悠久,自80286以来,绝大多数IBM PC兼容机都提供这一功能,现在的Windows和linux也是通过这一类方法在启动阶段获取物理内存容量,另外由于这种方法是通过对内存的遍历来获取内存容量的,因此一些内存检测工具也是通过这类方法对内存进行检测。但是,这类方法在检测大容量内存时,若只需要物理内存总容量信息,则效率低于第二类方法。
第二类方法与第一类方法本质的区别在于这种方法使用硬件方法实现物理内存容量的获取,因此速度较快。但是这种方法仅能获取物理内存容量,不包括对内存的检测。
第一类方法包括三种具体做法,这三种做法都是基于BIOS INT15h中断(在PC和XT机上是BIOS磁带I/O功能调用。在AT机上是AT扩充服务功能调用),它们的名称依此为88h,E801h,E820h。其中,88h方法是在Intel80286出现的那天起就存在的,后续的PC及其兼容机为了保持向下兼容,都继续保留了这种方法。因此,这种方法在所有的IBMPC及其兼容机上都存在。但事实上,这种方法存在一个重要的缺陷,由于这种方法是在16bit时代就存在的,所以,它通过16bit寄存器来保存内存容量作为返回值。但16bit所能够表示的最大值是64KB。由于这种方法的返回值是以KB为单位,所以它能够表示的系统最大物理内存容量为64 MB。而对于今天的PC机来说,64MB已经是很低的内存配置了,大多数PC机的实际物理内存配置都大于64 MB。另外,需要注意的是,由于这种方法出现于Intel80286时代,而80286的24bit地址总线能够访问的最大地址为16MB,所以,尽管这种方法使用16bit寄存器能够表示的最大内存数量为64MB,但标准的BIOS只允许通过这种方法获取最大16MB的物理内存数量。
为了能够获取大于64MB的内存,就必须通过另外两种方法的一种。但并非每一台PC机都实现了这两种方法或这两种方法之一。对于绝大多数现代PC机来说,至少提供了这两种方法的一种;而对于过去的PC机,又很少有超过64MB的配置。从这个意义上来说,这已经足够了。
1.Int 15/AX=E820h是用来获取系统中的内存映地址描述符的,操作系统常用来获取内存大小。
PS:内存(Memory)可以是我们平时所说的在主板上的内存条,我们称作Base Memory,也可以是毗边Base Memory 的并作为普通内存扩展的内存。
相关的数据结构:
地址范围描述符结构
Offset |
Name |
Description |
0 |
BaseAddrLow |
基地址的低32位 |
4 |
BaseAddrHigh |
基地址的高32位 |
8 |
LengthLow |
长度(字节)的低32位 |
12 |
LengthHigh |
长度(字节)的高32位 |
16 |
Type |
这个地址范围的地址类型 |
其中Type的取值及其意义如下:
1 |
AddressRangeMemory |
这个内存段是一段可以被OS使用的RAM |
2 |
AddressRangeReserved |
这个地址段正在被使用,或者被系统保留,所以一定不要被OS使用 |
Other |
Undefined |
保留为未来使用,任何其它值都必需被OS认为是AddressRangeReserved |
输入参数:
Register |
Meaning |
Description |
EAX |
功能码 |
E820h |
EBX |
后续值 |
放置着"后续值",这个值是为了得到下一块物理内存段,它应该指定上一次调用此程序所返回的值,如果这是第一次调用,EBX必须被指定为0。 |
ES:DI |
缓冲指针 |
指向一个地址范围描述符结构,BIOS将会填充此结构。 |
ECX |
缓冲大小 |
缓冲指针所指向的地址范围描述符结构的大小,以Byte为单位,无论ES:DI所指向的结构如何设置,BIOS最多将会填充ECX个字节,必需被BIOS以及调用者所支持的最小尺寸是20个字节,未来的实现将会扩展此限制。 |
EDX |
标志 |
'SMAP' -BIOS将会使用此标志,对调用者将要请求的系统映象信息进行校验,这些信息会被BIOS放置到ES:DI所指向的结构中。 |
输出结果:
Register |
Meaning |
Description |
CF |
进位标志 |
不进位表示没有错误,否则则存在错误。 |
EAX |
标志 |
'SMAP' |
ES:DI |
缓冲指针 |
返回的地址范围描述符结构指针,和输入值相同。 |
ECX |
缓冲大小 |
BIOS所填充在地址范围描述符中的字节数量,被BIOS所返回的最小值是20个字节。 |
EBX |
后续 |
这里放置着为了等到下一个地址描述符所需要的"后续值",这个值的实际形势倚赖于具体的BIOS的实现,调用者不必要关心它的具体形式,只需要在下次迭代的时候,将其原封不动的放置到EBX中,就可以通过它获取下一个地址范围描述符。如果它的值为0,则表示它是最后一个地址范围描述符。 一定注意,只有这个后续值为零,并且CF没有进位的时候,才表示这是最后一个地址范围描述符。 |
如下一些原因造成BIOS将某个内存段标记为AddressRangeReserved:
- 这个地址范围包含着系统ROM;
- 这个地址范围包含着被ROM使用的RAM;
- 这个地址范围被用作系统设备内存映射;
- 这个地址范围由于某种原因,不适合被标准设备用作设备内存空间。
原理描述:
调用此功能可以从系统某一个地方(这里存放有一个List(列表),这个列表里存放的都是一个个的
Entry)通过设置EBX(20的倍数)的值,然后调用int 0x15就可以讲EBX所指向的Entry拷贝
到ES:DI所指向的内存区)
2.INT 15h, AX=E801h
E801h只能在Real Mode下使用。
最初,这种方式是为EISA服务定义的,这个接口能够报告多达4G的RAM。然而它不象E820h方式那么通用,E820h在更多的系统上可以使用。
输入:
Register |
Meaning |
Description |
AX |
功能码 |
E801h |
输出:
Register |
Meaning |
Description |
CF |
进位标志 |
不进位表示没有错误 |
AX |
扩展1 |
1到16M内存的容量,以KB为单位,最大数量0x3C00 =15M |
BX |
扩展2 |
16MB到4GB之间的内存容量,以64K为单位 |
CX |
配置1 |
1到16M内存的容量,以KB为单位,最大数量0x3C00 =15M |
DX |
配置2 |
16MB到4GB之间的内存容量,以64K为单位 |
无法确定"扩展"和"配置"之间的不同到底在哪里,事实上它们的值是相同的。
注意:一个机器可能使用这个接口来报告16M以下内存空洞(count1小于15M,但count2却为非0)。
3.INT 15h, AH=88h
E88h只能在实模式下使用。
这个接口是相当原语性的,它返回1M地址以上的后续内存容量。最大的限止是它的返回值是16-bit的,以KB为单位,所以它最多能够返回64M。在某些系统上,它仅仅能够返回16M以内的内存。
和前两者相比,它的唯一好处是它在所有的PC上都工作。
输入:
Register |
Meaning |
Description |
AH |
功能码 |
88h |
输出:
Register |
Meaning |
Description |
CF |
进位标志 |
不进位表示没有错误 |
AX |
内存容量 |
以KB为单位,1MB以上的内存容量 |
PS:需要说明的是,使用BIOS的方法在某些情况下会无法工作。在这些情况下,可以使用直接探测内存方法。直接探测内存,是一种不依赖于BIOS的内存检测方法,这使得它是一种更加轻便的方法。当使用这种方法的时候,需要考虑系统内存空洞,以及被映射到外部设备上的内存(比如framebuffering SVGA cards)。
使用这种方法的时候,必须进入Protected Mode。
另外,由于Intel的386以后的CPU为了提高内存访问速度,使用了memorycache,来缓冲内存中的内容。但对于我们想通过直接探测内存来确定内存容量的方法来讲, memorycache反而会造成负面的影响,因此我们必须禁止memory cache。留着以后有时间再慢慢研究吧:)
4.使用硬件方式获取物理内存容量
通过读内存spd和北桥的memory controller相关的寄存器,再根据内存的当前模式(mirror或者non-mirror)计算内存大小。不过,对于普通开发者来说,使用这种方式获取内存容量信息,需要知道北桥芯片相关寄存器地址,这就需要依赖北桥芯片datasheet了。Intel生产的芯片组datasheet一般来说比较容易获得,但是诸如VIA、NV等厂商生产的芯片组datasheet在网上比较少见,而且对应型号也都比较老,因此这种方法在有些时候并不是那么可行:)