WinCE 编程实验(第10 章 虚拟内存)

来源:互联网 发布:ios客户端程序员招聘 编辑:程序博客网 时间:2024/06/05 10:12
 

10

虚拟内存

 

虚拟内存储存管理是现代操作系统的核心技术之一,它是整个系统高效合理地管理储存资源的基本保障,尤其是在嵌入式系统中,系统自身资源管理占的比例相对较大的情况下,就显得更为重要。在Windows CE中,除了个别对实时性能要求很高的应用程序需要关闭分页以外,大部分的应用程序都要求系统在虚拟储存上花费很大的比例。本章从系统的角度在特定的环境下跟踪虚拟内存的配置部分,因为虚拟内存配置是系统和使用者程序连接的主要渠道之一,配置的算法也在很大程度上决定了系统的性能。在分析过程中,还要对实体页的获取过程进行详细地分析,因为实体页面是系统极其珍贵的资源,对其的管理要格外小心。本章将会对在这个过程中可能出现的情况逐一分析,力图完整的向读者展示从系统的视角看到的储存管理技术。

 

10.1 配置过程概述

 

在本书的第4章初步介绍了虚拟内存的配置,下面再详细地介绍一下配置的各个具体过程(总体分析及流程图参见第4章)。虚拟内存的状态分为提交( c o m m i t t e d )和保留( r e s e r v e d ),这两部分功能都是在DoVitualAlloc中实现的。先看一下该函数的入口参数

 

LPVOID lpvAddress0, /*要保存或提交的区域地址* /

DWORD cbSize, /*区域大小* /

DWORD fdwAllocationType, /* 配置方式* /

DWORD fdwProtect, /*存取保护方式* /

 

lpvAddress0为呼叫者指定配置的虚拟内存启始地址,为零则表示由系统自动配置,cbSize为需要配置的虚拟内存大小,fdwAllocationTypefdwProtect分别为配置类型(保留或提交)和保护信息。

 

10.1.1 参数验证

 

以下参数为无效参数:

• cbSize为零。

• cbSize>32MB且没有指定虚拟内存启始地址。

无效的存取控制信息。


10.1.2 扫描虚拟内存区域找到合适大小的空闲区块

 

由于各应用程序扫描空闲区块存在互斥, 配置的虚拟内存前,要先进入虚拟内存临界区。如果需要配置的虚拟内存大于2MB,则从系统的空闲虚拟内存堆中配置,否则根据使用者指定的配置方式从第一个空闲区块或最后一个空闲区块扫描空闲的虚拟内存(MEM_TOPDOWN为从后向前扫描),若使用者指定了配置的虚拟内存启始地址,则只要简单的验证指定的虚拟内存是否都空闲即可。

 

10.1.3 在得到的虚拟内存区块中写入控制信息

 

写入的信息包括存取控制信息、虚拟内存的配置方式信息、共享使用者数(初始为1)以及该段虚存的启始地址。注意如果最后一个区块的页没有用完,要为其配置一个区块描述子,记录其为无效区块,对自动提交的页也要配置区块描述子,这样以后在提交的时候就不必再进入虚拟内存临界区了。

 

10.1.4 获取足够的实体区块并建立映像

 

在指定配置方式为提交的情况下,则跳过上两步,直接执行此步。因为如果需要映像到实体页面,所有的配置都是分两次呼叫DoVirtualAlloc的,第一次保留,第二次映射。所以,这一步也可以看作前两步的延续。在此过程中,先要在系统堆中建立这段区域的页表,最关键的步骤是向系统提交对实体页的申请,得到使用指定大小的实体页的授权,这就有点像盖房子要先申请地皮,申请下来以后再怎么盖由自己决定,当然可以空在那里不用。而前面的对虚拟内存的保留则是单单得到了对地皮使用的认可,并没有真正拿到这块地方(当然,认可也是有限的)。

 

10.1.5 小实验:虚拟内存配置的直观印象

 

Platform Builder建立一个应用程序,下载到终端机上,观察它的储存配置。在控制台的命令行下键入mi full可得到有关储存管理的全部信息,关于虚拟内存配置的部分信息摘录如下:

 

Memory usage for Process 81a9711c: 'device.exe' pid 3

Slot base 08000000 Section ptr 83fca000

08000000(1): -----r----------

08010000(0): -CCCCCc

08020000(0): ------------SSSS

08030000(0): WWWWWWWWWWWWWWWW

08040000(0): WWWWWWWWWWWWWWWW

08050000(0): WWWWWWWWWWWWWWW

08060000(0): --------------SS

08070000(0): ---------------S

08080000(0): ---------------S

08090000(0): -------------SSS

080a0000(0): ---------------S

080b0000(0): ---------------S

080c0000(0): --------------SS

080d0000(0): ---------------S

080e0000(0): ---------------S

080f0000(0): ---------------S

08100000(0): -------------SSS

08110000(0): -------------SSS

08120000(0): ---------------S

08140000(0): ---------------S

08150000(0): ---------------S

08160000(0): --------------SS

08170000(0): WW--------------

08190000(0): ---------------

081a0000(0): ---------------S

081b0000(0): W---------------

081d0000(0): ---------------

081e0000(0): ---------------S

081f0000(0): WWWWWWWWWWWWWWWW

08200000(0): W---------------

08210000(0): ---------------

08220000(0): WWWWWWWWWWWWWWWW

08230000(0): WWWWWWWWWWWWWWWW

08240000(0): WWWWWWWWWWWWWWW

08250000(0): WWWW------------

08340000(0): ----------

08350000(0): ---------------S

08360000(0): ---------------S

08370000(0): ---------------S

08390000(0): ---------------S

083a0000(0): ---------------S

083b0000(0): ---------------S

083c0000(0): ---------------S

083d0000(0): ---------------S

083e0000(0): -------SSSSSSSSS

083f0000(0): ---------------S

08400000(0): ---------------S

08420000(0): ---------------S

08430000(0): WWWWWWWWW-------

08450000(0): ---------------

08460000(0): ---------------S

09e70000(0): ------W-W-------

09e80000(0): ----------------

09e90000(0): -------------W--

09ea0000(0): ----W-W---------

09f50000(0): -----WW---------

09f60000(0): ----------W-----

09f70000(0): ----W-----W-W---

09f80000(0): W---W-W--W-W-W-W

09f90000(0): WWWWWWW-W-W-----

09fa0000(0): ----W-W---W-W-W-

09fb0000(0): W-W-W-W-W-W-W-W-

09fc0000(0): ----W-W-W-W-W-W-

09fd0000(0): W-W-W-W-WW-W-W-W

09fe0000(0): ---------WWWWW-W

09ff0000(0): ---W-------W-W-W

Page summary: code=6(1) data r/o=0 r/w=193 stack=52 reserved=1216

 

这是系统分配给行程device.exe的虚拟内存储存槽(Slot),可以看出每个Slot的第一行(每行1 6个页)用来存放该Slot的信息,堆栈都是在每行末尾向前增长,经过几次分配回收,Slot中出现了一些碎片。在该例子中,最长的一段虚拟内存是09e70000~09ff0000384页。

 

10.2 物理内存的获取

 

10.2.1 分配过程

 

下面详细地分析一下系统在调度实际物理内存时的行为,之所以选择这部分做详细地分析,是因为它是整个系统对公共资源进行分配的底层行为,从系统作为资源管理者的角度,可以从宏观上把握控制各微观行为之间的关系,可以很好地呈现系统的整体性,以及各部分之间的交互作用。而且这部分虽然重要,却只有短短的100多行原始码,因为系统把对物理内存的分配简化成对一个全局变量(PageFreeCount)的切割,很好地实现了系统设计的模块化想法。原始码中几个关于控制的阈值的选取也是经过深思熟虑的,可以帮助理解系统设计者在各个方面的权衡,也能帮助理解嵌入式系统对性能的要求与通用系统的差异。

下面对Holdpages函数这段原始码做详细分析,这部分原始码定义在[CEROOT]private/winceos/coreos/nk/kernel/physmem.c中,原始码的流程图参见第4四章图4 - 11

 

0684 BOOL

0685 HoldPages(

0686          int cpNeed,

0687          BOOL bForce

0688          )

0689 {

0690          BOOL fSetGweOomEvent = FALSE;

0691          BOOL bRet = FALSE;

0692          WORD prio;

0693          EnterPhysCS();

0694          // Check if this request will drop the page free count below the

0695          // page out trigger and signal the clean up thread to start doing

0696          // pageouts if so.

0697          if ((cpNeed+PageOutTrigger > PageFreeCount) &&

0698                   (!PageOutNeeded || (PagedInCount > PAGE_OUT_TRIGGER))) {

0699                   PageOutNeeded = 1;

0700                   PagedInCount = 0;

0701                   if (prio = GET_CPRIO(pCurThread))

0702                            prio--;

0703                   if (prio < GET_CPRIO(pCleanupThread))

0704                            KCall((PKFN)SetThreadBasePrio, pCleanupThread->hTh, (DWORD)prio);

0705                   SetEvent(hAlarmThreadWakeup);

0706          }

 

这段原始码首先进入物理内存临界区,检测内存分配是否低于一定值,来决定是否要启动换页行程,将部分实体页换出。具体表现是将PCleanupThread的优先级设置为比目前行程略低,这样就可以在目前行程执行完毕之后马上启动。请注意,PageOutNeededPagedInCountPageOutTrigger都是与schedule.cloader.c通信的变量,其值和系统当时的执行状态密切相关。

 

0707 do {

0708          if (cpNeed+GwesLowThreshold <= PageFreeCount) {

0709            DWORD pfc, pfc2;

0710            do {

0711                   pfc = PageFreeCount;

0712          } while (InterlockedTestExchange(&PageFreeCount,pfc,pfc-cpNeed) != (LONG) pfc);

0718            pfc -= cpNeed;

0719            do {

0720                   pfc2 = KInfoTable[KINX_MINPAGEFREE];

0721            } while ((pfc2 > pfc)

0722                   && (InterlockedTestExchange ((PLONG)&KInfoTable [KINX_MINPAGEFREE], pfc2,

pfc) != (LONG)pfc2));

0723            LogPhysicalPages(PageFreeCount);

0724            bRet = TRUE;

0725            goto hpDone;

0726          }

0727 } while (ScavengeStacks(cpNeed+GwesLowThreshold));

 

第二层阈值,如果超出则清空一些无用行程占用的堆栈,注意在这个过程中会修改PageFreeCount的值,因为如果有其它行程释放物理内存则会增加这个值,所以修改过程必须是不可分割的执行,否则可能会出现不一致,造成以后的增加无效。修改系统的核心数据结构KInfoTable中的闲置页项的时候同样是这个道理,请读者仔细体会这段原始码为什么要进行两次不可分割的执行,以及执行顺序。对协调系统工作的临界变量的使用有个初步的认识。

 

0729 // Even after scavenging stacks, we were unable to satisfy the request

0730          // without going below the GWE low threshold.

0731

0732          // Do not allow a request of size GwesLowBlockSize to succeed if

0733          // doing so would leave less than the low threshold. Same with

0734          // GwesCriticalBlockSize and GwesCriticalThreshold.

0735          if (bForce || !((cpNeed > GwesLowBlockSize

0736                   && cpNeed+GwesLowThreshold > PageFreeCount)

0737                   || (cpNeed > GwesCriticalBlockSize

0738                   && cpNeed + GwesCriticalThreshold > PageFreeCount))) {

0739

 

第三层阈值,如果清空行程堆栈之后仍然不能满足条件,则要启动这层阈值,由于此时实体资源已经接近枯竭,所以除了要预留部分物理内存维持系统运行以外,还要限制每次分配的内存大小,由GwesLowBlockSize限定。分配之后向GWE系统发出内存资源不足的警告。

 

0740          // Memory is low. Notify GWE, so that GWE can ask

0741          // the user to close some apps.

0742          if (GwesOOMEvent &&

0743                   ((PageFreeCount >= GwesLowThreshold) ||

0744                   ((PageFreeCount < cpNeed + GwesCriticalThreshold))))

0745            fSetGweOomEvent = TRUE;

0746

0747          if ((cpNeed + (bForce?0:STACK_RESERVE)) <= PageFreeCount) {

0748            DWORD pfc, pfc2;

0749            do {

0750                   pfc = PageFreeCount;

0751            } while (InterlockedTestExchange(&PageFreeCount,pfc,pfc-cpNeed) != (LONG)pfc);

0757          pfc -= cpNeed;

0758          do {

0759            pfc2 = KInfoTable[KINX_MINPAGEFREE];

0760          } while ((pfc2 > pfc)

0761          && (InterlockedTestExchange ((PLONG)&KInfoTable[KINX_MINPAGEFREE], pfc2,

pfc) != (LONG)pfc2));

0762            LogPhysicalPages(PageFreeCount);

0763            bRet = TRUE;

0764          }

0765 }

0766 hpDone:

0767          LeaveCriticalSection(&PhysCS);

0768          if (fSetGweOomEvent)

0769            SetEvent(GwesOOMEvent);

0770          return bRet;

0771 }

本节详细分析了HoldPages函数的原始码,在对物理内存的配置上,总体上分为三层阈值,这种层次结构可以较好地满足各种不同层次的需要,使得系统在时间和空间的总体效率比较好,较多地使用了系统的全域环境变量,能灵活地适应系统在不同状态下的需求。

 

10.2.2 小实验:HoldPages函数跟踪

 

Platform Builder中配置一个应用程序,建立其debug版本,在HoldPages函数入口设置断点,可以跟踪其执行,由于在debug模式下无法仿真多行程协调工作的环境,只能简略的看看这个函数的工作过程。

 

10-1 察看变数

 

这是在程序启动入口处设置断点时的变量值,由于系统刚刚启动,各阈值还都是0,由于Platform Builder的命名关联的原因,有些变量的值不能直接观察到,可以在汇编码中找到其地址,在除错窗口中直接读取内存的值。察看特定地址的内容如图1 0 - 2所示。

 

10-2 察看特定地址的内容

 

从上图可以看出,PageFreeCount的值是0x00000c26,即3100个空闲页,大约有12M的空闲物理内存,跟实际试验机的空闲内存数相仿,接下来的除错工作就变得相对简单,可以设置变量cpNeed的值来仿真各种实用条件。也可以通过在进入临界区后改变PageFreeCount的值(可以在Memory 1窗口中直接进行修改)来做一致性检测。这部分工作留给读者自己去完成。

 

10.3 虚拟内存配置原始码片段

 

这部分的流程和提交部分的原始码分析在第4章已经给了,读者可再对照上下文读一下这段原始码,另外作者在认为需要的地方适当地增加了一些中文注释,为了节约篇幅,有些常数、变量的定义和一些简单的英文注释、除错信息、系统日志信息也略去。原始码定义在private/winceos/coreos/nk/kernel/virmem.c中。

 

0645 LPVOID

0646 DoVirtualAlloc(

0647          LPVOID lpvAddress0, /* address of region to reserve or commit */

0648          DWORD cbSize, /* size of the region */

0649          DWORD fdwAllocationType, /* type of allocation */

0650          DWORD fdwProtect, /* type of access protection */

0651          DWORD fdwInternal,

0652          DWORD dwPFNBase

0653 )

0654 {

0681          if (IsKernelVa (dwAddr) // invalid address?

0682            || !cbSize // size == 0?

0683            || ((cbSize >= (1<<VA_SECTION)) // size too big? (okay to reserver 32M with specific parameters)

0684            && (dwSlot0Addr || (fdwAllocationType != MEM_RESERVE) || (fdwProtect !=

PAGE_NOACCESS)))

0685            || !(ulPgMask = MakePagePerms(fdwProtect))) { // invalid flag?

0686                   KSetLastError(pCurThread, ERROR_INVALID_PARAMETER);

0687                   return NULL;

0688 }

 

这段原始码进行了参数验证,其原始码结构安排比较巧妙,将最经常发生的或比较时间最短的非法情况放在前面比较,可以让总合的平均比较时间最小。

 

0694 EnterCriticalSection(&VAcs);

0695

0696 if (IsSecureVa(dwAddr)) {

0697   aky = ProcArray[0].aky;

0698   pscn = &NKSection;

0699   dwBase = SECURE_VMBASE;

0700 } else {

0701   ix = dwAddr >> VA_SECTION;

0702   DEBUGCHK (ix <= SECTION_MASK); // can't be 0, but can be NULL_SECTION

0703   if ((pscn = SectionTable[ix]) == NULL_SECTION)

0704          goto invalidParm;

0705          dwBase = dwAddr & (SECTION_MASK<<VA_SECTION);

0706          // Make sure that when remotely allocating memory in another process that

0707          // we use the access key for the remote process instead of the current one.

0708          if (dwAddr < FIRST_MAPPER_ADDRESS) {

0709            if (ix && ix <= MAX_PROCESSES && ProcArray[ix-1].dwVMBase)

0710                   aky = ProcArray[ix-1].aky;

0711          }

0712 }

0713

0714 if ((fdwAllocationType & MEM_RESERVE) || (!dwAddr && (fdwAllocationType &

MEM_COMMIT))) {

0715   // Set MEM_RESERVE so the error cleanup works properly.

0716   fdwAllocationType |= MEM_RESERVE;

0717

0718   ixBlock = dwSlot0Addr >> VA_BLOCK;

0719   if (dwSlot0Addr) {

0720          /* The client has asked to reserve a specific region of memory.

0721            * Verify that the requested region is within range and within an

0722            * existing memory section that the client is allowed to access.

0723            */

0724          /* adjust lpvAddress to 64K boundary. */

0725          dwAddr &= 0xFFFF0000l;

0726          /* Verify that the entire range is available to be reserved. */

0727          cPages = (dwEnd - dwAddr + PAGE_SIZE-1) / PAGE_SIZE;

0728          for (cNeed = cPages, ix = ixBlock ; cNeed > 0

0729                   ; ++ix, cNeed -= PAGES_PER_BLOCK) {

0730            if ((*pscn)[ix] != NULL_BLOCK)

0731                   goto invalidParm;

0732            }

 

这段原始码保留指定的虚拟内存地址,只需要简单地做合法性检查,并标记虚拟内存区块为Reserved,并不需要向系统申请虚拟内存地址。

 

0733          } else {

0734            /* No specific address given. Go find a region of free blocks */

0735            cPages = (dwEnd - dwAddr + PAGE_SIZE-1) / PAGE_SIZE;

0736            cNeed = cPages;

0737          if (((cPages * PAGE_SIZE >= 2*1024*1024) || (fdwInternal & MEM_SHAREDONLY))

0738            && (fdwAllocationType == MEM_RESERVE)

0739            && (fdwProtect == PAGE_NOACCESS)) {

0740                   DEBUGMSG(ZONE_VIRTMEM, (TEXT("VirtualAlloc(%8.8lx, %lx, %lx, %lx):

doing HugeVirtualAlloc/r/n"),

0741                   lpvAddress0, cbSize, fdwAllocationType, fdwProtect));

0742            LeaveCriticalSection(&VAcs);

0743            lpvResult = HugeVirtualReserve(cbSize, (fdwInternal & MEM_SHAREDONLY)

? MEM_SHAREDONLY : 0);

0744            if (IsCeLogEnabled()) {

0745                   CELOG_VirtualAlloc((DWORD) lpvResult,(DWORD) dwAddr, cbSize, fdwAllocationType, fdwProtect);

0746            }

0747            return lpvResult;

0748            }

0749            if (fdwAllocationType & MEM_TOP_DOWN) {

0750                   /* Scan backwards from the end of the section */

0751                   for (ix = BLOCK_MASK+1 ; --ix > 0 ; ) {

0752                     if ((*pscn)[ix] != NULL_BLOCK)

0753                            cNeed = cPages;

0754                     else if ((cNeed -= PAGES_PER_BLOCK) <= 0) {

0755                            ixBlock = ix;

0756                            goto foundRegion;

0757                     }

0758                   }

0759            } else {

0760                   /* Scan forwards from the beginning of the section */

0761                   ixBlock = 1;

0762                   for (ix = 1 ; ix < BLOCK_MASK+1 ; ++ix) {

0763                     if ((*pscn)[ix] != NULL_BLOCK) {

0764                            ixBlock = ix+1;

0765                            cNeed = cPages;

0766                     } else if ((cNeed -= PAGES_PER_BLOCK) <= 0)

0767                            goto foundRegion;

0768                     }

0769                   }

0770                   err = ERROR_NOT_ENOUGH_MEMORY;

0771                   goto setError;

0772 foundRegion:

0773                   dwAddr = dwBase | ((ulong)ixBlock << VA_BLOCK);

0774          }

0775          /* Reserve the range of blocks */

0776          if (!(pmb = AllocMem(HEAP_MEMBLOCK))) {

0777          err = ERROR_NOT_ENOUGH_MEMORY;

0778          goto setError;

0779          }

0780          memset(pmb,0,sizeof(MEMBLOCK));

0781          LockFromKey(&pmb->alk, &aky);

0782

0783          DEBUGMSG(ZONE_VIRTMEM, (TEXT("VirtualAlloc: reserved lock=%lx/r/n"),

pmb->alk));

0784          pmb->cUses = 1;

0785          switch (fdwAllocationType & (MEM_MAPPED|MEM_IMAGE)) {

0786          case MEM_MAPPED:

0787            pmb->flags = MB_PAGER_MAPPING;

0788            break;

0789          case MEM_IMAGE:

0790            pmb->flags = MB_PAGER_LOADER;

0791          break;

0792          case 0:

0793            pmb->flags = MB_PAGER_NONE;

0794            break;

0795          default:

0796            goto invalidParm;

0797          }

0798          if (fdwAllocationType & MEM_AUTO_COMMIT)

0799            pmb->flags |= MB_FLAG_AUTOCOMMIT;

0800          pmb->ixBase = ixBlock;

0801          (*pscn)[ixBlock] = pmb;

0802          DEBUGMSG(ZONE_VIRTMEM,

0803            (TEXT("VirtualAlloc: created head block pmb=%8.8lx (%x)/r/n"),

0804            pmb, ixBlock));

0805          ixFirB = ixBlock; /* note start of region for error recovery */

0806          for (cNeed = cPages, ix = ixBlock+1 ; (cNeed -= PAGES_PER_BLOCK) > ++ix) {

0807            if (cNeed < PAGES_PER_BLOCK || (fdwAllocationType & MEM_AUTO_COMMIT)) {

0808                   /* The last block is not full so must allocate a MEMBLOCK

0809                    * and mark the extra pages as invalid or this is an auto-commit

0810                    * region which must have all memblocks filled in so that

0811                    * pages can be committed without entering the virtual memory

0812                    * critical section. */

0813                   if (!(pmb = AllocMem(HEAP_MEMBLOCK))) {

0814                     err = ERROR_NOT_ENOUGH_MEMORY;

0815                     goto setError;

0816                   }

0817                   memset(pmb,0,sizeof(MEMBLOCK));

0818                   pmb->alk = aky;

0819                   pmb->flags = (*pscn)[ixFirB]->flags;

0820                   pmb->cUses = 1;

0821                   pmb->ixBase = ixFirB;

0822                   (*pscn)[ix] = pmb;

0823                   DEBUGMSG(ZONE_VIRTMEM,

0824                     (TEXT("VirtualAlloc: created a tail block pmb=%8.8lx (%x)/r/n"),

0825                     pmb, ixBlock));

0826                   } else

0827                     (*pscn)[ix] = RESERVED_BLOCK;

0828          }

0829          DEBUGMSG(ZONE_VIRTMEM, (TEXT("Reserved %d pages @%8.8lx/r/n"), cPages,

dwAddr));

0830          if (cNeed) {

0831            /* Set unused entries to BAD_PAGE */

0832            DEBUGMSG(ZONE_VIRTMEM, (TEXT("Reserved %d extra pages./r/n"), - cNeed));

0833            cNeed += PAGES_PER_BLOCK;

0834            for (ix = cNeed ; ix < PAGES_PER_BLOCK ; ++ix)

0835                   pmb->aPages[ix] = BAD_PAGE;

0836            }

0837            /* If not committing pages, then return the address of the

0838             * reserved region */

0839            if (!(fdwAllocationType & MEM_COMMIT)) {

0840                   LeaveCriticalSection(&VAcs);

0841                   ERRORMSG(!dwAddr,(L"Failed VirtualAlloc/reserve of %8.8lx bytes/r/n",cbSize));

0842                   if (IsCeLogEnabled()) {

0843                     CELOG_VirtualAlloc(dwAddr, (DWORD) lpvAddress0, cbSize, fdwAllocationType, fdwProtect);

0844                   }

0845                   return (LPVOID) dwAddr;

0846

0847            }

 

这段原始码处理了没有指定虚拟内存地址的情况下,由系统寻找空间虚拟内存区块的情况。在这个过程中,系统还负责修改已保留的虚拟内存区块的各种信息。

 

0848          } else {

0849            //

0850            // Not reserving memory, so must be committing. Verify that the

0851            // requested region is within range and within an existing reserved

0852            // memory region that the client is allowed to access.

0853            //

0854            if (!(fdwAllocationType & MEM_COMMIT)

0855                   || !dwAddr

0856 //               || (!(fdwInternal&MEM_NOSTKCHK) && IsStackVA (dwSlot0Addr | (dwBase? dwBase : pCurProc->dwVMBase), cbSize))

0857                   || (fdwInternal & MEM_CONTIGUOUS))

0858            goto invalidParm;

0859

0860            ixBlock = (dwAddr >> VA_BLOCK) & BLOCK_MASK;

0861            /* Adjust lpvAddress to PAGE boundary and calculate the number

0862             * of pages to commit. */

0863            dwAddr &= ~(PAGE_SIZE-1);

0864            cPages = (dwEnd - dwAddr + PAGE_SIZE-1) / PAGE_SIZE;

0865            /* locate the starting block of the region */

0866            if ((ixFirB = FindFirstBlock(pscn, ixBlock)) == UNKNOWN_BASE)

0867                   goto invalidParm;

0868          }

0869          /* Verify that cPages of memory starting with the first page indicated by

0870           * lpvAddress can be committed within the region.

0871           *

0872           * (pscn) = ptr to section array

0873           * (ixBlock) = index of block containing the first page to commit

0874           * (cPages) = # of pages to commit

0875           */

0876          ixPage = (dwAddr >> VA_PAGE) & PAGE_MASK;

0877          cpAlloc = ScanRegion(pscn, ixFirB, ixBlock, ixPage, cPages, 0);

0878          if (cpAlloc == -1)

0879            goto cleanUp;

0880          //

0881          // Commit cPages of memory starting with the first page indicated by

0882          // lpvAddress. Allocate all required pages before any changes to the

0883          // virtual region are performed.

0884          //

0885          // (pscn) = ptr to section array

0886          // (ixBlock) = index of block containing the first page to commit

0887          // (cPages) = # of pages to commit

0888          // (cpAlloc) = # of physical pages required

0889          //

0890          DEBUGMSG(ZONE_VIRTMEM, (TEXT("Allocating %d pages./r/n"), cpAlloc));

0891

0892          if (fdwInternal & MEM_CONTIGUOUS) {

0893            //

0894            // Map to physically contiguous pages. Mark as LOCKED as we go.

0895            // Walk the page tables to map in the physical pages.

0896            //

0897            ULONG ulPFN = dwPFNBase;

0898

0899            for (; cPages ; ixBlock++) {

0900                   pmb = (*pscn)[ixBlock];

0901                   pmb->cLocks++;

0902                   ix = 0;

0903                   for (; cPages && ix < PAGES_PER_BLOCK; --cPages) {

0904                     DEBUGCHK(pmb->aPages[ix] == 0);

0905                     pmb->aPages[ix] = ulPFN | ulPgMask;

0906                     ulPFN = NextPFN(ulPFN);

0907                     ix++;

0908                   }

0909            }

 

指定物理内存地址的映像,系统只需要验证有效性并填写映像信息,不必抓取实体页。

 

0910          } else {

0911            if (!HoldPages(cpAlloc, FALSE)) {

0912                   err = ERROR_NOT_ENOUGH_MEMORY;

0913                   goto setError;

0914            }

0915            ixBlock = (ixBlock*PAGES_PER_BLOCK+ixPage+cPages-1)/PAGES_PER_BLOCK;

0916            ix = ((ixPage + cPages - 1) % PAGES_PER_BLOCK) + 1;

0917            //

0918            // Walk the page tables to map in the physical pages.

0919            //

0920            for (; cPages ; --ixBlock) {

0921                   pmb = (*pscn)[ixBlock];

0922                   for (; cPages && ix-- > 0 ; --cPages) {

0923                     if (pmb->aPages[ix] == 0) {

0924                            DWORD dwRetries;

0925                            for(dwRetries=0; (dwRetries < 20) && !(ulPFN = GetHeldPage()); dwRetries++)

0926                              Sleep(100);

0927                            if (ulPFN) {

0928                              DEBUGMSG(ZONE_VIRTMEM, (TEXT("Mapping %8.8lx @%3.3x%x000 perm=%x/r/n"),

0929                                      ulPFN, ixBlock, ix, ulPgMask));

0930                              pmb->aPages[ix] = ulPFN | ulPgMask;

0931                            } else {

0932                              InterlockedIncrement(&PageFreeCount);

0933                              RETAILMSG(1,(L"--->>> VirtualAlloc: FATAL ERROR! COMPLETELY OUT OF MEMORY (%8.8lx)!/r/n",PageFreeCount));

0934                            }

0935                     } else

0936                            pmb->aPages[ix] = (pmb->aPages[ix] & ~PG_PERMISSION_MASK) | ulPgMask;

0937                   }

0938                   ix=PAGES_PER_BLOCK; /*start with last page of block */

0939            }

0940          }

0941          DEBUGMSG(ZONE_VIRTMEM, (TEXT("Calling InvalidateRange (%8.8lx, %d)/n"), dwAddr, cbSize));

0942          InvalidateRange((LPVOID) dwAddr, cbSize); // in case we changed permissionsabove

0943          LeaveCriticalSection(&VAcs);

0944          ERRORMSG(!dwAddr, ( L"FailedVirtualAlloc(%8.8lx) of %8.8lx bytes/r/n", fdwAllocationType, cbSize));

0945

0946          if (IsCeLogEnabled()) {

0947            CELOG_VirtualAlloc(dwAddr, dwAddr, cbSize, fdwAllocationType, fdwProtect);

0948          }

0949          DEBUGMSG(ZONE_VIRTMEM, (TEXT("Returing %8.8lx/n"), dwAddr));

0950          return (LPVOID) dwAddr;

0951

0952          //

0953          // There was an error reserving or commiting a range of pages. If reserving

0954          // pages, release any pages which were reserved before the failure occured.

0955          //

0956 invalidParm:

0957          err = ERROR_INVALID_PARAMETER;

0958 setError:

0959          DEBUGMSG(ZONE_VIRTMEM, (TEXT("VirtualAlloc failed. error=%d/r/n"), err));

0960          KSetLastError(pCurThread, err);

0961 cleanUp:

0962          if (fdwAllocationType & MEM_RESERVE && ixFirB != UNKNOWN_BASE) {

0963            /* Remove the reservation */

0964            ReleaseRegion(pscn, ixFirB);

0965          }

0966          LeaveCriticalSection(&VAcs);

0967          ERRORMSG(!dwAddr, (L"Failed VirtualAlloc(%8.8lx) of %8.8lx bytes/r/n", fdwAllocationType, cbSize));

0968          return 0;

0969 }

 

这段原始码要向系统申请物理内存资源,并建立实体地址和虚拟内存地址的映像,并提供了整个操作的不可分割性保证,在出现错误的情况下支持回朔。

 

原创粉丝点击