System Memory Pools

 < Day Day Up > 

At system initialization, the memory manager creates two types of dynamically sized memory pools that the kernel-mode components use to allocate system memory:

  • Nonpaged pool Consists of ranges of system virtual addresses that are guaranteed to reside in physical memory at all times and thus can be accessed at any time (from any IRQL level and from any process context) without incurring a page fault. One of the reasons nonpaged pool is required is because of the rule described in Chapter 2: page faults can't be satisfied at DPC/dispatch level or above.

  • Paged pool A region of virtual memory in system space that can be paged in and out of the system. Device drivers that don't need to access the memory from DPC/dispatch level or above can use paged pool. It is accessible from any process context.

Both memory pools are located in the system part of the address space and are mapped in the virtual address space of every process. (In Table 7-8, you'll find out where in the system memory they start.) The executive provides routines to allocate and deallocate from these pools; for information on these routines, see the functions that start with ExAllocatePool in the Windows DDK documentation.

Table 7-8. System Variables That Describe System Space Regions

System Variables

Description

x86 2-GB System Space (non-PAE)

x86 1-GB System Space (non-PAE)

MmSystemRangeStart

Start address of system

space0x80000000

0xC0000000

MmSystemCacheWorkingSetList

System working set list

0xC0C00000

Calculated

MmSystemCacheStart

Start of system cache

Calculated

Calculated

MmSystemCacheEnd

End of system cache

Calculated

Calculated

MiSystemCacheStartExtra

Start of system cache or system PTE extension

Calculated

0

MiSystemCacheEndExtra

End of system cache or PTE extension

0xC0000000

0

MmPagedPoolStart

Start of paged pool

Calculated

Calculated

MmPagedPoolEnd

End of paged pool

Calculated (maximum size is 650 MB)

Calculated (minimum size is 160 MB)

MmNonPagedSystemStart

Start of system PTEs

Calculated (lowest value is 0xEB000000)

Calculated

MmNonPagedPoolStart

Start of nonpaged pool

Calculated

Calculated

MmNonPagedPoolExpansionStart

Start of nonpaged pool expansion

Calculated

Calculated

MmNonPagedPoolEnd

End of nonpaged pool

0xFFBE0000

0xFFBE0000


Uniprocessor systems have three paged pools; multiprocessor systems have five. Having more than one paged pool reduces the frequency of system code blocking on simultaneous calls to pool routines. Both nonpaged and paged pool start at an initial size based on the amount of physical memory on the system and then grow, if necessary, up to a maximum size computed at system boot time.

Note

Future releases of Windows might support dynamic pool sizes, which means the maximum size will no longer apply. Therefore, applications and device drivers should not assume that the pool size maximum is a fixed value for each system.


Configuring Pool Sizes

You can override the initial size of these pools by changing the values NonPagedPoolSize and PagedPoolSize in the registry key HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management from 0 (which causes the system to compute the size) to the size desired in bytes. You can't, however, go beyond the maximum pool sizes listed in Table 7-5. A value of 0xFFFFFFFF for PagedPoolSize indicates that the largest possible size is selected, which means allowing a larger paged pool at the expense of system page table entries (PTEs).

Table 7-5. Maximum Pool Sizes

Pool Type

Maximum on 32-Bit Systems

Maximum on 64-Bit Systems

Nonpaged

256 MB (128 MB if booted /3GB)

128 GB

Paged

491.875 MB (Windows 2000 and Windows XP); 650 MB (Windows Server 2003)

128 GB


The computed sizes are stored in four kernel variables, three of which are exposed as performance counters. These variables and counters, as well as the two registry keys that can alter the sizes, are listed in Table 7-6.

Table 7-6. System Pool Size Variables and Performance Counters

Kernel Variable

Performance Counter

Registry Key to Override

Description

MmSizeOfNonPagedPoolInBytes

Memory: Pool Nonpaged Bytes

Not applicable

Current size of nonpaged pool

MmMaximumNonPagedPoolInBytes

Not available

HKLM\SYSTEM\CurrentControlSet\ Control\Session Manager\Memory Management\NonPagedPoolSize

Maximum size of nonpaged pool

Not available

Memory: Pool Paged Bytes

Not applicable

Current virtual size of paged pool

MmPagedPoolPage (number of pages)

Memory: Pool Paged Resident Bytes

Not applicable

Current physical(resident) size of paged pool

MmSizeOfPagedPoolInBytes

Not available

HKLM\SYSTEM\CurrentControlSet\ Control\Session Manager\ Memory Management\PagedPoolSize

Maximum (virtual) size of paged pool


EXPERIMENT: Determining the Maximum Pool Sizes

Because paged and nonpaged pool represent a critical system resource, it is important to know when you're nearing the maximum size computed for your system so that you can determine whether you need to override the default maximum with the appropriate registry values. The pool-size performance counters report only the current size, however, not the maximum size. So you don't know when you're reaching the limit until you've exhausted pool. (As noted earlier, future versions of Windows might support dynamically sized pools. Therefore, the need to check the pool maximums might no longer be necessary in the future.)

You can obtain the pool maximums by using either Process Explorer or live kernel debugging (explained in Chapter 1). To view pool maximums with Process Explorer, click on View, System Information. The pool maximums are displayed in the Kernel Memory section as shown below:



Note that for Process Explorer to retrieve this information, it must have access to the symbols for the kernel running on your system. (For a description of how to configure Process Explorer to use symbols, see the experiment "Viewing Process Details with Process Explorer" in Chapter 1.)

To view the same information by using the kernel debugger, you can use the !vm command as shown below:

lkd>  !vm *** Virtual  Memory  Usage *** Physical  Memory:     261980  (1047920 Kb) Page  File:\??\C:\pagefile.sys    Current:   1024000Kb FreeSpace:     862236Kb    Minimum:   1024000Kb Maximum:      1024000Kb AvailablePages:      96077   (  384308Kb) ResAvail Pages:     194722   (  778888Kb) Locked IO Pages:        89   (     356Kb) Free System PTEs:   175933   (  703732Kb) Free NP PTEs:        31832   (  127328Kb) Free  Special NP:        0   (       0Kb) Modified Pages:        529   (    2116Kb) Modified PF Pages:     529   (    2116Kb) NonPagedPool Usage:   5617   (   22468Kb) NonPagedPool Max:    52741   (  210964Kb) PagedPool 0 Usage:    6582   (   26328Kb) PagedPool 1 Usage:    3009   (   12036Kb) PagedPool 2 Usage:    3044   (   12176Kb) PagedPool Usage:     12635   (   50540Kb) PagedPool Maximum:   40960   (  163840Kb)

On this system, nonpaged and paged pool were far from their maximums. You can also examine the values of the kernel variables listed in Table 7-6:

kd> dd mmmaximumnonpagedpoolinbytes  l1 8047f620     0328c000 kd> ? 328c000 Evaluate expression: 53002240 = 0328c000 kd> dd mmsizeofpagedpoolinbytes l1 80470a98    06800000 kd> ? 6800000 Evaluateexpression:   109051904 = 06800000

From this example, you can see that the maximum size of nonpaged pool is 53,002,240 bytes (approximately 50 MB) and the maximum size of paged pool is 109,051,904 bytes (104 MB). On the test system we used for this example, current nonpaged pool usage was 5.5 MB and paged pool usage was 34 MB, so both pools were far from full.


Monitoring Pool Usage

The Memory performance counter object has separate counters for the size of nonpaged pool and paged pool (both virtual and physical). In addition, the Poolmon utility (in the Windows Support Tools) allows you to monitor the detailed usage of nonpaged and paged pool. To do so, you must have the internal Enable Pool Tagging option enabled. Pool tagging is turned on by default as of Windows Server 2003 (in fact, it cannot be turned off) and in all checked builds of Windows. To enable pool tagging, run the Gflags utility in the Windows Support Tools, Platform SDK, or DDK, and select Enable Pool Tagging, as shown here:



Then click Apply, and reboot the system. After the system reboots, run Poolmon; you should see a display like this one:



The highlighted lines represent changes to the display. (You can disable the highlighting feature by typing l while running Poolmon. Type l again to reenable highlighting.) Type ? while Poolmon is running to bring up its help screen. You can configure which pools you want to monitor (paged, nonpaged, or both) and the sort order. Also, the command-line options are shown, which allow you to monitor specific structures (or everything but one structure type). For example, the command poolmon -iCM will monitor only structures of type CM (the configuration manager, which manages the registry). The columns have the meanings shown in Table 7-7.

Table 7-7. Poolmon Columns

Column

Explanation

Tag

Four-byte tag given to the pool allocation

Type

Pool type (paged or nonpaged pool)

Allocs

Count of all allocations (The number in parentheses shows the difference in the Allocs column since the last update.)

Frees

Count of all Frees (The number in parentheses shows the difference in the Frees column since the last update.)

Diff

Count of Allocs minus Frees

Bytes

Total bytes consumed by this structure type (The number in parentheses shows the difference in the Bytes column since the last update.)

Per Alloc

Size in bytes of a single instance of this structure type


In this example, CM structures (the configuration manager, or registry) are taking up the most space in paged pool, and MmSt structures (a memory management related structure used for mapped files) are taking up the most space in nonpaged pool.

For a description of the meaning of the pool tags used by Windows, see the file \Program Files\Debugging Tools for Windows\Triage\Pooltag.txt. (This file is installed as part of the Windows Debugging Tools described in Chapter 1.) Because third-party device driver pool tags are not listed in this file, you can use the "-c" switch on the version of Poolmon that comes with the Windows Server 2003 Device Driver Kit (DDK) to generate a local pool tag file (Localtag.txt). This file will contain pool tags used by drivers found on your system, including third-party drivers. (Note that if a device driver binary has been deleted after it was loaded, its pool tags will not be recognized.)

Alternatively, you can search the device drivers on your system for a pool tag by using the Strings.exe tool from http://www.sysinternals.com. For example, the command

strings \windows\system32\drivers\*.sys > findstr /i "abcd"

will display drivers that contain the string "abcd". Note that device drivers do not necessarily have to be located in \Windows\System32\Drivers they can be in any folder. To list the full path of all loaded drivers, click Start, click Run, and type Msinfo32. Then click on Software Environment and System Drivers. As already noted, if a device driver has been loaded and then deleted from the system, it will not be listed here.

An alternative to view pool usage by device driver is to enable the pool tracking feature of Driver Verifier, explained later in this chapter. While this makes the mapping from pool tag to device driver unnecessary, it does require a reboot (to enable Driver Verifier on the desired drivers). After rebooting with pool tracking enabled, you can either run the graphical Driver Verifier Manager (\Windows\System32\Verifier.exe) or use the Verifier /Log command to send the pool usage information to a file.

Finally, if you are looking at a crash dump, you can view pool usage with the kernel debugger !poolused command. The command !poolused 2 shows nonpaged pool usage sorted by structure tag using the most amount of pool. The command !poolused 4 lists paged pool usage, again sorted by structure tag using the most amount of pool. The following example shows the partial output from these two commands:

 lkd> !poolused 2    Sorting by  NonPaged  Pool Consumed   PoolUsed:            NonPaged             Paged  Tag    Allocs     Used     Allocs     Used  MmCm      386  2237440          0        0    Calls  madeto  MmAllocateContiguousMemory, Bin ary:nt!mm  File    12544  2121232          0        0    File objects  Devi      385  1487184          0        0    Device  objects  Ntfr    19163  1226888          0        0    ERESOURCE , Binary:  ntfs.sys kd> !poolused 4    Sorting by Paged  Pool Consumed   PoolUsed:             NonPaged             Paged  Tag    Allocs    Used     Allocs      Used  CM         11     704       5146  22311904  Gh5         0       0        727   2523648  MmSt        0       0        968   1975872  Ntff       10    2240        790    682560  Gla1        1     128        383    600544  Ttfd        0       0        265    545440

EXPERIMENT: Troubleshooting a Pool Leak

In this experiment, you will fix a real paged pool leak on your system so that you can put to use the techniques described in the previous section to track down the leak. The leak will be generated by the NotMyFault tool, which you can download from http://www.sysinternals.com/windowsinternals.shtml. (Note that this tool is not listed on the main Sysinternals tools list.) When you run NotMyFault.exe, it loads a device driver Myfault.sys and presents the following dialog box:



  1. Click the Leak Pool button. This causes NotMyFault to begin sending requests to the Myfault device driver to allocate paged pool. (Do not click the Do Bug button or you will experience a system crash; this button is used in Chapter 14 to demonstrate various types of crashes.) NotMyFault will continue to do this until you click the Stop Leaking button. Note that the paged pool is not released even when you close the program; the pool is permanently leaked until you reboot the system. However, because you will leak pool only for a short period, this should not cause any problems on your system.

  2. While the pool is leaking, first open Task Manager and click on the Performance tab. You should notice Paged Pool climbing. You can also check this with Process Explorer's System Information display. (Click on Show and then System Information.)

  3. To determine the pool tag that is leaking, run Poolmon and press the "b" key to sort by the number of bytes. Press "p" twice so that Poolmon is showing only paged pool. You should notice the pool tag "Leak" climbing to the top of the list. (Poolmon shows changes to pool allocations by highlighting the lines that change.)

  4. Now press the "Stop Leaking" button so that you don't exhaust paged pool on your system.

  5. Using the technique described in the previous section, run Strings (from http://www.sysinternals.com) to look for driver binaries that contain the pool tag "Leak":

    Strings \windows\system32\drivers\*.sys | findstr Leak

This should display a match on the file Myfault.sys, thus confirming it as the driver using the "Leak" pool tag.


Look-Aside Lists

Windows also provides a fast memory allocation mechanism called look-aside lists. The basic difference between pools and look-aside lists is that while general pool allocations can vary in size, a look-aside list contains only fixed-sized blocks. Although the general pools are more flexible in terms of what they can supply, look-aside lists are faster because they don't use any spinlocks and also because the system doesn't have to search for free memory that fits a varying size allocation.

Executive components and device drivers can create look-aside lists that match the size of frequently allocated data structures by using the ExInitializeNPagedLookasideList and ExInitializePagedLookasideList functions (documented in the DDK). To minimize the overhead of multiprocessor synchronization, several executive subsystems (such as the I/O manager, cache manager, and object manager) create separate look-aside lists for each processor for their frequently accessed data structures. The executive also creates a general per-processor paged and nonpaged look-aside list for small allocations (256 bytes or less).

If a look-aside list is empty (as it is when it's first created), the system must allocate from paged or nonpaged pool. But if it contains a freed structure, the allocation can be satisfied very quickly. (The list grows as structures are returned to it.) The pool allocation routines automatically tune the number of freed buffers that look-aside lists store according to how often a device driver or executive subsystem allocates from the list the more frequent the allocations, the more buffers are stored on a list. Look-aside lists are automatically reduced in size if they aren't being allocated from. (This check happens once per second when the balance set manager system thread wakes up and calls the function KiAdjustLookasideDepth.)

EXPERIMENT: Viewing the System Look-Aside Lists

You can display the contents and sizes of the various system look-aside lists with the kernel debugger !lookaside command. The following excerpt is from the output of this command:

kd> !lookaside Lookaside "nt!IopSmallIrpLookasideList" @  804758a0 "Irps"     Type     =     0000 NonPagedPool     Current  Depth =        3   MaxDepth    =        4     Size           =      148   MaxAlloc    =      592     AllocateMisses =       32   FreeMisses  =        9     TotalAllocates =       52   TotalFrees  =       32     Hit Rate       =      38%   HitRate     =      71% Lookaside "nt!IopLargeIrpLookasideList" @  804756a0 "Irpl"     Type     =     0000 NonPagedPool     Current  Depth =        4   MaxDepth    =        4     Size           =      436   MaxAlloc    =     1744     AllocateMisses =     2623   FreeMisses  =     2443     TotalAllocates =     7039   TotalFrees  =     6863     Hit Rate       =      62%   HitRate     =      64% " Lookaside "nt!IopMdlLookasideList" @ 80475740 "Mdl "     Type     =     0000 NonPagedPool     Current Depth  =        3   MaxDepth    =        4     Size           =      120   MaxAlloc    =      480     AllocateMisses =     7017   FreeMisses  =     1824     TotalAllocates =    10901   TotalFrees  =     5711     Hit Rate       =      35%   HitRate     =      68% § Total NonPaged currently allocated for above lists = 4200 Total NonPaged potential  for above lists          = 6144 Total Paged currently  allocated for above lists   = 5136 Total Paged potential  for  above lists            = 12032


Driver Verifier

Driver Verifier is a mechanism that can be used to help find and isolate commonly found bugs in device driver or other kernel-mode system code. Microsoft uses Driver Verifier to check its own device drivers as well as all device drivers that vendors submit for Hardware Compatibility List (HCL) testing. Doing so ensures that the drivers on the HCL are compatible with Windows and free from common driver errors. (Although not described in this book, there is also a corresponding Application Verifier tool that has resulted in quality improvements for usermode code in Windows.)

Driver Verifier consists of support in several system components: the memory manager, I/O manager, and the HAL all have driver verification options that can be enabled. This section describes the memory management related verification options Driver Verifier provides. The options related to device drivers are described in Chapter 9.

Driver Verifier Configuration and Initialization

To configure Driver Verifier and view statistics about its operation, run the Driver Verifier Manager (\Windows\System32\Verifier.exe). (The Windows 2000 version is shown in Figure 7-7.) It displays several tabbed pages. You use the Settings tab to specify which device drivers you want to verify (including an option to Verify All Drivers) and what types of verification you want performed.

Figure 7-7. The Driver Verifier Manager in Windows 2000


In Windows XP and Windows Server 2003, Verifier.exe has a wizard-style interface, as shown in Figure 7-8.

Figure 7-8. The Driver Verifier Manager in Windows XP and Windows Server 2003


You can also enable and disable Driver Verifier as well as display current settings by using its command line interface. Type "verifier /?" to see the switches.

The settings are stored in the registry under HKLM\SYSTEM\CurrentControlSet\Control\ Session Manager\Memory Management. The value VerifyDriverLevel contains a bitmask that represents the verification types enabled. The VerifyDrivers value contains the names of the drivers to validate. (These values won't exist in the registry until you select drivers to verify in the Driver Verifier Manager.) If you choose to verify all drivers, VerifyDrivers is set to an asterisk (*) character. Depending on the settings you have made, you might need to reboot the system for the selected verification to occur.

Early in the boot process, the memory manager reads the Driver Verifier registry values to determine which drivers to verify and which Driver Verifier options you enabled. (Note that if you boot in safe mode, any Driver Verifier settings are ignored.) Subsequently, if you've selected at least one driver for verification, the kernel checks the name of every device driver it loads into memory against the list of drivers you've selected for verification. For every device driver that appears in both places, the kernel invokes the MiApplyDriverVerifier function, which replaces the driver's references to a number of kernel functions with references to Driver Verifier equivalent versions of those functions. For example, ExAllocatePool is replaced with a call to VerifierAllocatePool. The windowing system driver also makes similar changes to use Driver Verifier equivalent functions.

Now that we've reviewed how to set up Driver Verifier, let's examine the four memory-related verification options that can be applied to device drivers: Special Pool, Pool Tracking, Force IRQL Checking, and Low Resources Simulation.

Special Pool The Special Pool option causes the pool allocation routines to bracket pool allocations with an invalid page so that references before or after the allocation will result in a kernel-mode access violation, thus crashing the system with the finger pointed at the buggy driver. Special pool also causes some additional validation checks to be performed when a driver allocates or frees memory.

When special pool is enabled, the pool allocation routines allocate a region of kernel memory for Driver Verifier to use. Driver Verifier redirects memory allocation requests that drivers under verification make to the special pool area rather than to the standard kernel-mode memory pools. When a device driver allocates memory from special pool, Driver Verifier rounds up the allocation to an even-page boundary. Because Driver Verifier brackets the allocated page with invalid pages, if a device driver attempts to read or write past the end of the buffer, the driver will access an invalid page and the memory manager will raise a kernel-mode access violation.

Figure 7-9 shows an example of the special pool buffer that Driver Verifier allocates to a device driver when Driver Verifier checks for overrun errors.

Figure 7-9. Layout of special pool allocations


By default, Driver Verifier performs overrun detection. It does this by placing the buffer that the device driver uses at the end of the allocated page and fills the beginning of the page with a random pattern. Although the Driver Verifier Manager doesn't let you specify underrun detection, you can set this type of detection manually by adding the DWORD registry value HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\MemoryManagement\PoolTagOverruns and setting it to 0 (or by running the Gflags utility and selecting the Verify Start option instead of the default option Verify End). When Windows enforces underrun detection, Driver Verifier allocates the driver's buffer at the beginning of the page rather than at the end.

The overrun-detection configuration includes some measure of underrun detection as well. When the driver frees its buffer to return the memory to Driver Verifier, Driver Verifier ensures that the pattern preceding the buffer hasn't changed. If the pattern is modified, the device driver has underrun the buffer and written to memory outside the buffer.

Special pool allocations also check to ensure that the processor IRQL at the time of an allocation and deallocation is legal. This check catches an error that some device drivers make: allocating pageable memory from an IRQL at DPC/dispatch level or above.

You can also configure special pool manually by adding the DWORD registry value HKLM\SYSTEMCurrentControlSet\Control\Session Manager\Memory Management\PoolTag, which represents the allocation tags the system uses for special pool. Thus, even if Driver Verifier isn't configured to verify a particular device driver, if the tag the driver associates with the memory it allocates matches what is specified in the PoolTag registry value, the pool allocation routines will allocate the memory from special pool. If you set the value of PoolTag to 0x0000002a or the wildcard (*), all memory that drivers allocate is from special pool, provided there's enough virtual and physical memory. (The drivers will revert to allocating from regular pool if there aren't enough free pages bounding exists, but each allocation uses two pages.)

Pool Tracking If pool tracking is enabled, the memory manager checks at driver unload time whether the driver freed all the memory allocations it made. If it didn't, it crashes the system, indicating the buggy driver. Driver Verifier also shows general pool statistics on the Driver Verifier Manager's Pool Tracking tab. You can also use the !verifier kernel debugger command. This command shows more information than Driver Verifier and is useful to driver writers.

Force IRQL Checking One of the most common device driver bugs occurs when a driver accesses pageable data or code when the processor on which the device driver is executing is at an elevated IRQL. As explained in Chapter 3, the memory manager can't service a page fault when the IRQL is DPC/dispatch level or above. The system often doesn't detect instances of a device driver accessing pageable data when the processor is executing at a high IRQL level because the pageable data being accessed happens to be physically resident at the time. At other times, however, the data might be paged out, which results in a system crash with the stop code IRQL_NOT_LESS_OR_EQUAL (that is, the IRQL wasn't less than or equal to the level required for the operation attempted in this case, accessing pageable memory).

Although testing device drivers for this kind of bug is usually difficult, Driver Verifier makes it easy. If you select the Force IRQL Checking option, Driver Verifier forces all kernel-mode pageable code and data out of the system working set whenever a device driver under verification raises the IRQL. The internal function that does this is MmTrimAllSystemPagableMemory. With this setting enabled, whenever a device driver under verification accesses pageable memory when the IRQL is elevated, the system instantly detects the violation and the resulting system crash identifies the faulty driver.

Low Resources Simulation Enabling Low Resources Simulation causes Driver Verifier to randomly fail memory allocations that verified device drivers perform. In the past, developers wrote many device drivers under the assumption that kernel memory would always be available and that if memory ran out, the device driver didn't have to worry about it because the system would crash anyway. However, because low-memory conditions can occur temporarily, it's important that device drivers properly handle allocation failures that indicate kernel memory is exhausted.

Beginning 7 minutes after the system boots which is enough time to get past the critical initialization period in which a low-memory condition might prevent a device driver from loading Driver Verifier starts randomly failing allocation calls for device drivers it is verifying. If a driver doesn't correctly handle allocation failures, this will likely show up as a system crash.

Driver Verifier is a valuable addition to the arsenal of verification and debugging tools available to device driver writers. Many device drivers that first ran with Driver Verifier had bugs that Driver Verifier was able to expose. Thus, Driver Verifier has resulted in an overall improvement in the quality of all kernel-mode code running in Windows.

     < Day Day Up > 


    Microsoft Windows Internals
    Microsoft Windows Internals (4th Edition): Microsoft Windows Server 2003, Windows XP, and Windows 2000
    ISBN: 0735619174
    EAN: 2147483647
    Year: 2004
    Pages: 158

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net