页错误的时候,先处理有效的情况,例如copy-on-write
二.无效情况:
MmAccessFault函数中查看到faultaddress对应的pte无效之时,查看TempPte.u.Soft.Prototype,
1 判断原型pte
if (TempPte.u.Soft.Prototype != 0) {
如果是指向原型PTE,就要找出对应的原型PTE
PointerProtoPte = MiPteToProto (&TempPte); //这个是直接在PTE中取出来
if ((SessionAddress == TRUE) &&
(TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {
//这个是从VAD树里面找出来
PointerProtoPte = MiCheckVirtualAddress (VirtualAddress,
&ProtectionCode,
&ProtoVad);
这
如果是,则获取对应的ppte做为 PointerProtoPte
MiDispatchFault (
IN ULONG_PTR FaultStatus,
IN PVOID VirtualAddress,
IN PMMPTE PointerPte,
IN PMMPTE PointerProtoPte,
IN LOGICAL RecheckAccess,
IN PEPROCESS Process,
IN PVOID TrapInformation,
IN PMMVAD Vad
)
最后不管什么情况,就去调用MiDispatchFault来处理page fault
下面是MiDispatchFault中的处理情况:
1。原型PTE,当PointerProtoPte不为空时,说明这是一个原型PTE,调用MiResolveProtoPteFault来处理
else TempPte = *PointerPte;
2 ,transition状态的PTE
else if ((TempPte.u.Soft.Transition == 0) &&
(TempPte.u.Soft.Protection == 0)) {
调用 MiResolveTransitionFault 来解决
3。
else if (TempPte.u.Soft.PageFileHigh == 0) {
//
// Demand zero fault.
//
status = MiResolveDemandZeroFault (VirtualAddress,
4。
else {
//
// Page resides in paging file.
//
MiResolvePageFileFault
在调用 MiResolveProtoPteFault 处理原型PTE时候,也要根据判断原型PTE是否有效来处理
原型PTE有效的话,直接调用MiCompleteProtoPteFault 完成处理
否则无效时,又对应下面4种情况:
// Make the prototype PTE valid, the prototype PTE is in
// one of these 4 states:
//
// demand zero
// transition
// paging file
// mapped file
//下面的判断是按顺序来的。
//if (TempPte.u.Soft.Prototype == 1) {MiResolveMappedFileFault//这个是用subsection来读取
// Calculate address of subsection for this prototype PTE.
// //PointerPte里面记录的是subsection的偏移(相对MmSubsectionBase),MmSubsectionBase =MmNonPagedPoolstart
Subsection = MiGetSubsectionAddress (PointerPte);
else if (TempPte.u.Soft.Transition == 1) {MiResolveTransitionFault //本身里面的物理页还在内存中。只是暂时无效而已
else if (TempPte.u.Soft.PageFileHigh == 0) {MiResolveDemandZeroFault//找一块物理内存,清空,然后让原来的pte有效并指向这块物理内存
else {
//
// Paging file.
//
////TempPte.u.Soft.PageFileHigh便是在page中的偏移,但只有20位,这是页编号,按页对齐,所以//TempPte.u.Soft.PageFileHigh<<12后才是准确的offset
//所以pagefile最大也是只支持4G,windows internals这书里面也有提到
status = MiResolvePageFileFault (FaultingAddress,
PointerProtoPte,
//
//////////////////////////////////////
在处理有效的原型PTE时调用MiCompleteProtoPteFault。
里面只使用了PointerProtoPte原型PTE获取对应的PageFrameIndex,然后就设置给相应的va的pte,
MI_MAKE_VALID_USER_PTE (TempPte,
PageFrameIndex,
Protection,
PointerPte);
PageFrameIndex并不是从pfn的"原始PTE内容:"取的
无效原型PTE时,也只是从原型PTE获取Subsection,并不是用了pfn中的原始内容,这里和书上说的有出入。奇怪
。有了subsection后,就能能进来了
//
// Calculate address of subsection for this prototype PTE.
//
Subsection = MiGetSubsectionAddress (PointerPte);
================================================
另外,这里也有一个地方说明了不要在dispatch level下访问分页内存的原因:
// Issue the read request.
//
status = IoPageRead (ReadBlock->FilePointer,
&ReadBlock->Mdl,
&ReadBlock->ReadOffset,
&ReadBlock->Event,
&ReadBlock->IoStatus);
if (!NT_SUCCESS(status)) {
//
// Set the event as the I/O system doesn't set it on errors.
//
ReadBlock->IoStatus.Status = status;
ReadBlock->IoStatus.Information = 0;
KeSetEvent (&ReadBlock->Event, 0, FALSE);
}
//
// Initializing PageFrameIndex is not needed for correctness, but
// without it the compiler cannot compile this code W4 to check
// for use of uninitialized variables.
//
PageFrameIndex = (PFN_NUMBER)-1;
//
// Wait for the I/O operation.
//
status = MiWaitForInPageComplete (ReadBlock->Pfn,
ReadPte,
VirtualAddress,
&OriginalPte,
CapturedEvent,
Process);
注:CapturedEvent和ReadBlock是同一块内存来的,
ChildEBP RetAddr
f7556358 8084bc13 nt!MiWaitForInPageComplete+0xd [c:\wrk-v1.2\base\ntos\mm\pagfault.c @ 4174]
f75563e0 80856fda nt!MiDispatchFault+0xd57 [c:\wrk-v1.2\base\ntos\mm\pagfault.c @ 1271]
f755643c 808858d8 nt!MmAccessFault+0x9c0 [c:\wrk-v1.2\base\ntos\mm\mmfault.c @ 2001]
f755643c 75de7c8b nt!_KiTrap0E+0xdc [C:\WRK-v1.2\base\ntos\ke\i386\trap.asm @ 5527]
0129f508 75db67b5 BROWSEUI!LoadToolbarBackBmp+0xf4
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。