Windows内存管理分析(四)

七、页面的换出 前文看到,当申请内存时发现物理内存已经快不足时就会触发事件,让平衡线程去换出一些页面到磁盘中,保证物理页面还足够。这个平衡线程执行的函数为MiBalancerThread
Windows内存管理分析(四)
文章图片
这个线程处于不断的等待之中,如果时间一到或者申请内存物理页面已经过低时,KeWaitForMultipleObjects就会等待到资源继续执行
Windows内存管理分析(四)
文章图片
如果是2级分页的话,每次遍历一个大页面,判断一下每个物理页面的引用计数,如果已经为0并且页面又是有效的那就会回收这个物理页面
Windows内存管理分析(四)
文章图片
接下来判断遍历系统每种类型的消费者,调用它们的修剪函数来修剪内存,此时如果消费者们都不肯腾出空间,那么视为严重错误,我们分析MiTrimMemoryConsumer函数
Windows内存管理分析(四)
文章图片
参数1是消费者类型,参数2是要释放的页面数量,首先判断消费者是否提供了Trim函数,如果没有直接返回原来的数值,如果页面使用数量大于页面原有的配给量,证明消费者过度使用,相减表明可以腾出多少空间,如果这时候还小于系统最小可用页数,那就必须把目标消减页数设置成刚好可以等于系统最小可用页数
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
循环的调用每个消费者的修剪函数。实际上OS只提供了1种消费者MmTrimUserMemory的函数,系统消费者和缓存消费者都不提供。事实上,分页内存池的Trim函数还没有实现,否则在平衡线程中,也应该加入分页内存池的trim函数。
Windows内存管理分析(四)
文章图片
这个使用LRU算法 循环知道修剪了目标参数给定的页数
Windows内存管理分析(四)
文章图片
在物理页面分配和回收的时候我们已经见过这个位图MiUserPfnBitMap
然后调用MmPageOutPhysicalAddress换出到磁盘中
在此之前介绍一个PFN的一个类型为PMM_RMAP_ENTRY的 RmapListHead成员
Windows内存管理分析(四)
文章图片
MM_RMAP_ENTRY是一个单向链表,它组织起来这个物理页面目前正在被谁所映射。对Process的解释却有2种,一种就是单纯的进程结构体,另一种是Section缓存页表,下文进行解释
首先我们来看一下怎么把一个物理页面倒换,使用的函数是MmPageOutPhysicalAddress
Windows内存管理分析(四)
文章图片
首先获取PFN数据库具体MMPFN项,然后获取这个链表头,开始遍历,然后获取对应的进程与页面基地址
Windows内存管理分析(四)
文章图片
页面基地址必须是4KB对齐,否则是严重错误
接下来判断地址是否处于用户空间,如果是则获取进程结构体对应的Vm成员,获取对整个虚拟空间的描述,否则是内核就获取内核的。
Windows内存管理分析(四)
文章图片
加锁之后,获取这个地址所在的MM_AREA,如果为空,或者这个AREA已经被删除,返回错误。
Windows内存管理分析(四)
文章图片
如果是一个SECTION,计算这个地址在SECTION的偏移,然后取出对应的页表项,交由MmPageOutSectionView做真正的处理
Windows内存管理分析(四)
文章图片
如果是缓存类型就调用MmpPageOutPhysicalAddress做真正处理
【Windows内存管理分析(四)】我们来分析这2个函数MmPageOutSectionView,MmpPageOutPhysicalAddress
Windows内存管理分析(四)
文章图片
首先是通过物理页号调用MmGetSectionAssociation获取对应的区和文件偏移,具体怎么通过物理页号获得这2个信息呢。
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
我们之前学过MMPFN有一个类型为MM_RMAP_ENTRY的成员连接着所有映射这个物理页面的一个链表,这个函数建立起这种联系过程,提供物理页号,进程,地址就可以构建MM_RMAP_ENTRY并插入,但是在设置这种联系的时候如果给定的地址 高24位全1的话此时的EPROCESS实际上是类型为CACHE_SECTION_PAGE_TABLE的页表指针
Windows内存管理分析(四)
文章图片
所以只要这个AREA是SECTION_VIEW类型,那么给定页号,就可以知道它映射到哪些物理页面(通过RmapListHead成员),不仅如此,还可以知道它映射在进程的哪个虚拟地址(RmapListHead结构成员的Address),如果这个虚拟地址同样也是属于SECTION的话 那么就可以获取到CACHE_SECTION_PAGE_TABLE,里面的成员Segment就是我们想要获得的区,还有具体的文件偏移。
接下来代码进行检查后,来到
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
进入循环遍历这些映射的页面,首先过滤掉Address是段映射的那些内容。这时候判断这个页面是用户还是系统,用户的话获取用户的MMSUPPORT,系统获取系统的MMSUPPORT
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
接下来定位到这个虚拟地址定位到进程具体的AREA,通过调用
MmPageOutCacheSection将这个AREA的内容全部换出磁盘。
Windows内存管理分析(四)
文章图片
先介绍SwapEntry, MMPFN结构体有一个SwapEntry,表明这个物理页面当前正在交换文件具体位置。我们看代码怎么样从一个页表项转换成一个SwapEntry
Windows内存管理分析(四)
文章图片
实际上就是页表项右移1位。SwapEntry如果是MM_WAIT_ENTRY 代表它正在被人处理中
Windows内存管理分析(四)
文章图片
页表中,如果一个页面被换出,那么页表中存着的是一个SSE。我们来看SSE是什么
Windows内存管理分析(四)
文章图片
实际上就是物理页号左移页面偏移位数(默认12位)。
Windows内存管理分析(四)
文章图片
第1个比特位记录着它是否是一个交换文件
Windows内存管理分析(四)
文章图片
并且第2个比特位记录是否是脏的SSE
判断一个页表项是交换文件
Windows内存管理分析(四)
文章图片
可知是 PA_PRESENT位为0,并且0x800的比特位为1时(这个比特位ia32并没有进行使用)
Windows内存管理分析(四)
文章图片
回到代码中,这个函数判断这个页表项是否正在被他人处理中,如果是就返回SUCCESS+1让他人等待,如果不是解除了对应物理页面MMPFN项的RmapListHead中关于参数给定MMSUPPORT(可定位到进程)的映射,然后再取消该进程对于此页面的映射(将有效位置位0),紧接着MmReleasePageMemoryConsumer递减引用计数,如果引用计数变为0就会释放这个物理页面挂入空闲链表。这样的话就完成了这个进程页面取消映射,然后代码基本上就是循环遍历,不再讲述。我们回到MmpPageOutPhysicalAddress的代码。
如果返回值是SUCCESS+1,代表刚才的倒换项已经被他人在处理。直接返回失败
如果正常的话循环执行MmPageOutCacheSection后就已经将所有关于映射到此物理页面的AREA全部取消映射。然后进入
Windows内存管理分析(四)
文章图片
如果有SEGMENT的存着,调用MmFinalizeSectionPageOut将Section换出,换出后设置SEGMENT的页表为对应的SSE。我们分析这个函数MmFinalizeSectionPageOut
Windows内存管理分析(四)
文章图片
取出这个物理页号对应的交换项
Windows内存管理分析(四)
文章图片
这里判断了是否是脏页面,如果是脏页面,为了保持磁盘和内存数据一致,我们要将其写回磁盘
Windows内存管理分析(四)
文章图片
写回磁盘的函数先对这个页面做临时的映射,因为这里是Section的换出,首先是根据文件对象,和文件当前的偏移然后调用IO函数写到磁盘,这里是文件管理的内容,我们不做分析。
这样就完成了写入磁盘。
Windows内存管理分析(四)
文章图片
通过MAKE_SWAP_SSE 把一个SWAP转换成一个SSE(实际上就是左移1位),因为如果是页面文件,写入页表的不应该是SWAPENTRY,而是SSE。
如果成功就应该把物理页号对应的MMPFN项的swapentry设置成0,因为它等下就要被释放了,它现在是全新的,如果还是原来的值的话,这个页面被申请的时候,可能就和原有页面文件起冲突。
递减SEGMENT的引用计数之后判断SEGMENT的引用计数是否为0,是的话调用MmFinalizeSegment,结束这个Segment的生命。我们来学习怎么结束一个SEGMENT的生命周期
Windows内存管理分析(四)
文章图片
将本SECTION_SEGMENT从链表中删除,接下来如果这个是一个DATA_SEGMENT,就将SEGEMENT的文件对象进行相关设置,通过调用
Windows内存管理分析(四)
文章图片
调用参数FreePage将页表内所有的页面释放 参数FreePage的值是MiFreeSegmentPage,代表每次释放一个页
Windows内存管理分析(四)
文章图片
根据FileOffset下标来获取对应的entry (SSE),如果当前页面并没有对应倒换文件,继续判断是不是一个脏的SSE,如果是就要写回页面文件。然后设置这个页表项为0,递减该物理内存页PFN引用计数。如果页面对应了倒换文件调用
MmFreeSwapPage将这个倒换页面释放
Windows内存管理分析(四)
文章图片
Windows内存管理分析(四)
文章图片
从宏定义的认识我么也可以看到低4位定位出PagingFileList的下标,高21位(32-11)定位出AllocMap的具体位置。我们来分析一下页面文件是怎么实现的。
Windows内存管理分析(四)
文章图片
这个结构体代表一个页面文件,PagingFileListEntry把所有页面文件串联在一起,FileObject表示具体的文件对象,MaximumSize代表这个页面文件最大支持多少字节,CurrentSize代表当前用了多少字节,FreePages为还可以使用多少页,UsedPage为当前使用了多少页,AllocMap为数组,是文件的内容。
Windows内存管理分析(四)
文章图片
PagingFileList 是PAGINGFILE指针数组 一个16个元素
Windows内存管理分析(四)
文章图片
申请页面文件就会从这个数组中进行申请。释放和申请都比较简单,就不再讨论
这里代码深入学习的层次比较多,我们跳回最上面一层,MmPageOutPhysicalAddress对SECTION_VIEW情况的处理,上面讨论的情况是AREA_CACHE的情况。
Windows内存管理分析(四)
文章图片
调用MmSetPageEntrySectionSegme将对应页表项的内容设置成MM_WAIT_ENTRY<<1,表明这一项已经被正在被换出,然后调用MmPageOutSectionView进行实际上的换出操作。
Windows内存管理分析(四)
文章图片
是页面换出上下文,在这里面填好信息方便进行使用,填完上下文信息后,调用
Windows内存管理分析(四)
文章图片
删除所有物理页面相关映射,调用MmPageOutDeleteMapping将页面换出
Windows内存管理分析(四)
文章图片
总结来说,对一个物理页面的换出过程,首先是寻找到对这个页面所有映射的进程,对这个地址进行一一的取消映射,当取消映射完毕后,会判断这个页面是否为脏页面,如果是脏页面就写回磁盘,然后将这个物理页面挂入空闲链表(通过引用计数),完成了物理页面的释放。
[原创]Windows内存管理分析-『编程技术』-看雪安全论坛
weixin.qq.com/r/M3W7oxbE-0uArVLA9yAh (二维码自动识别)

    推荐阅读