Использование списков lookaside

Драйверы, которые должны динамически выделять буферы фиксированного размера для выполнения операций ввода-вывода по запросу, могут использовать процедуры поддержки ExXxxLookasideListEx или ExXxxLookasideList . После того как такой драйвер инициализирует свой список lookaside, операционная система будет хранить некоторое количество динамически выделенных буферов заданного размера в списке lookaside драйвера, фактически резервируя набор многократно используемых буферов фиксированного размера для драйвера. Формат и содержимое буферов фиксированного размера драйвера (также известных как записи) в списке lookaside определяются драйвером.

Например, драйверы класса хранения, которые должны настроить блоки запросов SCSI (SRB) для базовых драйверов портов или минипортов SCSI, используют списки lookaside. Такой драйвер класса выделяет буферы для srb по мере необходимости из списка lookaside и освобождает каждый буфер SRB обратно в список lookaside для списка lookaside для повторного использования всякий раз, когда SRB возвращается в драйвер класса в завершенном IRP. Так как драйвер класса хранения не может заранее определить, сколько SDB он должен использовать в любое время, так как спрос на ввод-вывод в драйвере увеличивается и падает, список просмотров является удобным и экономичным способом управления выделением и освобождением буферов для SDB фиксированного размера в таком драйвере.

Операционная система поддерживает состояние всех постраничных и невыгружаемых списков просмотра, которые используются в настоящее время, динамически отслеживает спрос на выделение и освобождение записей во всех списках, а также доступный системный пул для новых записей. Когда потребность в выделении высока, операционная система увеличивает количество записей, хранящихся в каждом списке lookaside. Когда спрос снова падает, он освобождает излишки lookaside записи обратно в системный пул.

Списки lookaside являются потокобезопасны. Список lookaside имеет встроенную синхронизацию, позволяющую нескольким одновременно работающим потокам в драйвере совместно использовать список lookaside. Эти потоки могут безопасно выделять буферы из общего списка lookaside и освобождать эти буферы в списке, не требуя от драйвера явной синхронизации этих операций. Однако, чтобы избежать возможных утечек и повреждения данных, набор потоков, совместно использующих список lookaside, должен явно синхронизировать инициализацию и удаление списка.

Интерфейсы списков lookaside

Начиная с Windows Vista, структура LOOKASIDE_LIST_EX описывает список внешних элементов, который может содержать буферы с разбивкой на страницы или буферы без пачки. Если драйвер предоставляет пользовательские подпрограммы Allocate и Free для этого списка lookaside, эти подпрограммы получают закрытый контекст в качестве входного параметра. Драйвер может использовать этот контекст для сбора личных данных для списка lookaside. Например, контекст может использоваться для подсчета количества записей списка, которые динамически выделяются и освобождаются списком. Пример кода, демонстрирующий использование контекста таким образом, см. в разделе ExInitializeLookasideListEx.

Следующие системные подпрограммы поддерживают списки lookaside, которые описываются структурой LOOKASIDE_LIST_EX :

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

Начиная с Windows 2000, структура PAGED_LOOKASIDE_LIST описывает список просмотров, содержащий буферы со страницами. Если драйвер предоставляет пользовательские подпрограммы Allocate и Free для этого списка lookaside, эти подпрограммы не получают закрытый контекст в качестве входного параметра. По этой причине, если драйвер предназначен для работы только в Windows Vista и более поздних версиях Windows, рассмотрите возможность использования структуры LOOKASIDE_LIST_EX вместо структуры PAGED_LOOKASIDE_LIST для списков lookaside. Следующие системные подпрограммы поддерживают списки lookaside, которые описываются структурой PAGED_LOOKASIDE_LIST :

ExAllocateFromPagedLookasideList

ExDeletePagedLookasideList

ExFreeToPagedLookasideList

ExInitializePagedLookasideList

Начиная с Windows 2000, структура NPAGED_LOOKASIDE_LIST описывает список lookaside, содержащий буферы без пахты. Если драйвер предоставляет пользовательские подпрограммы Allocate и Free для этого списка lookaside, эти подпрограммы не получают закрытый контекст в качестве входного параметра. Опять же, если драйвер предназначен для работы только в Windows Vista и более поздних версиях Windows, рассмотрите возможность использования структуры LOOKASIDE_LIST_EX вместо структуры NPAGED_LOOKASIDE_LIST для списков lookaside. Следующие системные подпрограммы поддерживают списки lookaside, которые описываются NPAGED_LOOKASIDE_LIST структурой:

ExAllocateFromNPagedLookasideList

ExDeleteNPagedLookasideList

ExFreeToNPagedLookasideList

ExInitializeNPagedLookasideList

Рекомендации по реализации

Чтобы реализовать список lookaside, использующий структуру LOOKASIDE_LIST_EX , следуйте приведенным ниже рекомендациям по проектированию.

  • Вызовите ExInitializeLookasideListEx , чтобы настроить список lookaside. В этом вызове укажите, должны ли записи в списке lookaside быть страничными или неоплаченными буферами. Используйте непагированные буферы, если сам драйвер или любой базовый драйвер, которому он передает свои записи списка просмотра, может получить доступ к этим записям в IRQL >= DISPATCH_LEVEL. Используйте постраничные буферы только в том случае, если доступ к записям внешнего списка драйвера всегда выполняется в IRQL <= APC_LEVEL.

  • Структура LOOKASIDE_LIST_EX для списка lookaside всегда должна находиться в непагружаемой системной памяти, независимо от того, являются ли записи в списке выгружаемой или не погашенной.

  • Для повышения производительности передайте указатели NULL для параметров Allocate и Free в ExInitializeLookasideListEx , если подпрограммы выделения и освобождения не должны делать больше, чем просто выделять и освобождать память для записей списка lookaside. Например, эти подпрограммы могут записывать сведения об использовании динамически выделенных буферов драйвером.

  • Подпрограмма Выделения , предоставляемая драйвером, может передавать входные параметры (PoolType, Tag и Size), которые она получает, непосредственно в подпрограмму ExAllocatePoolWithTag или ExAllocatePoolWithQuotaTag для выделения нового буфера.

  • Для каждого вызова ExAllocateFromLookasideListEx следует выполнять обратный вызов ExFreeToLookasideListEx как можно скорее, когда ранее выделенная запись больше не используется.

Предоставление подпрограмм Allocate и Free , которые выполняют не более чем вызов ExAllocatePoolWithTag и ExFreePool соответственно, приводит к отработке циклов процессора. ExAllocateFromLookasideListEx выполняет необходимые вызовы ExAllocatePoolWithTag и ExFreePool автоматически, когда драйвер передает указатели NullAllocate и Freeв ExInitializeLookasideListEx.

Любая подпрограмма выделения , предоставляемая драйвером, не должна выделять память для записи из выгружаемого пула, которая будет храниться в списке просмотра без пачки или наоборот. Он также должен выделять записи фиксированного размера, так как любой последующий вызов драйвера к ExAllocateFromLookasideListEx возвращает первую запись, которая в настоящее время хранится в списке lookaside, если список не пуст. То есть вызов ExAllocateFromLookasideListEx вызывает процедуру Allocate , предоставляемую драйвером, только если данный список lookaside в настоящее время пуст. Таким образом, при каждом вызове ExAllocateFromLookasideListEx возвращаемая запись будет точно соответствовать размеру, необходимому драйверу, только если все записи в списке lookaside имеют фиксированный размер. Предоставленная драйвером подпрограмма Allocate также не должна изменять значение тега , которое драйвер изначально передал в ExInitializeLookasideListEx, так как изменения в значении тега пула усложнят отладку и отслеживание использования памяти драйвером.

Вызовы ExFreeToLookasideListEx сохраняют ранее выделенные записи в списке lookaside, если список уже не заполнен (то есть список содержит определяемое системой максимальное количество записей). Для повышения производительности драйвер должен выполнять обратный вызов ExFreeToLookasideListEx как можно быстрее при каждом вызове ExAllocateFromLookasideListEx. Когда драйвер быстро освобождает записи обратно в свой список lookaside, следующий вызов этого драйвера к ExAllocateFromLookasideListEx гораздо менее вероятно, что приведет к снижению производительности при динамическом выделении памяти для новой записи.

Аналогичные рекомендации применяются к списку lookaside, использующему структуру PAGED_LOOKASIDE_LIST или NPAGED_LOOKASIDE_LIST .