The memory manager is part of the Windows 2000 executive and therefore exists in the file Ntoskrnl.exe. No parts of the memory manager exist in the HAL. The memory manager consists of the following components:
Each of these components is covered in more detail later in the chapter.
Like all other components of the Windows 2000 executive, the memory manager is fully reentrant and supports simultaneous execution on multiprocessor systems—that is, it allows two threads to acquire resources in such a way that they don't corrupt each other's data. To accomplish the goal of being fully reentrant, the memory manager uses several different internal synchronization mechanisms to control access to its own internal data structures, such as spinlocks and executive resources. (Synchronization objects are discussed in Chapter 3.)
Systemwide resources to which the memory manager must synchronize access include the page frame number (PFN) database (controlled by a spinlock), section objects and the system working set (controlled by executive resources), and page file creation (controlled by a mutex), as well as other internal structures. Per-process memory management data structures are synchronized using two per-process mutexes: the working set lock (held while changes are being made to the working set list) and the address space lock (held whenever the address space is being changed).
Like most of Windows 2000, the memory manager attempts to automatically provide optimal system performance for varying workloads on systems of varying sizes and types. You can use registry values under the key HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management to override these default performance calculations. Some of these values are listed in Table 7-1. For more details, see the Windows 2000 resource kit Technical Reference to the Registry help file.
Table 7-1 Registry Values That Affect the Memory Manager
|ClearPageFileAtShutdown||Specifies whether inactive pages in the paging file are filled with zeros when the system is shut down. This is a security feature.|
|DisablePagingExecutive||Specifies whether user-mode and kernel-mode drivers and kernel-mode system code can be paged to disk when not in use. If the value of this entry is 0 (the default), drivers and the kernel must remain in physical memory. If the value is 1, they can be paged to disk as needed.|
|IoPageLockLimit||Specifies the limit of the number of bytes that can be locked in a user process for I/O operations. When this value is 0, the system uses the default (512 KB). The maximum value is approximately the equivalent of physical memory minus 7 MB. This registry key isn't used in Windows 2000 Datacenter Server and is no longer used in Windows 2000 starting with Windows 2000 Service Pack 1.|
|LargePageMinimum||Indicates the minimum number of megabytes necessary to map Ntoskrnl and HAL using large (4-MB) pages. (This value isn't documented and isn't present by default; you must add it manually.)|
|LargeSystemCache||Affects whether the file system cache or the working sets of processes are given priority when it comes to memory trade-offs. Also affects the size of the file system cache. (On Windows 2000 Server systems, you can adjust this value indirectly by setting the properties of the file server service—see Chapter 11 for details.)|
|NonPagedPoolQuota||Indicates the maximum nonpaged pool that can be allocated by any process (in megabytes). If the value of this entry is 0, the system calculates the value.|
|NonPagedPoolSize||Indicates the initial size of nonpaged pool in bytes. When this value is 0, the system calculates the value.|
|PagedPoolQuota||Indicates the maximum paged pool (in megabytes) that any process can allocate. If the value of this entry is 0, the system calculates the value.|
|PagedPoolSize||Indicates the maximum size of paged pool in bytes. When the value of this entry is 0, the system calculates the value. A value of -1 indicates that the largest possible size is selected, which means allowing a larger paged pool in favor of system page table entries (PTEs).|
|SystemPages||Indicates the number of system page table entries reserved for mapping I/O buffers, device drivers, kernel thread stacks, or pages for programmed I/O into the system address space. If the value is 0, the system calculates the value. If the value is -1, the maximum number of system PTEs will be reserved. (This value might be needed to support, for example, a device that requires a large number of system PTEs, such as a video card with 512 MB of video memory that has to be mapped all at one time.)|
Most of the interesting "knobs" or controls that affect memory manager policy are kernel variables that contain various thresholds and limits computed at system boot time on the basis of memory size and product type (Windows 2000 Professional being optimized for desktop interactive use and Windows 2000 Server systems for running server applications). Examples of these knobs include the sizing of system memory (paged pool, nonpaged pool, system cache, number of system page table entries), page read cluster size, counters that trigger working set trimming, and thresholds for the modified page writer. To find some of these, search for global variables in Ntoskrnl.exe that have names beginning with Mm that contain the word "maximum" or "minimum."
Although you'll find references to many of these knobs, you shouldn't change them. Windows 2000 has been tested to operate properly with the current possible permutations of these values that can be computed. Changing the value of these kernel variables on a running system can result in unpredictable system behavior, including system hangs or even crashes.
The current memory sizes that determine whether Windows 2000 considers a system to have a small, medium, or large amount of memory are listed in Table 7-2. The memory manager uses this value in many of its boot-time calculations.
Table 7-2 Values That Determine System Memory Size
|System Memory Size||Physical Memory|
|Large||>32 MB if Windows 2000 Professional >64 MB if Windows 2000 Server|
Determining the System Memory Size
Because device drivers (as well as other Windows 2000 kernel-mode components) might make resource allocation and run-time policy decisions based on memory size values, the following kernel-mode routines have been provided (and are documented in the DDK):
Function Description MmQuery-SystemSize Returns whether the machine has a small, medium, or large amount of available memory. MmIsThisAn-NtAsSystem Returns TRUE for Windows 2000 Server, Windows 2000 Advanced Server, and Windows 2000 Datacenter Server, and FALSE for Windows 2000 Professional. (This routine's name was based on the original product name of the server of the server edition of Microsoft Windows NT, Windows NT Advanced Server.)
The Memory and Process performance counter objects provide access to most of the details about system and process memory utilization. Throughout the chapter, we'll include references to specific performance counters that contain information related to the component being described.
Besides the Performance tool, a number of tools in the Windows 2000 Support Tools and Windows 2000 resource kits display different subsets of memory usage information. We've included relevant examples and experiments throughout the chapter. One word of caution, however—different utilities use varying and sometimes inconsistent or confusing names when displaying memory information. The following experiment illustrates this point. (We'll explain the terms used in this example in subsequent sections.)
Viewing System Memory Information
The Performance tab in the Windows 2000 Task Manager, shown in the following screen shot, displays basic system memory information. This information is a subset of the detailed memory information available through the performance counters.
Both Pmon.exe (in the Windows 2000 Support Tools) and Pstat.exe (in the Platform SDK) display system and process memory information. The annotations in the following output from Pstat explain the information reported. (For an explanation of the commit total and limit, see Table 7-8.)
To see the specific usage of paged and nonpaged pool, use the Poolmon utility, described in the experiment.
Finally, the !vm command in the kernel debugger shows the basic memory management information available through the memory-related performance counters. This command can be useful if you're looking at a crash dump or hung system. Here's an example of its output:
kd> !vm *** Virtual Memory Usage *** Physical Memory: 32620 ( 130480 Kb) Page File: \??\C:\pagefile.sys Current: 204800Kb Free Space: 101052Kb Minimum: 204800Kb Maximum: 204800Kb Available Pages: 3604 ( 14416 Kb) ResAvail Pages: 24004 ( 96016 Kb) Modified Pages: 768 ( 3072 Kb) NonPagedPool Usage: 1436 ( 5744 Kb) NonPagedPool Max: 12940 ( 51760 Kb) PagedPool 0 Usage: 6817 ( 27268 Kb) PagedPool 1 Usage: 982 ( 3928 Kb) PagedPool 2 Usage: 984 ( 3936 Kb) PagedPool Usage: 8783 ( 35132 Kb) PagedPool Maximum: 26624 ( 106496 Kb) Shared Commit: 1361 ( 5444 Kb) Special Pool: 0 ( 0 Kb) Free System PTEs: 189291 ( 757164 Kb) Shared Process: 3165 ( 12660 Kb) PagedPool Commit: 8783 ( 35132 Kb) Driver Commit: 1098 ( 4392 Kb) Committed pages: 45113 ( 180452 Kb) Commit limit: 79556 ( 318224 Kb) Total Private: 30536 ( 122144 Kb) IEXPLORE.EXE 3028 ( 12112 Kb) svchost.exe 2128 ( 8512 Kb) WINWORD.EXE 1971 ( 7884 Kb) POWERPNT.EXE 1905 ( 7620 Kb) Acrobat.exe 1761 ( 7044 Kb) winlogon.exe 1361 ( 5444 Kb) explorer.exe 1300 ( 5200 Kb) livekd.exe 1015 ( 4060 Kb) hh.exe 960 ( 3840 Kb)
Accounting for Physical Memory Use
By combining information available from performance counters with output from kernel debugger commands, you can come close to accounting for physical memory usage on a machine running Windows 2000. To examine the memory usage information available through performance counters, run the Performance tool and add the counters to view the following information. (You'll see the results more easily if you change the vertical scale maximum on the graph to 1000.)
- Total process working set size To view this information, select the Process performance object and the Working Set counter for the _Total process instance. This number will be larger than the actual total process memory utilization because shared pages are counted in each process working set. To get a more accurate picture of process memory utilization, subtract free memory (available bytes), operating system memory used (nonpaged pool, resident paged pool, and resident operating system and driver code), and the size of the modified list from the total physical memory on the machine. What you're left with is the memory being used by processes. Comparing this value against the total process working set size as reported by the Performance tool gives you some indication of the amount of sharing occurring between processes. Although examining process physical memory usage is interesting, of more concern is the private committed virtual memory usage by processes, because memory leaks show up as an increasing private virtual size, not an increasing working set size. (At some point, the memory manager will stop the process from growing in physical size, though it can continue to grow in its virtual size until the commit limit—the maximum amount of private committed virtual memory available on the system—is reached. For more information, see the section "Page Files.")
- Total system working set size To view this information, select the Memory processor object and the Cache Bytes counter. As explained in the section "System Working Set," the total system working set size includes more than just the cache size—it includes resident paged pool and resident operating system and driver code.
- Size of nonpaged pool View this information by adding the Memory: Pool Nonpaged Bytes counter.
- Size of the free, zero, and standby lists View the sizes of these lists by adding the Memory: Available Bytes counter. (Use the !memusage kernel debugger command to get the size of each of the lists separately.)
Your graph or report now contains a representation of all of physical memory except for two components that you can't obtain from a performance counter:
- Nonpaged operating system and driver code
- The modified and modified-no-write paging lists
Although you can easily obtain the size of both the modified and modified-no-write lists by using the kernel debugger !memusage command, there's no easy way to get the size of the nonpaged operating system and driver code. You can, however, get the amount of physical memory being used by nonpaged and paged operating system and device driver code by using the kernel debugger !drivers 1 command and the total of the Resident column. This column represents the number of pages that physically reside in each loaded kernel-mode module.