Uso di MDLs
Un buffer di I/O che si estende su un intervallo di indirizzi di memoria virtuale contigui può essere distribuito su più pagine fisiche e queste pagine possono essere disconteguate. Il sistema operativo usa un elenco di descrittori di memoria (MDL) per descrivere il layout di pagina fisico per un buffer di memoria virtuale.
Un MDL è costituito da una struttura MDL seguita da una matrice di dati che descrive la memoria fisica in cui risiede il buffer di I/O. Le dimensioni di un MDL variano in base alle caratteristiche del buffer di I/O descritto dal file MDL. Le routine di sistema sono disponibili per calcolare le dimensioni necessarie di un MDL e allocare e liberare il file MDL.
Una struttura MDL è semi-opaca. Il driver deve accedere direttamente solo ai membri Next e MdlFlags di questa struttura. Per un esempio di codice che usa questi due membri, vedere la sezione Esempio seguente.
I membri rimanenti di un file MDL sono opachi. Non accedere direttamente ai membri opachi di un file MDL. Utilizzare invece le macro seguenti, che il sistema operativo fornisce per eseguire operazioni di base sulla struttura:
MmGetMdlVirtualAddress restituisce l'indirizzo di memoria virtuale del buffer di I/O descritto da MDL.
MmGetMdlByteCount restituisce le dimensioni, in byte, del buffer di I/O.
MmGetMdlByteOffset restituisce l'offset all'interno di una pagina fisica dell'inizio del buffer di I/O.
È possibile allocare un MDL con la routine IoAllocateMdl . Per liberare il file MDL, usare la routine IoFreeMdl . In alternativa, è possibile allocare un blocco di memoria non di paging e quindi formattare questo blocco di memoria come MDL chiamando la routine MmInitializeMdl .
Né IoAllocateMdl né MmInitializeMdl inizializza la matrice di dati che segue immediatamente la struttura MDL. Per un MDL che risiede in un blocco allocato dal driver di memoria non di paging, usare MmBuildMdlForNonPagedPool per inizializzare questa matrice per descrivere la memoria fisica in cui risiede il buffer di I/O.
Per la memoria pagable, la corrispondenza tra memoria virtuale e fisica è temporanea, quindi la matrice di dati che segue la struttura MDL è valida solo in determinate circostanze. Chiamare MmProbeAndLockPages per bloccare la memoria impaginabile e inizializzare questa matrice di dati per il layout corrente. La memoria non verrà usata fino a quando il chiamante non utilizzerà la routine MmUnlockPages , a quel punto il contenuto della matrice di dati non è più valido.
La routine MmGetSystemAddressForMdlSafe esegue il mapping delle pagine fisiche descritte dal codice MDL specificato a un indirizzo virtuale nello spazio indirizzi del sistema, se non sono già mappate allo spazio indirizzi del sistema. Questo indirizzo virtuale è utile per i driver che potrebbero dover esaminare le pagine per eseguire operazioni di I/O, perché l'indirizzo virtuale originale potrebbe essere un indirizzo utente che può essere usato solo nel contesto originale e può essere eliminato in qualsiasi momento.
Si noti che quando si compila un MDL parziale usando la routine IoBuildPartialMdlche MmGetMdlVirtualAddress restituisce l'indirizzo iniziale originale per il linguaggio MDL parziale. Questo indirizzo è un indirizzo in modalità utente se il file MDL è stato originariamente creato in seguito a una richiesta in modalità utente. Di conseguenza, l'indirizzo non ha rilevanza al di fuori del contesto del processo in cui ha avuto origine la richiesta.
In genere, un driver crea invece un indirizzo in modalità di sistema chiamando la macro MmGetSystemAddressForMdlSafe per eseguire il mapping del file MDL parziale. Ciò garantisce che il driver possa continuare ad accedere alle pagine in modo sicuro indipendentemente dal contesto del processo.
Quando un driver chiama IoAllocateMdl, può associare un IRP al file MDL appena allocato specificando un puntatore all'IRP come parametro Irp di IoAllocateMdl. A un IRP possono essere associati uno o più MDL. Se l'IRP dispone di un singolo MDL associato, il membro MdlAddress di IRP punta a tale MDL. Se a questo oggetto sono associati più MDL, MdlAddress punta al primo MDL in un elenco collegato di MDLs associato all'IRP, noto come catena MDL. Gli mdls sono collegati dai relativi membri Next . Il membro Next dell'ultimo MDL nella catena è impostato su NULL.
Se, quando il driver chiama IoAllocateMdl, specifica FALSE per il parametro SecondaryBuffer , il membro MdlAddress di IRP viene impostato in modo che punti al nuovo MDL. Se SecondaryBuffer è TRUE, la routine inserisce il nuovo MDL alla fine della catena MDL.
Al termine dell'IRP, il sistema sblocca e libera tutti gli ELENCHI MDL associati all'IRP. Il sistema sblocca gli ELENCHI di dati dati prima di accodamento della routine di completamento di I/O e li libera dopo l'esecuzione della routine di completamento di I/O.
I driver possono attraversare la catena MDL usando il membro Next di ogni MDL per accedere al successivo MDL nella catena. I driver possono inserire manualmente mdls nella catena aggiornando i membri Next .
Le catene MDL vengono in genere usate per gestire una matrice di buffer associati a una singola richiesta di I/O. Ad esempio, un driver di rete può usare un buffer per ogni pacchetto IP in un'operazione di rete. Ogni buffer nella matrice ha un proprio MDL nella catena. Quando il driver completa la richiesta, combina i buffer in un singolo buffer di grandi dimensioni. Il sistema pulisce quindi automaticamente tutti gli ELENCHI MDL allocati per la richiesta.
Il gestore di I/O è un'origine frequente di richieste di I/O. Quando il gestore di I/O completa una richiesta di I/O, il gestore di I/O libera l'IRP e libera tutti gli ELENCHI MDL collegati all'IRP. Alcuni di questi mdls potrebbero essere stati collegati all'IRP dai driver che si trovano sotto il gestore di I/O nello stack di dispositivi. Analogamente, se il driver è l'origine di una richiesta di I/O, il driver deve pulire l'IRP e tutti gli MDL collegati all'IRP al termine della richiesta di I/O.
Esempio
L'esempio di codice seguente è una funzione implementata dal driver che libera una catena MDL da un 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 le pagine fisiche descritte da un MDL nella catena sono bloccate, la funzione di esempio chiama la routine MmUnlockPages per sbloccare le pagine prima di chiamare IoFreeMdl per liberare il file MDL. Tuttavia, la funzione di esempio non deve annullare esplicitamente il mapping delle pagine prima di chiamare IoFreeMdl. IoFreeMdl rimuove automaticamente il mapping delle pagine quando libera il file MDL.