Usando MDLs

Um buffer de E/S que abrange um intervalo de endereços contíguos de memória virtual pode ser distribuído em várias páginas físicas e essas páginas podem ser descoloridas. O sistema operacional usa uma MDL ( lista de descritores de memória ) para descrever o layout de página física de um buffer de memória virtual.

Um MDL consiste em uma estrutura MDL seguida por uma matriz de dados que descreve a memória física na qual reside o buffer de E/S. O tamanho de um MDL varia de acordo com as características do buffer de E/S descrito pelo MDL. As rotinas do sistema estão disponíveis para calcular o tamanho necessário de um MDL e alocar e liberar o MDL.

Uma estrutura MDL é semipaca. Seu driver deve acessar diretamente apenas os membros Next e MdlFlags dessa estrutura. Para obter um exemplo de código que usa esses dois membros, consulte a seção Exemplo a seguir.

Os membros restantes de um MDL são opacos. Não acesse diretamente os membros opacos de um MDL. Em vez disso, use as seguintes macros, que o sistema operacional fornece para executar operações básicas na estrutura:

MmGetMdlVirtualAddress retorna o endereço de memória virtual do buffer de E/S descrito pelo MDL.

MmGetMdlByteCount retorna o tamanho, em bytes, do buffer de E/S.

MmGetMdlByteOffset retorna o deslocamento dentro de uma página física do início do buffer de E/S.

Você pode alocar um MDL com a rotina IoAllocateMdl . Para liberar o MDL, use a rotina IoFreeMdl . Como alternativa, você pode alocar um bloco de memória nãopagada e formatar esse bloco de memória como um MDL chamando a rotina MmInitializeMdl .

Nem IoAllocateMdl nem MmInitializeMdl inicializam a matriz de dados que segue imediatamente a estrutura MDL. Para um MDL que reside em um bloco alocado por driver de memória nãopagada, use MmBuildMdlForNonPagedPool para inicializar essa matriz para descrever a memória física na qual reside o buffer de E/S.

Para memória paginável, a correspondência entre memória virtual e física é temporária, portanto, a matriz de dados que segue a estrutura MDL é válida somente em determinadas circunstâncias. Chame MmProbeAndLockPages para bloquear a memória paginável no local e inicializar essa matriz de dados para o layout atual. A memória não será paginada até que o chamador use a rotina MmUnlockPages , momento em que o conteúdo da matriz de dados não é mais válido.

A rotina MmGetSystemAddressForMdlSafe mapeia as páginas físicas descritas pelo MDL especificado para um endereço virtual no espaço de endereço do sistema, se elas ainda não estiverem mapeadas para o espaço de endereço do sistema. Esse endereço virtual é útil para drivers que podem ter que examinar as páginas para executar E/S, pois o endereço virtual original pode ser um endereço de usuário que só pode ser usado em seu contexto original e pode ser excluído a qualquer momento.

Observe que quando você cria um MDL parcial usando a rotina IoBuildPartialMdl , o MmGetMdlVirtualAddress retorna o endereço inicial original do MDL parcial. Esse endereço será um endereço no modo de usuário se o MDL tiver sido originalmente criado como resultado de uma solicitação de modo de usuário. Dessa forma, o endereço não tem relevância fora do contexto do processo de origem da solicitação.

Normalmente, um driver cria um endereço de modo de sistema chamando a macro MmGetSystemAddressForMdlSafe para mapear o MDL parcial. Isso garante que o driver possa continuar acessando as páginas com segurança, independentemente do contexto do processo.

Quando um driver chama IoAllocateMdl, ele pode associar um IRP ao MDL recém-alocado especificando um ponteiro para o IRP como o parâmetro Irp de IoAllocateMdl. Um IRP pode ter um ou mais MDLs associados a ele. Se o IRP tiver um único MDL associado a ele, o membro MdlAddress do IRP apontará para esse MDL. Se o IRP tiver vários MDLs associados a ele, MdlAddress apontará para o primeiro MDL em uma lista vinculada de MDLs associados ao IRP, conhecido como uma cadeia MDL. Os MDLs são vinculados por seus membros Next . O próximo membro do último MDL na cadeia é definido como NULL.

Se, quando o driver chama IoAllocateMdl, ele especifica FALSE para o parâmetro SecondaryBuffer , o membro MdlAddress do IRP é definido para apontar para o novo MDL. Se SecondaryBuffer for TRUE, a rotina inserirá o novo MDL no final da cadeia de MDL.

Quando o IRP é concluído, o sistema desbloqueia e libera todos os MDLs associados ao IRP. O sistema desbloqueia os MDLs antes de enfileirar a rotina de conclusão de E/S e os libera após a execução da rotina de conclusão de E/S.

Os drivers podem percorrer a cadeia de MDL usando o próximo membro de cada MDL para acessar o próximo MDL na cadeia. Os drivers podem inserir MDLs manualmente na cadeia atualizando os membros Next .

As cadeias de MDL normalmente são usadas para gerenciar uma matriz de buffers associados a uma única solicitação de E/S. (Por exemplo, um driver de rede pode usar um buffer para cada pacote IP em uma operação de rede.) Cada buffer na matriz tem seu próprio MDL na cadeia. Quando o driver conclui a solicitação, ele combina os buffers em um único buffer grande. Em seguida, o sistema limpa automaticamente todos os MDLs alocados para a solicitação.

O gerenciador de E/S é uma fonte frequente de solicitações de E/S. Quando o gerente de E/S conclui uma solicitação de E/S, o gerenciador de E/S libera o IRP e libera todos os MDLs anexados ao IRP. Alguns desses MDLs podem ter sido anexados ao IRP por drivers localizados abaixo do gerenciador de E/S na pilha do dispositivo. Da mesma forma, se o driver for a origem de uma solicitação de E/S, o driver deverá limpo o IRP e quaisquer MDLs anexados ao IRP quando a solicitação de E/S for concluída.

Exemplo

O exemplo de código a seguir é uma função implementada pelo driver que libera uma cadeia MDL de um IRP:

VOID MyFreeMdl(PMDL Mdl)
{
    PMDL currentMdl, nextMdl;

    for (currentMdl = Mdl; currentMdl != NULL; currentMdl = nextMdl) 
    {
        nextMdl = currentMdl->Next;
        if (currentMdl->MdlFlags & MDL_PAGES_LOCKED) 
        {
            MmUnlockPages(currentMdl);
        }
        IoFreeMdl(currentMdl);
    }
} 

Se as páginas físicas descritas por um MDL na cadeia estiverem bloqueadas, a função de exemplo chamará a rotina MmUnlockPages para desbloquear as páginas antes de chamar IoFreeMdl para liberar o MDL. No entanto, a função de exemplo não precisa desmapear explicitamente as páginas antes de chamar IoFreeMdl. Em vez disso, IoFreeMdl cancela automaticamente as páginas quando libera o MDL.