Usar listas Lookaside

Os drivers que devem alocar buffers de tamanho fixo dinamicamente para executar operações de E/S sob demanda podem usar as rotinas de suporte ExXxxLookasideListEx ou ExXxxLookasideList. Depois que esse driver inicializar sua lista lookaside, o sistema operacional manterá algum número de buffers alocados dinamicamente do tamanho determinado na lista lookaside do driver, reservando um conjunto de buffers reutilizáveis de tamanho fixo para o driver. O formato e o conteúdo dos buffers de tamanho fixo de um driver (também conhecidos como entradas) em sua lista lookaside são determinados pelo driver.

Por exemplo, os drivers de classe de armazenamento que devem configurar blocos de solicitação SCSI (SRBs) para os drivers de porta/miniporta SCSI subjacentes usam listas lookaside. Esse driver de classe aloca buffers para SRBs conforme necessário de sua lista lookaside e libera cada buffer SRB de volta para a lista lookaside para que a lista lookaside seja reutilizada sempre que um SRB for retornado ao driver de classe em um IRP concluído. Como um driver de classe de armazenamento não pode predeterminar quantos SRBs ele precisa usar a qualquer momento porque a demanda de E/S no driver aumenta e diminui, uma lista lookaside é uma maneira conveniente e econômica de gerenciar a alocação e a desalocação de buffers para SRBs de tamanho fixo em tal driver.

O sistema operacional mantém o estado sobre todas as listas lookaside paginadas e não paginadas que estão sendo usadas no momento, rastreando dinamicamente a demanda por alocações e desalocações de entradas em todas as listas e o pool de sistema disponível para novas entradas. Quando a demanda por alocações é alta, o sistema operacional aumenta o número de entradas que mantém em cada lista lookaside. Quando a demanda cai novamente, ele libera as entradas lookaside excedentes de volta para o pool do sistema.

As listas lookaside são seguras para threads. Uma lista lookaside tem sincronização interna para permitir que vários threads em execução simultânea em um driver compartilhem uma lista lookaside. Esses threads podem alocar com segurança buffers da lista lookaside compartilhada e liberar esses buffers para a lista sem exigir que o driver sincronize explicitamente essas operações. No entanto, para evitar possíveis vazamentos e corrupção de dados, um conjunto de threads que compartilham uma lista lookaside deve sincronizar explicitamente a inicialização e a exclusão da lista.

Interfaces de lista Lookaside

A partir do Windows Vista, a estrutura LOOKASIDE_LIST_EX descreve uma lista lookaside que pode conter buffers paginados ou não paginados. Se um driver fornecer as rotinas personalizadas Allocate e Free para essa lista lookaside, essas rotinas receberão um contexto privado como um parâmetro de entrada. Um driver pode usar esse contexto para coletar dados privados para a lista lookaside. Por exemplo, o contexto pode ser usado para contar o número de entradas da lista que são dinamicamente alocadas e liberadas por ela. Para obter um exemplo de código que mostra como usar um contexto dessa maneira, consulte ExInitializeLookasideListEx.

As seguintes rotinas fornecidas pelo sistema oferecem suporte a listas lookaside que são descritas por uma estrutura LOOKASIDE_LIST_EX:

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

A partir do Windows 2000, a estrutura PAGED_LOOKASIDE_LIST descreve uma lista lookaside que contém buffers paginados. Se um driver fornecer as rotinas personalizadas Allocate e Free para essa lista lookaside, essas rotinas não receberão um contexto privado como um parâmetro de entrada. Por esse motivo, se o driver é destinado a ser executado somente no Windows Vista e em versões posteriores do Windows, considere usar a estrutura LOOKASIDE_LIST_EX em vez da estrutura PAGED_LOOKASIDE_LIST para suas listas lookaside. As seguintes rotinas fornecidas pelo sistema oferecem suporte a listas lookaside que são descritas por uma estrutura PAGED_LOOKASIDE_LIST:

ExAllocateFromPagedLookasideList

ExDeletePagedLookasideList

ExFreeToPagedLookasideList

ExInitializePagedLookasideList

A partir do Windows 2000, a estrutura NPAGED_LOOKASIDE_LIST descreve uma lista lookaside que contém buffers não paginados. Se um driver fornecer as rotinas personalizadas Allocate e Free para essa lista lookaside, essas rotinas não receberão um contexto privado como um parâmetro de entrada. Novamente, se o driver é destinado a ser executado somente no Windows Vista e em versões posteriores do Windows, considere usar a estrutura LOOKASIDE_LIST_EX em vez da estrutura NPAGED_LOOKASIDE_LIST para suas listas lookaside. As seguintes rotinas fornecidas pelo sistema oferecem suporte a listas lookaside que são descritas por uma estrutura NPAGED_LOOKASIDE_LIST:

ExAllocateFromNPagedLookasideList

ExDeleteNPagedLookasideList

ExFreeToNPagedLookasideList

ExInitializeNPagedLookasideList

Diretrizes de implementação

Para implementar uma lista lookaside que usa uma estrutura LOOKASIDE_LIST_EX, siga estas diretrizes de design:

  • Chame ExInitializeLookasideListEx para configurar uma lista lookaside. Nesta chamada, especifique se as entradas na lista lookaside devem ser buffers paginados ou não paginados. Use buffers não paginados se o próprio driver ou qualquer driver subjacente para o qual ele passa suas entradas de lista lookaside puder acessar essas entradas em IRQL >= DISPATCH_LEVEL. Use buffers paginados somente se os acessos às entradas da lista lookaside do driver sempre ocorrerem em IRQL <= APC_LEVEL.

  • A estrutura LOOKASIDE_LIST_EX para a lista lookaside deve sempre residir na memória do sistema não paginada, independentemente de as entradas na lista serem paginadas ou não paginadas.

  • Para obter melhor desempenho, passe ponteiros NULL para os parâmetros Allocate e Free para ExInitializeLookasideListEx, a menos que as rotinas de alocação e desalocação precisem fazer mais do que simplesmente alocar e liberar memória para entradas de listas lookaside. Por exemplo, essas rotinas podem registrar informações sobre o uso de buffers alocados dinamicamente pelo driver.

  • Uma rotina Allocate fornecida pelo driver pode passar os parâmetros de entrada (PoolType, Tag e Size) que receber diretamente para a rotina ExAllocatePoolWithTag ou ExAllocatePoolWithQuotaTag para alocar um novo buffer.

  • Para cada chamada para ExAllocateFromLookasideListEx, faça a chamada recíproca para ExFreeToLookasideListEx o mais rápido possível sempre que uma entrada alocada anteriormente não estiver mais sendo usada.

Fornecer as rotinas Allocate e Free que não fazem mais do que chamar ExAllocatePoolWithTag e ExFreePool, respectivamente, desperdiça ciclos do processador. ExAllocateFromLookasideListEx faz as chamadas necessárias para ExAllocatePoolWithTag e ExFreePool automaticamente quando um driver passa ponteiros NULL Allocate e Free para ExInitializeLookasideListEx.

A rotina Allocate fornecida pelo driver não deve alocar memória para uma entrada do pool paginado a ser mantida em uma lista lookaside não paginada ou vice-versa. Ela também deve alocar entradas de tamanho fixo, porque qualquer chamada de driver subsequente para ExAllocateFromLookasideListEx retorna a primeira entrada atualmente mantida na lista lookaside, a menos que a lista esteja vazia. Ou seja, uma chamada para ExAllocateFromLookasideListEx causa uma chamada para a rotina Allocate fornecida pelo driver somente se a lista lookaside fornecida estiver vazia no momento. Portanto, a cada chamada para ExAllocateFromLookasideListEx, a entrada retornada será exatamente do tamanho que o driver precisa somente se todas as entradas na lista lookaside forem de um tamanho fixo. Uma rotina Allocate fornecida pelo driver também não deve alterar o valor de Tag que o driver passou originalmente para ExInitializeLookasideListEx, pois as alterações no valor da tag de pool dificultariam a depuração e o rastreamento do uso de memória do driver.

Chamadas para ExFreeToLookasideListEx armazenam entradas previamente alocadas na lista lookaside, a menos que a lista já esteja cheia (ou seja, a lista contém o número máximo de entradas determinado pelo sistema). Para um melhor desempenho, um driver deve fazer uma chamada recíproca para ExFreeToLookasideListEx o mais rápido possível para cada chamada que ele faz para ExAllocateFromLookasideListEx. Quando um driver libera entradas de volta para sua lista lookaside rapidamente, é muito menos provável que a próxima chamada desse driver para ExAllocateFromLookasideListEx incorra na penalidade de desempenho de alocar memória dinamicamente para uma nova entrada.

Diretrizes semelhantes se aplicam a uma lista lookaside que usa uma estrutura PAGED_LOOKASIDE_LIST ou NPAGED_LOOKASIDE_LIST.