The memory manager uses a demand-paging algorithm to know when to load pages into memory, waiting until a thread references an address and incurs a page fault before retrieving the page from disk. Like copy-on-write, demand paging is a form of lazy evaluation—waiting to perform a task until it is required.
The memory manager uses lazy evaluation not only to bring pages into memory but also to construct the page tables required to describe new pages. For example, when a thread commits a large region of virtual memory with VirtualAlloc, the memory manager could immediately construct the page tables required to access the entire range of allocated memory. But what if some of that range is never accessed? Creating page tables for the entire range would be a wasted effort. Instead, the memory manager waits to create a page table until a thread incurs a page fault, and then it creates a page table for that page. This method significantly improves performance for processes that reserve and/or commit a lot of memory but access it sparsely.
With the lazy-evaluation algorithm, allocating even large blocks of memory is a fast operation. This performance gain isn't without its trade-offs, however: when a thread allocates memory, the memory manager must respond with a range of addresses for the thread to use. Because the memory manager doesn't build page tables until the thread actually accesses the memory, it can't look to determine which virtual addresses are free. To solve this problem, the memory manager maintains another set of data structures to keep track of which virtual addresses have been reserved in the process's address space and which have not. These data structures are known as virtual address descriptors (VADs). For each process, the memory manager maintains a set of VADs that describes the status of the process's address space. VADs are structured as a self-balancing binary tree to make lookups efficient. A diagram of a VAD tree is shown in Figure 7-18.
Figure 7-18 Virtual address descriptors
When a process reserves address space or maps a view of a section, the memory manager creates a VAD to store any information supplied by the allocation request, such as the range of addresses being reserved, whether the range will be shared or private, whether a child process can inherit the contents of the range, and the page protection applied to pages in the range.
When a thread first accesses an address, the memory manager must create a PTE for the page containing the address. To do so, it finds the VAD whose address range contains the accessed address and uses the information it finds to fill in the PTE. If the address falls outside the range covered by the VAD or in a range of addresses that are reserved but not committed, the memory manager knows that the thread didn't allocate the memory before attempting to use it and therefore generates an access violation.
EXPERIMENT
Viewing Virtual Address DescriptorsYou can use the kernel debugger's !vad command to view the VADs for a given process. First find the address of the root of the VAD tree with the !process command. Then specify that address to the !vad command, as shown in the following example of the VAD tree for a process running Notepad.exe:
kd>á!processá2a0á1 SearchingáforáProcessáwitháCidá==á2a0 PROCESSá8614d030áSessionId:á0áCid:á02a0áPeb:á7ffdf000áParentCid:á0554 ááááDirBase:á00d93000ááObjectTable:á81bc47c8ááTableSize:áá41. ááááImage:ánotepad.exe ááááVadRootá8118d868áCloneá0áPrivateá252.áModifiedá0.áLockedá0. áááá kd>á!vadá8118d868 VADáááááleveláááááástartááááááendáááácommit 84df4148á(á2)ááááááááá10ááááááá10ááááááááá1áPrivateááááááREADWRITE 850cdbe8á(á3)ááááááááá20ááááááá20ááááááááá1áPrivateááááááREADWRITE 810b0ee8á(á1)ááááááááá30ááááááá6fááááááááá7áPrivateááááááREADWRITE 8109d308á(á3)ááááááááá70áááááá16fáááááááá32áPrivateááááááREADWRITE 810e9a28á(á2)áááááááá170áááááá17fááááááááá0áMappedáááááááREADWRITE 84aedfc8á(á3)áááááááá180áááááá195ááááááááá0áMappedáááááááREADONLY 8118d868á(á0)áááááááá1a0áááááá1ceááááááááá0áMappedáááááááREADONLY 81190a08á(á4)áááááááá1d0áááááá210ááááááááá0áMappedáááááááREADONLY 85c7b928á(á3)áááááááá220áááááá223ááááááááá0áMappedáááááááREADONLY 86253a08á(á4)áááááááá230áááááá2f7ááááááááá0áMappedáááááááEXECUTE_READ 810aab48á(á2)áááááááá300áááááá342ááááááááá0áMappedáááááááREADONLY 80db5448á(á5)áááááááá350áááááá64fááááááááá0áMappedáááááááEXECUTE_READ TotaláVADs:áááá49ááaverageálevel:áááá6áámaximumádepth:á13