1.每个进程都有自己的4Gb的线性地址空间,只有低2G是私有的。
注:空指针赋值区和64KB禁入分区正常编程肯定是不可访问的,因为内存没有分配(没有挂物理页,可以自己给它挂物理页进行访问)。
2.这么多线性地址肯定需要记录哪些线性地址分配了,哪些没分配。内核空间中会用一个链表将已经使用的线性地址串起来(链表查询效率低);用户空间中是通过一个搜索二叉树(查询性能好)来管理用户空间的线性地址。
使用virtualalloca申请内存时,首先会查该内存是否已经被分配了,如果已经被分配了,那肯定会申请失败。
3.使用windbg查看用户空间的搜索二叉树
(1)搭建双机调试环境
(2)查看被调试机器上正在运行的进程(!process 0 0命令)
(3)每个进程都有一个对应的结构体叫_EPROCESS,使用命令(dt _EPROCESS 进程id)查看该结构的数据。在0x11c的位置有一个成员VadRoot(类型是_MMVAD,可以通过dt命令查看该结构),是一个搜索二叉树的入口点,该树的每个节点记录了一块被占用的线性地址空间.
(4)_MMVAD结构中StartingVpn和EndingVpn(以4kb为单位,即补3个0)记录了当前节点所描述的线性地址的起始位置和结束位置已经被占用了。
(5)_MMVAD中的_CONTROL_AREA中的filepointer为空,说明该节点对应的是真正的物理页;否则对应的是一个_FILE_OBJECT结构体。
(6)所有内存只有两类,一类是通过virtualalloc分配的内存,另一类是通过map映射的内存(可能是文件、dll、exe)。virtualalloc分配的内存为private memory,线性地址独享物理页。
4.两类内存
1.private memory:通过virtualalloc/virtualallocex分配的内存,线性地址独享物理页。reserved属性表示当前只分配线性地址,不挂物理页;commit属性表示既分配线性地址又分配物理页。在c/c++编程中通过malloc/new分配内存时,其实并没有真的分配内存,进程启动时,操作系统会通过virtualalloc申请内存,malloc/new等只会从已经分好的内存中取一块用。
2.mapped memory:通过createfilemapping分配的内存。分为两种:(1)共享物理页(2)共享文件。共享物理页时,createfilemapping只是会准备好物理页,共享文件时,createfilemapping不仅要准备好物理页,还要将物理页与文件关联。调用createfilemapping后,准备的物理页与当前进程并没有关系,需要通过调用mapviewoffile来讲物理页映射到进程空间中(即分配线性地址)。执行完mapviewoffile后,在进程的搜索二叉树中就能找到该节点了。