Работа с ассоциативными списками
Иногда выделение памяти при помощи системных вызовов, описанных выше, становится неоптимальным. Например, частое выделение и освобождение мелких блоков при помощи системных вызовов оказывается причиной существенного падения быстродействия. Между тем, если известно заранее, что манипуляция будет производиться блоками определенного и постоянного размера, то имеет смысл организовать "локальную кучу", которая управлялась бы более производительными функциями.
Такую возможность предоставляет механизм ассоциативных списков (lookaside list), который реализован в системных вызовах, представленных ниже. Поначалу, ассоциативный список — это всего лишь заранее созданный заголовок, который должен хранить информацию о состоянии списка, и не содержит никаких выделенных блоков памяти. По мере выполнения вызовов ExAllocateFrom(N)PagedLookasideList
(таблицы 7.9 и 7.10) такие блоки создаются — либо системным вызовом ExAllocatePoolWithTag, либо внутри предоставленной драйвером функции (указанной драйвером параметром pAllocFunction). По мере создания и, возможно, последующего освобождения выделенных ранее блоков (системным вызовом ExFreePool, либо предоставленной драйвером функцией), ассоциативный список может оказаться держателем некоторого количества блоков фиксированного размера в страничной либо нестраничной памяти (в зависимости от способа инициализации).
Таблица 7.9. Прототип вызова ExInitializePagedLookasideList
PVOID ExInitializePagedLookasideList | IRQL < DISPATCH_LEVEL |
Параметры | Создание ассоциативного списка блоков страничной памяти |
IN PPAGED_LOOKASIDE_LIST pLookasideListHeader | Указатель на предварительно выделенную драйвером область размером sizeof(PAGED_LOOKASIDE_LIST) |
Остальные параметры совпадают с параметрами вызова ExInitializeNPagedLookasideList, см. ниже таблицу 7.10 | |
Возвращаемое значение | void |
В настоящий момент, Windows XP и Server 2003 самостоятельно и динамически определяет максимальное число элементов в ассоциативном списке.
Для Windows 2000 в качестве такого параметра был анонсирован параметр Depth. (K сожалению, в документации способ определения значения этого параметра умалчивается, начиная с версии DDK Win98.) Следует отметить, что до достижения данного максимума освобождаемые драйвером блоки не возвращаются в системную память, оставаясь в составе списка. Если в списке имеются ранее освобожденные блоки, и поступил запрос на новый блок, то предоставляется указатель на один из них. Ситуация кардинально меняется если максимум достигнут. При запросе нового блока (и при этом ранее освобожденных блоков в списке нет) память под него берется непосредственно из системной памяти и при освобождении сразу же возвращается в соответствующий пул системной памяти — иными словами, исчезают преимущества использования ассоциативного списка.
Таблица 7.10. Прототип вызова ExInitializeNPagedLookasideList
VOID ExInitializeNPagedLookasideList | IRQL <= DISPATCH_LEVEL |
Параметры | Создание ассоциативного списка блоков нестраничной памяти |
IN PNPAGED_LOOKASIDE_LIST pLookasideListHeader |
Указатель на предварительно выделенную драйвером область размером sizeof(NPAGED_LOOKASIDE_LIST) |
IN OPTIONAL PALLOCATE_FUNCTION pAllocFunction |
NULL или указатель на предоставляемую драйвером функцию, которая будет заниматься выделением блоков из массива системной нестраничной памяти (если NULL — будет использован системный вызов ExAllocatePoolWithTag) |
IN OPTIONAL PFREE_FUNCTION pFreeFunction |
NULL или указатель на предоставляемую драйвером функцию, которая будет заниматься освобождением блоков (если NULL — будет использован вызов ExFreePool) |
IN ULONG Flags | Зарезервировано. Указывать 0 |
IN ULONG ByteSize | Размер отдельных блоков, поддерживаемых данным списком |
IN ULONG Tag | Метка (тег) для создаваемых блоков, можно задавать как 4 символа, например, 'ABCD' |
IN USHORT Depth | Зарезервировано. Указывать 0 |
Возвращаемое значение | void |
размером sizeof(PAGED_LOOKASIDE_LIST) или sizeof(NPAGED_LOOKASIDE_LIST) — в зависимости от того, какой список инициализируется. Для этих целей можно использовать описанные выше системные вызовы ExAllocatePool
или ExAllocatePoolWithTag. В конце работы со списком обязательно следует выполнить вызов ExDelete(N)PagedLookasideList.
Функции, на которые указывает pAllocFunction, имеют прототип:
PVOID MyAllocateFunction ( IN_POOL_TYPE PoolType, // PagedPool или NonPagedPool IN ULONG NumberOfBytes, // размер IN ULONG Tag // тег );
Функции, на которые указывает pFreeFunction, имеют прототип:
PVOID MyFreeFunction (PVOID pBuffer);
Таблица 7.11. Прототип вызова ExAllocateFromNPagedLookasideList
PVOID ExAllocateFromNPagedLookasideList | IRQL <= DISPATCH_LEVEL |
Параметры | Выполняет выделение блока памяти из нестраничного списка |
IN PNPAGED_LOOKASIDE_LIST pLookasideList |
Указатель на инициализированный ассоциативный список |
Возвращаемое значение | Указатель на блок фиксированного размера или NULL (если функция выделения памяти не смогла получить очередной блок) |
PVOID ExAllocateFromPagedLookasideList | IRQL < DISPATCH_LEVEL |
Параметры | Выполняет выделение блока памяти из страничного списка |
IN PPAGED_LOOKASIDE_LIST pLookasideList |
Указатель на инициализированный ассоциативный список |
Возвращаемое значение | Указатель на блок фиксированного размера или NULL (если функция выделения памяти не смогла получить очередной блок) |
VOID ExFreeToNPagedLookasideList | IRQL <= DISPATCH_LEVEL |
Параметры | Возвращает блок в нестраничный ассоциативный список |
IN PNPAGED_LOOKASIDE_LIST pLookasideList | Указатель на инициализированный ассоциативный список |
IN PVOID pEntry | Указатель на ранее полученный из списка блок фиксированного размера |
Возвращаемое значение | void |
VOID ExFreeToPagedLookasideList | IRQL < DISPATCH_LEVEL |
Параметры | Возвращает блок в страничный ассоциативный список |
IN PPAGED_LOOKASIDE_LIST pLookasideList |
Указатель на инициализированный ассоциативный список |
IN PVOID pEntry | Указатель на ранее полученный из списка блок фиксированного размера |
Возвращаемое значение | void |
VOID ExDeleteNPagedLookasideList | IRQL <= DISPATCH_LEVEL |
Параметры | Выполняет удаление нестраничного ассоциативного списка |
IN PNPAGED_LOOKASIDE_LIST pLookasideList |
Указатель на ассоциативный список |
Возвращаемое значение | void |
VOID ExDeletePagedLookasideList | IRQL < DISPATCH_LEVEL |
Параметры | Выполняет удаление страничного ассоциативного списка |
IN PPAGED_LOOKASIDE_LIST pLookasideList |
Указатель на ассоциативный список |
Возвращаемое значение | void |