Uso de MDL
Un búfer de E/S que abarca un intervalo de direcciones de memoria virtual contiguas se puede distribuir en varias páginas físicas y estas páginas pueden ser desconcertantes. El sistema operativo usa una lista de descriptores de memoria (MDL) para describir el diseño de página físico de un búfer de memoria virtual.
Una MDL consta de una estructura MDL seguida de una matriz de datos que describe la memoria física en la que reside el búfer de E/S. El tamaño de una MDL varía según las características del búfer de E/S que describe la MDL. Las rutinas del sistema están disponibles para calcular el tamaño necesario de una MDL y para asignar y liberar MDL.
Una estructura MDL es semi opaca. El controlador solo debe acceder directamente a los miembros Next y MdlFlags de esta estructura. Para obtener un ejemplo de código que use estos dos miembros, vea la siguiente sección Ejemplo.
Los miembros restantes de una MDL son opacos. No acceda directamente a los miembros opacos de una MDL. En su lugar, use las siguientes macros, que proporciona el sistema operativo para realizar operaciones básicas en la estructura:
MmGetMdlVirtualAddress devuelve la dirección de memoria virtual del búfer de E/S descrito por MDL.
MmGetMdlByteCount devuelve el tamaño, en bytes, del búfer de E/S.
MmGetMdlByteOffset devuelve el desplazamiento dentro de una página física del principio del búfer de E/S.
Puede asignar una MDL con la rutina IoAllocateMdl . Para liberar la MDL, use la rutina IoFreeMdl . Como alternativa, puede asignar un bloque de memoria no paginada y, a continuación, dar formato a este bloque de memoria como MDL llamando a la rutina MmInitializeMdl .
Ni IoAllocateMdl ni MmInitializeMdl inicializan la matriz de datos que sigue inmediatamente a la estructura MDL. Para un MDL que reside en un bloque asignado por el controlador de memoria no paginada, use MmBuildMdlForNonPagedPool para inicializar esta matriz para describir la memoria física en la que reside el búfer de E/S.
Para la memoria paginable, la correspondencia entre la memoria virtual y física es temporal, por lo que la matriz de datos que sigue a la estructura MDL solo es válida en determinadas circunstancias. Llame a MmProbeAndLockPages para bloquear la memoria paginable en su lugar e inicializar esta matriz de datos para el diseño actual. La memoria no se paginará hasta que el autor de la llamada use la rutina MmUnlockPages , momento en el que el contenido de la matriz de datos ya no es válido.
La rutina MmGetSystemAddressForMdlSafe asigna las páginas físicas descritas por el MDL especificado a una dirección virtual en el espacio de direcciones del sistema, si aún no están asignadas al espacio de direcciones del sistema. Esta dirección virtual es útil para los controladores que podrían tener que examinar las páginas para realizar E/S, ya que la dirección virtual original podría ser una dirección de usuario que solo se puede usar en su contexto original y se puede eliminar en cualquier momento.
Tenga en cuenta que, al compilar una MDL parcial mediante la rutina IoBuildPartialMdl , MmGetMdlVirtualAddress devuelve la dirección inicial original para la MDL parcial. Esta dirección es una dirección en modo de usuario si el MDL se creó originalmente como resultado de una solicitud en modo de usuario. Por lo tanto, la dirección no tiene relevancia fuera del contexto del proceso en el que se originó la solicitud.
Normalmente, un controlador crea en su lugar una dirección de modo del sistema llamando a la macro MmGetSystemAddressForMdlSafe para asignar la MDL parcial. Esto garantiza que el controlador pueda seguir accediendo a las páginas de forma segura independientemente del contexto del proceso.
Cuando un controlador llama a IoAllocateMdl, puede asociar un IRP con el MDL recién asignado especificando un puntero al IRP como parámetro Irp de IoAllocateMdl. Un IRP puede tener una o varias MDL asociadas. Si el IRP tiene una sola MDL asociada, el miembro MdlAddress del IRP apunta a ese MDL. Si el IRP tiene varias MDL asociadas, MdlAddress apunta a la primera MDL en una lista vinculada de MDL asociada a IRP, conocida como cadena MDL. Los MDL están vinculados por sus miembros Next . El miembro Next del último MDL de la cadena se establece en NULL.
Si, cuando el controlador llama a IoAllocateMdl, especifica FALSE para el parámetro SecondaryBuffer , el miembro MdlAddress del IRP se establece para que apunte al nuevo MDL. Si SecondaryBuffer es TRUE, la rutina inserta el nuevo MDL al final de la cadena MDL.
Cuando se completa el IRP, el sistema desbloquea y libera todas las MDL asociadas al IRP. El sistema desbloquea las MDL antes de poner en cola la rutina de finalización de E/S y las libera después de que se ejecute la rutina de finalización de E/S.
Los controladores pueden atravesar la cadena MDL mediante el siguiente miembro de cada MDL para acceder a la siguiente MDL de la cadena. Los controladores pueden insertar manualmente mdL en la cadena actualizando los miembros Next .
Normalmente, las cadenas MDL se usan para administrar una matriz de búferes que están asociadas a una única solicitud de E/S. (Por ejemplo, un controlador de red podría usar un búfer para cada paquete IP en una operación de red). Cada búfer de la matriz tiene su propia MDL en la cadena. Cuando el controlador completa la solicitud, combina los búferes en un único búfer grande. A continuación, el sistema limpia automáticamente todas las MDL asignadas para la solicitud.
El administrador de E/S es un origen frecuente de solicitudes de E/S. Cuando el administrador de E/S completa una solicitud de E/S, el administrador de E/S libera el IRP y libera todos los MDL que estén adjuntos al IRP. Algunos de estos MDL podrían haber sido conectados al IRP por los controladores que se encuentran debajo del administrador de E/S en la pila de dispositivos. Del mismo modo, si el controlador es el origen de una solicitud de E/S, el controlador debe limpiar el IRP y los MDL que estén conectados al IRP cuando se complete la solicitud de E/S.
Ejemplo
El ejemplo de código siguiente es una función implementada por el controlador que libera una cadena MDL de 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 las páginas físicas descritas por una MDL de la cadena están bloqueadas, la función de ejemplo llama a la rutina MmUnlockPages para desbloquear las páginas antes de llamar a IoFreeMdl para liberar MDL. Sin embargo, la función de ejemplo no necesita desasignación explícita de las páginas antes de llamar a IoFreeMdl. En su lugar, IoFreeMdl desasignación automáticamente las páginas cuando libera MDL.