Reference Article
Lay out data structures efficiently and resue them when possible
1. Design issue
Plan your memory allocations according to type of memory, size, and lifetime.
Combine allocations of similar lifetimes, so that you can free unused memory as soon as it is no longer needed.
Don’t mix structures of greatly different sizes in the same allocation unless you can be sure that they will be aligned appropriately.
2. Reusability
Reuse structures instead of freeing them and later reallocating memory for other uses.
3. I/O request handling
Drivers often require additional memory while handling I/O requests.
The ways could be
- Allocate a memory descriptor list (MDL) or internal buffer to use for a specific I/O request
- Allocate an IRP to send to lower drivers.
The size of these structures varies depending on the request.
If your driver has a technique to limit I/O size or to split up a large I/O request, you could make the buffer a fixed size
4. Optimize for the most frequent operations
Allocate nonpaged pool memory for long-term use at start up.
1. Allocate memory for long-term use in a
DriverEntry or
AddDevice routine and free the memory as part of handling the device removal request.
2. The driver should not, however, preallocate excessively large blocks of memory (several megabytes, for example)
3. Appropriate memory allocation routines
- ExAllocatePoolWithTag
- ExAllocatePoolWithQuotaTag
- ExAllocatePoolWithTagPriority
- AllocateCommonBuffer (if the driver's device uses bus-master DMA or a system DMA controller's auto-initialize mode).
4. Use the tagged versions of the pool allocation routines instead of the nontagged versions, this will be helpful at debug and tracking.
Use memory economically.
Nonpaged pool memory is a limited system resource.
1. General Rules:
Avoid calling the memory allocation support routines repeatedly to request allocations of less than PAGE_SIZE.
Bundling structures into a single allocation,if your driver normally uses these related structures together.
2. Exception: (Drivers that use DMA)
If a driver that performs DMA needs several one-page buffers, but the buffers need not be contiguous, it should call
AllocateCommonBuffer once for each such buffer.
3. Round the allocation request up to the next page boundary.
ExAllocatePoolWithTag
- <PAGE_SIZE: allocates the number of bytes requested, they are aligned on an 8-byte boundary.
- >=PAGE_SIZE: allocates a page-aligned buffer that is an integral multiple of PAGE_SIZE bytes
AllocateCommonBuffer always allocates at least a page of memory. If the driver requests less than an integral multiple of PAGE_SIZE bytes, the remaining bytes on the last page are inaccessible to the driver.
Use lookaside lists.
1. Lookaside lists provide fixed-size, reusable buffers.
2. They are designed for structures that a driver might need to allocate dynamically and in unpredictable numbers.
3. Lookaside lists can be allocated from paged pool or nonpaged pool.
4. The driver defines the layout and contents of the entries in the list to suit its requirements, and the system maintains list status and adjusts the number of available entries according to demand.
- ExInitialize[N]PagedLookasideList to set up a lookaside list
- ExAllocateFrom[N]PagedLookasideList to allocate an entry in the list
- ExFreeTo[N]PagedLookasideList to free an entry in the list. The head of the list must be allocated from nonpaged memory, even if the list entries themselves are in paged memory.
Avoid frequently mapping and unmapping the virtual memory address space
This kind of operations can
decrease performance system-wide
Because it can result in frequent flushes of the translation lookaside buffer (TLB), a per-processor cache of virtual-to-physical address translations. Each entry in the TLB contains a page table entry (PTE).
Use
direct I/O to avoid this problem.
Internally, the I/O manager avoids this problem for the MDL in Irp->MdlAddress . The first time a kernel-mode component calls MmGetSystemAddressForMdlSafe , the I/O manager stores the system address in the MDL along with the corresponding physical address. When the IRP returns to the I/O manager after completion,, the I/O manager unmaps the MDL. Thus, the I/O manager requires only a single mapping (and a single virtual to physical address translation) for each I/O request.
Test and verify
Use Driver Verifier (verifier.exe), GFlags (gflags.exe), and PoolMon (poolmon.exe) to track, test, and verify memory allocation issues.
Driver verifier can allocate memory from a special pool and monitor the driver’s access to the allocated memory. It can detect attempts to access memory outside the allocated range or after the memory has been freed. In addition, it checks for memory leaks—memory that has been allocated but is no longer being used and is never freed. Verifier also gathers statistics on the number of memory allocations requested from the special pool and whether they succeeded or failed.
GFlags works together with Driver Verifier. Using GFlags, you can configure the Special Memory Pool option of Driver Verifier or designate the special pool for use in individual memory allocations.
PoolMon gathers and displays a variety of data about memory allocation, sorted by the pool tags assigned during allocation.