根源是这样的:(挖坟)
系统处理缺页异常时会调用下面的函数处理却页异常:
NTSTATUS MmReadFromSwapEntry(SwapEntry,pfn)
{
MDL mdl;
…
MmBuildMdlFromPages(mdl,pfn);//将物理页面pfn映射到系统的mdl映射区中
FileNo=SwapEntry.FileNo;
FileOffset=SwapEntry.PageNo * 4kb;
//这个函数内部会构造一个分页读irp发往文件系统,最后发给磁盘驱动,读入页文件中对应的页面
Status=IoPageRead(PagingFileList[FileNo]->FileObject, FileOffset,mdl,…);//读入到物理页面
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE,
NULL //看到没,Timeout参数=NULL,表示一直等到磁盘页面读入完成
);
Status = Iosb.Status;
}
…
Return status;
}
由于涉及到磁盘I/O,因此,置换过程有点耗时!频繁的缺页异常往往会造成系统性能瓶颈,这就是时间换空间带来的副作用。
另外:由于MmReadFromSwapEntry这个函数会在内部调用KeWaitForSingleObject一直等到页面读入到内存后才返回原处,继续执行。但是KeWaitForSingleObject这个函数,如果是要无限等待的话,只能运行在DISPATCH_LEVEL irql以下,否则,蓝屏。这就是为什么在DISPATCH_LEVEL及其以上irql时,千万不能不能访问分页内存。因为分页内存可能在磁盘中,这样,一触发缺页中断,在这个irql尝试去读取磁盘页面时,就会因为KeWaitForSingleObject的问题而崩溃。
【换言之,根源是DISPATCH中断级的代码不能调用KeWaitForSingleObject无限等待任意对象】
下面引自DDK原话:“Callers of KeWaitForSingleObject must be running at IRQL <= DISPATCH_LEVEL. However, if Timeout = NULL or *Timeout != 0, the caller must be running at IRQL <= APC_LEVEL and in a nonarbitrary thread context.”