首页
社区
课程
招聘
[求助]分页内存与非分页内存的疑惑
发表于: 2012-12-24 17:55 15230

[求助]分页内存与非分页内存的疑惑

2012-12-24 17:55
15230
张帆《驱动详解》中讲到:
当程序的中断请求级在DISPATCH_LEVEL之上时(包括DISPATCH_LEVEL层),程序只能使用非分页内存,否则将导致蓝屏死机。
我的疑问是:
1.代码本身只能被加载到非分页内存吗,假设下面的例程void somefun()总是运行在DISPATCH_LEVEL时,
我做如下定义,在实际运行怎么不报错?
#pragma PAGEDCODE
void somefun()
{
    ...
}

2.代码内部能否只能动态分配非分页内存,假设下面的例程void somefun()总是运行在DISPATCH_LEVEL时,。
我在例程内部编写如下代码,在实际运行怎么不报错?
#pragma PAGEDCODE
void somefun()
{
      PVOID pTmp = ExAllocatePool(PagePool, 1024);
     ... //对pTmp操作,
}

请高手帮我解答一下啊,谢谢

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 1370
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
斑竹,怎么没人帮忙解释啊
2012-12-25 11:26
0
雪    币: 57
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
假设下面的例程void somefun()总是运行在DISPATCH_LEVEL时
-------------------------------------------------------------------------
你自己都说了,这是假设。
2012-12-25 14:09
0
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
首先你得明白为什么不能使用分页内存
原因是: win系统的内核调度线程运行在DISPATCH_LEVEL这个级别,
如果你也运行在这个级别,线程调度将不能运行,直到你的irql级别降到DISPATCH_LEVEL以下,windows的调度线程不能运行的话,就不能执行线程等待,(书上说的)
而如果访问分页内存的话,(注意,这是关键)如果分页内存数据不在物理内存中,也就是被交换到了虚拟内存页面文件中,将触发内存缺页中断,windows将会试图访问虚拟内存页面文件pagefile.sys,把被交换出的数据读入物理内存中,可是,访问文件是会引发i/o操作的,i/o操作中会有等待,
接着,系统直接崩溃,(说好的不能做等待操作的,居然等待,你就等着蓝屏吧)

你的第二个问题的回答是
就算你的页面属性是分页内存,但是没有被交换出去,还是在内存中,访问时不会引发缺页中断
但是,哪天,windows发现内存不够了,把你的代码数据交换到内存页面文件中(pagefile。sys),
呵呵,立即蓝屏~~~

第一个问题答案是, 爱把代码放哪就放哪。蓝屏死机错误你就囧了

一句话概括 :运行在DISPATCH_LEVEL级别的线程不能做等待操作,一切有等待操作的调用都会直接导致蓝屏
2012-12-30 10:43
0
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
哦对了,等待操作   其实就是线程切换操作~~
2012-12-30 11:17
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
分页内存需要切换,DISPATCH_LEVEL级别是不允许切换的
2012-12-30 14:59
0
雪    币: 22
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
关键是 缺页中断处理程序 也运行在 DPC_LEVEL。

就好像你本来 运行在 DPC_LEVEL,访问一个不在物理内存中的 虚拟地址,就会产生缺页中断。 因为 你的程序 和 缺页中断 在同一级 DPC 所以,你的程序不会被 缺页中断 打断。 然后访问一个错误的地址。

如果程序是在 DPC 级以下,程序会被 缺页中断 打断,这样就有机会把 你要访问的内容 重新加载到内存,进行正确的访问。
2012-12-30 23:46
0
雪    币: 55
活跃值: (531)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
8
正解。+1.
2012-12-31 08:04
0
雪    币: 1370
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
谢谢学雄兄的热心解释。

将此问题归纳如下:
当例程somefun在任何时候都将运行在DISPATCH_LEVEL级别时,(1)定义该例程somefun页面属性为非分页内存。也就是只能将somefun加载到非分页内存,即在例程前加上#pragma LOCKEDCODE
(2)在例程内部使用ExAllocatePool进行动态分配内存时,也只能分配非分页内存。
对以上两点,用代码来表示就是:
#pragma LOCKEDCODE
void somefun()
{
PVOID pTmp = ExAllocatePool(NonPagePool, 1024);
... //对pTmp操作,
}

如果将#pragma LOCKEDCODE改为#pragma PAGEDCODE或者将NonPagePool改为PagedPool,都将因为可能产生缺页中断,而无法进行线程切换而蓝屏。

在调试实验过程中(还是假设somefun例程总是运行在DISPATCH_LEVEL中),
#pragma PAGEDCODE
void somefun()
{
PVOID pTmp = ExAllocatePool(PagePool, 1024);
... //对pTmp操作,
}
上述代码没有发生蓝屏的原因,是因为“运气好”(物理内存充足,没有将这段代码置换到页文件,所以没有发生缺页中断,也就没有蓝屏)。

这是我对我最初提出两点疑惑的理解总结,对吗?
2012-12-31 10:15
0
雪    币: 813
活跃值: (175)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
根源是这样的:(挖坟)
系统处理缺页异常时会调用下面的函数处理却页异常:
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.”
2013-12-3 11:53
0
雪    币: 693
活跃值: (108)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
清晰的正解,赞
2014-8-19 13:23
0
雪    币: 3785
活跃值: (3947)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
所有楼上的解释全是一本正经的胡说八道! 建议各位仔细看一下wrk里MmAccessFault以及MmAccessFault返回后Trap0E的处理。总结一句话就是:如果irql大于APC_LEVEL且当前虚拟地址对应的PTE的valid位为0时,直接bsod,根本不会等到换页时才bsod!
2020-9-12 15:11
0
雪    币: 83
活跃值: (1092)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
13
666
2020-9-13 00:29
0
游客
登录 | 注册 方可回帖
返回
//