0CCh Blog

使用Windows未使用内存原理和应用

20150202104522

配置4G内存,并且使用过32bit Windows系统的人都知道,虽然自己有4G的物理内存,但是Windows还是明确的告诉你,它只会用其中的3GB多,还有几百MB物理内存是用不到的,即使你开启了PAE。当然如果你用的服务器系统,那就当我没说。至于微软为啥服务端32bit系统可以用4GB以上,而限制普通客户端系统,按照Windows Internals的说法,是为了考虑驱动程序的兼容性问题。我这里想介绍的是,如何使用这些没有使用的物理内存。

20150202105145

首先,要想使用这些内存,我们必须找到他们,但是找到他们之前,我们还得了解物理内存地址是怎么分配。物理内存地址除了要给RAM提供地址之外,还需要给设备内存提供地址。为了考虑驱动的兼容性,这些设备内存被分配到4G以内的地址上,这样一来,就会有部分RAM不得不分配到4G以外的地址上了,所以我们无法使用它们。

20150202110332

知道了这些,我们就需要聚焦到如何访问超过4GB内存的方法上了。不过方法也很简单,就是MmMapIoSpace函数,这个函数可以访问64bit的物理内存地址,并且将其映射到我们可以访问的虚拟内存上。

说到这里,程序的代码仿佛就呈现在脑海了,不过等等,还忽略了一个最困难的问题!到底有存在多少RAM内存在4GB以上的地址空间呢?说这个问题最为困难,是因为你需要根据不同的情况做出不同的选择。

20150202095553

1.主板支持通过Bios查询RAM内存分配情况,在这种情况下,我们可以调用中断来获得最真实的RAM分配表。
2.主板不支持通过Bios查询RAM内存分配情况,那么我们很无奈的必须采用一个简单粗暴的方法,Write & Test来获得4GB以外到底有多少RAM可用。

对于第二种情况,我们的做法是通过MmMapIoSpace函数,把4GB以上的物理地址逐一映射到虚拟内存中,然后写入特殊值,写入后马上读取,看是否写入成功,成功则证明RAM可用。
对于第一种情况,这里又要分为两个分支:
首先你的系统是NT6内核以下的情况,这时可以调用Ke386CallBios函数,调用中断接口。
而对于NT6的内核,我们需要调用新的x86BiosCall等一套函数。
需要调用的中断函数是0x15以及AX=E820,通过这个中断和功能号,我们就能获得SYSTEM MEMORY MAP

下面附带上这个功能的使用方法

INT 15 - newer BIOSes - GET SYSTEM MEMORY MAP  
AX = E820h
EAX = 0000E820h
EDX = 534D4150h ('SMAP')
EBX = continuation value or 00000000h to start at beginning of map
ECX = size of buffer for result, in bytes (should be >= 20 bytes)
ES:DI -> buffer for result (see #00581)
Return: CF clear if successful
EAX = 534D4150h ('SMAP')
ES:DI buffer filled
EBX = next offset from which to copy or 00000000h if all done
ECX = actual length returned in bytes
CF set on error
AH = error code (86h) (see #00496 at INT 15/AH=80h)
Notes: originally introduced with the Phoenix BIOS v4.0, this function is
now supported by most newer BIOSes, since various versions of Windows
call it to find out about the system memory
a maximum of 20 bytes will be transferred at one time, even if ECX is
higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the
value of ECX on entry, and always copy 20 bytes
some BIOSes expect the high word of EAX to be clear on entry, i.e.
EAX=0000E820h
if this function is not supported, an application should fall back
to AX=E802h, AX=E801h, and then AH=88h
the BIOS is permitted to return a nonzero continuation value in EBX
and indicate that the end of the list has already been reached by
returning with CF set on the next iteration
this function will return base memory and ISA/PCI memory contiguous
with base memory as normal memory ranges; it will indicate
chipset-defined address holes which are not in use and motherboard
memory-mapped devices, and all occurrences of the system BIOS as
reserved; standard PC address ranges will not be reported
SeeAlso: AH=C7h,AX=E801h"Phoenix",AX=E881h,MEM xxxxh:xxx0h"ACPI"
Format of Phoenix BIOS system memory map address range descriptor:
Offset Size Description (Table 00580)
00h QWORD base address
08h QWORD length in bytes
10h DWORD type of address range (see #00581)
(Table 00581)
Values for System Memory Map address type:
01h memory, available to OS
02h reserved, not available (e.g. system ROM, memory-mapped device)
03h ACPI Reclaim Memory (usable by OS after reading ACPI tables)
04h ACPI NVS Memory (OS is required to save this memory between NVS
sessions)
other not defined yet -- treat as Reserved
(from http://www.ctyme.com/intr/rb-1741.htm

获得了4GB内存以上的RAM范围以后,再加上MmMapIoSpace,我们就能够访问系统没有使用的内存了。

说了这么多,这个有什么具体应用呢?我见过最多利用这个特性做的产品就是内存盘了。也没看到什么用的特别好的其他应用的方面,我想原因主要有三点,第一,即使获得了物理内存,最后还是映射了4GB以内的虚拟内存上,内存操作终究无法访问直接更多物理内存;第二,这部分内存没有系统统一管理,同类型的软件无法共存;第三,有4G内存赶紧快去换个64位系统吧=v=。