Cómo compartir memoria entre servicios y procesos de usuario
Método IOCTL: la aplicación asigna memoria compartida
La forma más sencilla y eficaz de compartir memoria entre el modo usuario y el modo kernel es utilizar IOCTL. Hay cuatro tipos diferentes de IOCTL. Los siguientes tres IOCTL le permiten acceder directamente a los buffers de usuario en el controlador del dispositivo:
METHOD_IN_DIRECT
METHOD_OUT_DIRECT
METHOD_NEITHER
No se crean buffers de sistema intermedios en estos métodos.
METHOD_IN_DIRECT y METHOD_OUT_DIRECT bloquean automáticamente un búfer especificado por el usuario en DeviceIoControl y crean una lista de descripción de memoria (MDL) para el controlador. El controlador puede obtener una dirección del sistema del MDL y pasar información dentro y fuera de los buffers según el tipo de transferencia IOCTL. Este es un enfoque simple ya que toda la validación, el bloqueo y la creación de MDL del búfer se realizan a través del administrador de E/S.
Por el contrario, METHOD_NEITHER proporciona el búfer del usuario directamente al controlador, que debe validar correctamente el búfer y bloquearlo y, si es necesario, obtener una dirección del sistema para el búfer. Aunque esta es la ruta más rápida a través del subsistema de E/S del controlador del dispositivo, existen algunos problemas que debe tener en cuenta al utilizar este método. Para obtener información adicional, consulte el siguiente artículo en Microsoft Knowledge Base:
P126416 INFORMACIÓN: Advertencias sobre el uso de METHOD_NEITHER IOCTL
Para comprender cómo funcionan estos tres IOCTL diferentes, consulte el siguiente artículo de Microsoft Artículo de la base de conocimientos:
Q178317 Archivo: IOCTL.exe: cómo utilizar diferentes tipos de IOCTL
Método MmMapLockedPages: el controlador del dispositivo asigna memoria compartida
En este método , el controlador asigna memoria a través de la función MmAllocateContiguousMemory o ExAllocatePoolXxx, crea y construye el MDL que describe el búfer y usa MmMapLockedPages para asignar la memoria al espacio de direcciones del proceso del usuario. Las aplicaciones de usuario pueden acceder directamente a la memoria del sistema utilizando la dirección virtual devuelta por MmMapLockPages.
Dado que el mapeo debe realizarse en el contexto del proceso que accede a la memoria, este método solo se puede utilizar para controladores de una sola capa, donde se garantiza que la rutina de envío se ejecutará en el contexto del proceso de llamada. . Puede asignar el mismo búfer del sistema a cualquier número de espacios de direcciones de procesos de usuario. Sin embargo, se debe diseñar un mecanismo de protección para sincronizar el acceso a la memoria por parte del controlador y todas las aplicaciones. Además, el búfer debe desasignarse en el mismo contexto de proceso en el que se asignó antes de finalizar el proceso o tan pronto como se utilice el búfer.
Estos son los pasos a seguir para asignar un búfer de controlador a un proceso de usuario:
Primero asigne memoria de la siguiente manera:
SystemVirtualAddress = MmAllocateContiguousMemory(NumberOfBytes,
HighestAcceptableAddress ); o
SystemVirtualAddress = ExAllocatePool(PoolType, NumberOfBytes);
Asigne un MDL de la siguiente manera:
Mdl = IoAllocateMdl(SystemVirtualAddress, NumberOfBytes, FALSE,
FALSE, NULL);
Crear MDL para describir páginas de memoria. Si se ha asignado memoria en el grupo no paginado, utilice:
MmBuildMdlForNonPaggedPool(Mdl);
Si se ha asignado memoria en el grupo paginado, utilice:
MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
Asigne las páginas bloqueadas al espacio de direcciones de usuario del proceso de la siguiente manera:
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode
Los primeros tres pasos se pueden completar en la rutina DriverEntry o Dispatch. Sin embargo, el paso final de asignar el búfer al espacio de direcciones del usuario debe realizarse en una rutina que se ejecute en el contexto del proceso de llamada (normalmente la rutina de distribución del controlador de una sola capa).
Puede utilizar SystemVirtualAddress en un controlador en un nivel IRQL elevado y en cualquier contexto de proceso (es decir, rutina de servicio de interrupción (ISR) y llamada a procedimiento retrasado (DPC)), en una aplicación o ejecutar correctamente UserVirtualAddress es utilizado en el contexto del proceso del controlador.
Siga los siguientes pasos para desasignar y liberar el búfer:
Primero desasignar la página del espacio de direcciones del usuario. La función debe llamarse cuando se ejecuta en el contexto del proceso de la UserVirtualAddress asignada:
MmUnmapLockedPages(UserVirtualAddress, Mdl);
Si la página está bloqueada usando MmProbleAndLockPages, use la siguiente función para desbloquearlo:
MmUnlockPages(Mdl)
Liberar MDL:
IoFreeMdl(Mdl);
Liberar memoria del sistema:
MmFreeContiguousMemory( SystemVirtualAddress); o
ExFreePool(SystemVirtualAddress);
***Método de objeto de memoria compartida
Utilizando el archivo asignado en memoria guardado en se utiliza el archivo de página Una forma común de compartir memoria entre procesos de usuario. Sin embargo, puede utilizar la técnica anterior para compartir memoria entre procesos de usuario y controladores de dispositivos. Hay dos formas de utilizar esta técnica. En el primer enfoque, el controlador crea un objeto de memoria con nombre (llamado objeto de sección) y una o más aplicaciones obtienen un puntero a él abriendo el objeto usando OpenFileMapping y luego llamando a la función MapViewOfFile.
Al especificar los atributos de protección de los objetos anteriores, puede definir cómo un determinado proceso usa la memoria.
En el segundo enfoque, una aplicación puede usar CreateFileMapping para crear un objeto de memoria con nombre en modo de usuario. El controlador abre el objeto de memoria anterior utilizando la función ZwMapViewOfSection y llamando a ZwMapViewOfSection para obtener su puntero. Debe utilizar un controlador de excepciones para acceder a esta dirección de memoria en modo kernel. Para ver un ejemplo del uso de esta técnica, consulte el siguiente artículo de Microsoft Knowledge Base:
Ejemplo Q194945: Sección.exe sobre cómo compartir memoria entre el modo kernel y usuario
Porque los objetos siempre se asignan en El espacio de direcciones de usuario del proceso (menos de 0x80000000, ya sea que el objeto se cree en modo kernel o en modo usuario), la dirección solo es válida si se accede a ella en el contexto del proceso. Cada llamada a MapViewOfFile o ZwMapViewOfSection en el mismo objeto de memoria devolverá una dirección de memoria diferente, incluso para el mismo proceso. Generalmente no se recomienda este enfoque, especialmente para controladores de dispositivos de bajo nivel. Como se mencionó anteriormente, esto se debe a que el rango de direcciones está restringido al proceso del objeto mapeado y no se puede acceder a él en DPC o ISR. Además, la API para crear objetos de memoria en modo central no está documentada en el DDK.
Sin embargo, para usar la dirección en un IRQL elevado (como un DPC o ISR), debe identificar y bloquear la página del búfer y obtener la dirección virtual del sistema como se describe en el método IOCTL (MmGetSystemAddressForMdl).
Este método es relativamente simple y fácil sólo si está preparado para compartir memoria entre dos (o más) procesos de usuario y uno (o más) controladores de dispositivo. De lo contrario, sería más sencillo y eficiente utilizar la tecnología IOCTL para compartir memoria entre un proceso de usuario y un controlador de dispositivo.