在CPU中有一个重要的寄存器CR0, 它是32位的寄存器, 其中一个位(PG位)
是负责告诉系统是否分页的. Windows在启动前将它的PG位置1, 即Windows允许分
页. DDK中有个宏PAGE_SIZE记录着分页的大小, 一般为4KB. 4GB的虚拟内存会被分
割成1M个(4GB/4KB=2^20)分页单元.
其中, 有一部分单元会和物理内存对应起来, 即虚拟内存中第N个分页单元
对应着物理内存第M个分页单元. 这种对应不是一一对应, 而是多对一的映射, 多个
虚拟内存页可以映射同一个物理内存页.
还有一部分单元会被映射成磁盘上的文件, 并标记为脏的(Dirty). 读取这
段虚拟内存的时候, 系统会发出一个异常, 此时会触发异常处理函数, 异常处理函数
会将这个页的磁盘文件读入内存, 并将标记设置为不脏.
当让经常不读写的内存页, 可以交换(Swap)成文件, 并将此页设置为脏.
还有一部分单元什么也没有对应, 即空的.
从图5-2中可以看出, 进程1和进程2虚拟内存映射的方式完全不同, 有的物理
内存块只映射到进程1上, 因此无论对进程2怎样操作, 也不会影响到那块内存.
另外有些物理内存既映射到进程1上, 也映射到进程2上. 这样修改进程2的那
段虚拟内存, 进程1的相应虚拟内存也会随之改变.
这种方法就是所谓的进程间共享内存.
分页与非分页内存
Windows规定有些虚拟内存页面时可以交换到文件中的, 这类内存被称为分页
内存, 而有些虚拟内存页永远不会交换到文件中的, 这些内存被称为非分页内存.
在编译DDK提供的历程时, 可以指定某个例程和某个全局变量时载入分页内存
还是非分页内存, 需要做如下定义:
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#defien PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
如果将某个函数载入到分页内存中, 我们需要在函数的实现中加入如下代码:
#pragma PAGEDCODE
void SomeFunction()
{
PAGED_CODE();
// do something
}
其中, PAGED_CODE()是DDK提供的宏, 它只在check版本中生效. 它会检验这个函数
是否运行低于DISPATCH_LEVEL的中断请求级, 如果等于或高于这个中断请求级别, 将产生一
个断言.
如果让函数加载到非分页内存中, 需要在函数的实现中加入如下代码:
#pragma LOCKEDCODE
void SomeFunction()
{
// do something
}
还有一种特殊情况, 就是某个例程需要在初始化的时候载入内存, 然后就可以从内存中
卸载掉. 这种情况指出现在DriverEntry情况下, 尤其是NT式的驱动, DriverEntry会很长, 占据
很大的空间, 为了节省内存, 需要及时地从内存中卸载掉.
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDrvierObject,
IN PUNICODE_STRING RegistryPath)
{
// do something
}