Utilisation de MDLs
Une mémoire tampon d’E/S qui couvre une plage d’adresses de mémoire virtuelle contiguës peut être répartie sur plusieurs pages physiques, et ces pages peuvent être discontiguantes. Le système d’exploitation utilise une liste de descripteurs de mémoire (MDL) pour décrire la mise en page physique d’une mémoire tampon de mémoire virtuelle.
Un MDL se compose d’une structure MDL suivie d’un tableau de données qui décrit la mémoire physique dans laquelle réside la mémoire tampon d’E/S. La taille d’un MDL varie en fonction des caractéristiques de la mémoire tampon d’E/S décrite par le MDL. Des routines système sont disponibles pour calculer la taille requise d’une MDL et pour allouer et libérer le MDL.
Une structure MDL est semi-opaque. Votre pilote doit accéder directement uniquement aux membres Next et MdlFlags de cette structure. Pour obtenir un exemple de code qui utilise ces deux membres, consultez la section Exemple suivant.
Les autres membres d’un MDL sont opaques. N’accédez pas directement aux membres opaques d’un MDL. Au lieu de cela, utilisez les macros suivantes, que le système d’exploitation fournit pour effectuer des opérations de base sur la structure :
MmGetMdlVirtualAddress retourne l’adresse de mémoire virtuelle de la mémoire tampon d’E/S décrite par le MDL.
MmGetMdlByteCount retourne la taille, en octets, de la mémoire tampon d’E/S.
MmGetMdlByteOffset retourne le décalage dans une page physique du début de la mémoire tampon d’E/S.
Vous pouvez allouer un MDL avec la routine IoAllocateMdl . Pour libérer le MDL, utilisez la routine IoFreeMdl . Vous pouvez également allouer un bloc de mémoire non paginée, puis mettre en forme ce bloc de mémoire en tant que MDL en appelant la routine MmInitializeMdl .
Ni IoAllocateMdl ni MmInitializeMdl n’initialisent le tableau de données qui suit immédiatement la structure MDL. Pour un MDL qui réside dans un bloc de mémoire non paginée alloué au pilote, utilisez MmBuildMdlForNonPagedPool pour initialiser ce tableau afin de décrire la mémoire physique dans laquelle réside la mémoire tampon d’E/S.
Pour la mémoire paginable, la correspondance entre la mémoire virtuelle et la mémoire physique étant temporaire, le tableau de données qui suit la structure MDL n’est valide que dans certaines circonstances. Appelez MmProbeAndLockPages pour verrouiller la mémoire paginable en place et initialiser ce tableau de données pour la disposition actuelle. La mémoire n’est pas désintédée tant que l’appelant n’utilise pas la routine MmUnlockPages , auquel cas le contenu du tableau de données n’est plus valide.
La routine MmGetSystemAddressForMdlSafe mappe les pages physiques décrites par le MDL spécifié à une adresse virtuelle dans l’espace d’adressage système, si elles ne sont pas déjà mappées à l’espace d’adressage système. Cette adresse virtuelle est utile pour les pilotes qui peuvent avoir à consulter les pages pour effectuer des E/S, car l’adresse virtuelle d’origine peut être une adresse utilisateur qui ne peut être utilisée que dans son contexte d’origine et peut être supprimée à tout moment.
Notez que lorsque vous générez un MDL partiel à l’aide de la routine IoBuildPartialMdl , mmGetMdlVirtualAddress retourne l’adresse de départ d’origine de la MDL partielle. Cette adresse est une adresse en mode utilisateur si la MDL a été créée à l’origine à la suite d’une demande en mode utilisateur. Par conséquent, l’adresse n’est pas pertinente en dehors du contexte du processus d’origine de la demande.
En règle générale, un pilote crée une adresse en mode système en appelant la macro MmGetSystemAddressForMdlSafe pour mapper le MDL partiel. Cela garantit que le pilote peut continuer à accéder aux pages en toute sécurité, quel que soit le contexte du processus.
Lorsqu’un pilote appelle IoAllocateMdl, il peut associer un IRP au MDL nouvellement alloué en spécifiant un pointeur vers l’IRP comme paramètre Irpd’IoAllocateMdl. Un IRP peut avoir une ou plusieurs DLL associées. Si une seule MDL est associée à l’IRP, le membre MdlAddress de l’IRP pointe vers cette MDL. Si l’IRP est associé à plusieurs MDL, MdlAddress pointe vers la première MDL dans une liste liée de MDL associées à l’IRP, appelée chaîne MDL. Les dll MDL sont liées par leurs membres Next . Le membre Suivant du dernier MDL de la chaîne a la valeur NULL.
Si, lorsque le pilote appelle IoAllocateMdl, il spécifie FALSE pour le paramètre SecondaryBuffer , le membre MdlAddress d’IRP est défini pour pointer vers le nouveau MDL. Si SecondaryBuffer a la valeur TRUE, la routine insère le nouveau MDL à la fin de la chaîne MDL.
Une fois l’IRP terminé, le système déverrouille et libère toutes les dll MDL associées à l’IRP. Le système déverrouille les listes MDL avant de mettre en file d’attente la routine d’achèvement des E/S et les libère après l’exécution de la routine d’achèvement des E/S.
Les pilotes peuvent parcourir la chaîne MDL à l’aide du membre Next de chaque MDL pour accéder au MDL suivant dans la chaîne. Les pilotes peuvent insérer manuellement des DLL dans la chaîne en mettant à jour les membres Next .
Les chaînes MDL sont généralement utilisées pour gérer un tableau de mémoires tampons associées à une seule demande d’E/S. (Par exemple, un pilote réseau peut utiliser une mémoire tampon pour chaque paquet IP d’une opération réseau.) Chaque mémoire tampon du tableau a sa propre MDL dans la chaîne. Lorsque le pilote termine la requête, il combine les mémoires tampons en une seule mémoire tampon volumineuse. Le système nettoie ensuite automatiquement toutes les listes mdl allouées pour la demande.
Le gestionnaire d’E/ S est une source fréquente de demandes d’E/S. Lorsque le gestionnaire d’E/S termine une demande d’E/S, le gestionnaire d’E/S libère l’IRP et libère toutes les LDM attachées à l’IRP. Certaines de ces dll MDL peuvent avoir été attachées à l’IRP par des pilotes situés sous le gestionnaire d’E/S dans la pile d’appareils. De même, si votre pilote est la source d’une demande d’E/S, celui-ci doit propre l’IRP et toutes les LDM attachées à l’IRP lorsque la demande d’E/S est terminée.
Exemple
L’exemple de code suivant est une fonction implémentée par un pilote qui libère une chaîne MDL d’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);
}
}
Si les pages physiques décrites par un MDL dans la chaîne sont verrouillées, l’exemple de fonction appelle la routine MmUnlockPages pour déverrouiller les pages avant d’appeler IoFreeMdl pour libérer le MDL. Toutefois, l’exemple de fonction n’a pas besoin d’annuler explicitement le mappage des pages avant d’appeler IoFreeMdl. Au lieu de cela, IoFreeMdl annule automatiquement le mappage des pages lorsqu’il libère le MDL.