Bloquear código paginable o datos

Algunos controladores en modo kernel, como los controladores serie y paralelo, no tienen que residir en memoria a menos que los dispositivos que administran estén abiertos. Sin embargo, siempre que haya una conexión o puerto activo, parte del código de controlador que administra ese puerto debe estar residente para atender el dispositivo. Cuando no se usa el puerto o la conexión, no se requiere el código del controlador. Por el contrario, un controlador para un disco que contiene código del sistema, código de aplicación o archivo de paginación del sistema siempre debe residir en memoria porque el controlador transfiere constantemente datos entre su dispositivo y el sistema.

Un controlador para un dispositivo usado esporádicamente (como un módem) puede liberar espacio del sistema cuando el dispositivo que administra no está activo. Si coloca en una sola sección el código que debe estar residente para atender un dispositivo activo y, si el controlador bloquea el código en memoria mientras se usa el dispositivo, esta sección se puede designar como paginable. Cuando se abre el dispositivo del controlador, el sistema operativo pone la sección paginable en la memoria y el controlador lo bloquea hasta que ya no sea necesario.

El código del controlador de audio cd del sistema usa esta técnica. El código del controlador se agrupa en secciones paginables según el fabricante del dispositivo CD. Es posible que algunas marcas nunca estén presentes en un sistema determinado. Además, incluso si existe un CD-ROM en un sistema, es posible que se tenga acceso con poca frecuencia, por lo que la agrupación de código en secciones paginables por tipo de CD garantiza que nunca se cargue el código de los dispositivos que no existen en un equipo determinado. Sin embargo, cuando se accede al dispositivo, el sistema carga el código para el dispositivo de CD adecuado. A continuación, el controlador llama a la rutina MmLockPagableCodeSection , como se describe a continuación, para bloquear su código en la memoria mientras se usa su dispositivo.

Para aislar el código paginable en una sección con nombre, lítelo con la siguiente directiva del compilador:

#pragma alloc_text(PAGE*Xxx, *RoutineName)

El nombre de una sección de código paginable debe comenzar con las cuatro letras "PAGE" y puede ir seguido de hasta cuatro caracteres (representados aquí como Xxx) para identificar de forma única la sección. Las cuatro primeras letras del nombre de sección (es decir, "PAGE") deben escribirse en mayúsculas. RoutineName identifica un punto de entrada que se va a incluir en la sección paginable.

El nombre válido más corto para una sección de código paginable en un archivo de controlador es simplemente PAGE. Por ejemplo, la directiva pragma del ejemplo de código siguiente identifica RdrCreateConnection como punto de entrada en una sección de código paginable denominada PAGE.

#ifdef  ALLOC_PRAGMA 
#pragma alloc_text(PAGE, RdrCreateConnection) 
#endif 

Para que el código de controlador paginable resida y se bloquee en la memoria, un controlador llama a MmLockPagableCodeSection, pasando una dirección (normalmente el punto de entrada de una rutina de controlador) que se encuentra en la sección de código paginable. MmLockPagableCodeSection bloquea todo el contenido de la sección que contiene la rutina a la que se hace referencia en la llamada. En otras palabras, esta llamada realiza todas las rutinas asociadas con el mismo identificador PAGEXxx residente y bloqueado en la memoria.

MmLockPagableCodeSection devuelve un identificador que se usará al desbloquear la sección (llamando a la rutina MmUnlockPagableImageSection ) o cuando el controlador debe bloquear la sección de ubicaciones adicionales en su código.

Un controlador también puede tratar los datos que rara vez se usan como paginables para que también se pueda paginar hasta que el dispositivo que admita esté activo. Por ejemplo, el controlador mezclador del sistema usa datos paginables. El dispositivo mezclador no tiene ninguna E/S asincrónica asociada, por lo que este controlador puede hacer que sus datos sean paginables.

El nombre de una sección de datos paginable debe comenzar con las cuatro letras "PAGE" y puede ir seguida de hasta cuatro caracteres para identificar de forma única la sección. Las cuatro primeras letras del nombre de sección (es decir, "PAGE") deben escribirse en mayúsculas.

Evite asignar nombres idénticos a secciones de código y datos. Para que el código fuente sea más legible, los desarrolladores de controladores suelen asignar el nombre PAGE a la sección de código paginable porque este nombre es corto y puede aparecer en numerosas directivas pragma de alloc_text. A continuación, se asignan nombres más largos a cualquier sección de datos paginable (por ejemplo, PAGEDATA para data_seg, PAGEBSS para bss_seg, etc.) que el controlador podría necesitar.

Por ejemplo, las dos primeras directivas pragma del ejemplo de código siguiente definen dos secciones de datos paginables, PAGEDATA y PAGEBSS. PAGEDATA se declara mediante la directiva pragma de data_seg y contiene datos inicializados. PAGEBSS se declara mediante la directiva pragma de bss_seg y contiene datos no inicializados.

#pragma data_seg("PAGEDATA")
#pragma bss_seg("PAGEBSS")

INT Variable1 = 1;
INT Variable2;

CHAR Array1[64*1024] = { 0 };
CHAR Array2[64*1024];

#pragma data_seg()
#pragma bss_seg()

En este ejemplo de código, y Array1 se inicializan explícitamente y, por tanto, Variable1 se colocan en la sección PAGEDATA. Variable2 y Array2 se inicializan implícitamente cero y se colocan en la sección PAGEBSS.

La inicialización implícita de variables globales en cero reduce el tamaño del archivo ejecutable en disco y se prefiere sobre la inicialización explícita a cero. Se debe evitar la inicialización explícita de cero excepto en los casos en los que se requiera para colocar una variable en una sección de datos específica.

Para que una sección de datos resida en memoria y la bloquee, un controlador llama a MmLockPagableDataSection, pasando un elemento de datos que aparece en la sección de datos paginable. MmLockPagableDataSection devuelve un identificador que se usará en solicitudes de bloqueo o desbloqueo posteriores.

Para restaurar el estado paginable de una sección bloqueada, llame a MmUnlockPagableImageSection y pase el valor de identificador devuelto por MmLockPagableCodeSection o MmLockPagableDataSection, según corresponda. La rutina Unload de un controlador debe llamar a MmUnlockPagableImageSection para liberar cada identificador que ha obtenido para secciones de datos y código bloqueables.

Bloquear una sección es una operación costosa porque el administrador de memoria debe buscar en su lista de módulos cargados antes de bloquear las páginas en la memoria. Si un controlador bloquea una sección de muchas ubicaciones en su código, debe usar la sección MmLockPagableSectionByHandle más eficaz después de su llamada inicial a MmLockPagableXxxSection.

El identificador pasado a MmLockPagableSectionByHandle es el identificador devuelto por la llamada anterior a MmLockPagableCodeSection o MmLockPagableDataSection.

El administrador de memoria mantiene un recuento para cada identificador de sección e incrementa este recuento cada vez que un controlador llama a MmLockPagableXxx para esa sección. Una llamada a MmUnlockPagableImageSection disminuye el recuento. Aunque el contador de cualquier identificador de sección no es cero, esa sección permanece bloqueada en la memoria.

El identificador de una sección es válido siempre que se cargue su controlador. Por lo tanto, un controlador debe llamar a MmLockPagableXxxSection solo una vez. Si el controlador requiere llamadas de bloqueo adicionales, debe usar MmLockPagableSectionByHandle.

Si un controlador llama a cualquier rutina MmLockPagableXxx para una sección que ya está bloqueada, el administrador de memoria incrementa el recuento de referencias de la sección. Si la sección se pagina cuando se llama a la rutina de bloqueo, las páginas del administrador de memoria de la sección y establecen su recuento de referencias en uno.

El uso de esta técnica minimiza el efecto del controlador en los recursos del sistema. Cuando se ejecuta el controlador, puede bloquear en la memoria el código y los datos que deben estar residentes. Cuando no hay solicitudes de E/S pendientes para su dispositivo (es decir, cuando el dispositivo está cerrado o si el dispositivo nunca se ha abierto), el controlador puede desbloquear el mismo código o datos, lo que hace que esté disponible para que se muestre.

Sin embargo, después de que un controlador haya conectado interrupciones, cualquier código de controlador al que se pueda llamar durante el procesamiento de interrupciones siempre debe ser residente en memoria. Aunque algunos controladores de dispositivos pueden ser paginables o bloqueados en memoria a petición, algunos conjuntos principales de código y datos de este tipo de controlador deben residir permanentemente en el espacio del sistema.

Tenga en cuenta las siguientes directrices de implementación para bloquear un código o una sección de datos.

  • El uso principal de las rutinas Mm(Un)LockXxx es permitir que el código o los datos no paginados normalmente se puedan paginar y se traigan como código o datos no paginados. Los controladores como el controlador serie y el controlador paralelo son buenos ejemplos: si no hay identificadores abiertos para un dispositivo que administra un controlador, las partes del código no son necesarias y pueden permanecer paginadas. El redirector y el servidor también son buenos ejemplos de controladores que pueden usar esta técnica. Cuando no hay conexiones activas, ambos componentes se pueden paginar.

  • Toda la sección paginable está bloqueada en la memoria.

  • Una sección para el código y otra para los datos por controlador es eficaz. Muchas secciones con nombre y paginables suelen ser ineficaces.

  • Mantenga las secciones puramente paginables y las secciones paginadas pero bloqueadas a petición independientes.

  • Recuerde que MmLockPagableCodeSection y MmLockPagableDataSection no deben llamarse con frecuencia. Estas rutinas pueden provocar una actividad de E/S intensiva cuando el administrador de memoria carga la sección. Si un controlador debe bloquear una sección de varias ubicaciones en su código, debe usar MmLockPagableSectionByHandle.