Protección de aislamiento
A partir de Windows XP, la protección de aislamiento está disponible para los controladores en modo kernel. Los controladores pueden usar la protección de aislamiento para acceder a objetos de forma segura en la memoria del sistema compartida que otro controlador en modo kernel crea y elimina.
Si un objeto está aislado significa que se han resuelto todos los accesos pendientes del objeto y no se aprobarán nuevas solicitudes para acceder al objeto. Por ejemplo, un objeto compartido puede necesitar quedar aislado para que se pueda eliminar y reemplazar por un nuevo objeto.
El controlador que posee el objeto compartido puede permitir que otros controladores adquieran y liberen la protección de aislamiento en el objeto. Cuando la protección de aislamiento está activa, un controlador distinto del propietario puede acceder al objeto sin riesgo de que el propietario elimine el objeto antes de que se complete el acceso. Antes de que se inicie el acceso, las solicitudes de acceso del controlador piden la protección de aislamiento en el objeto. En el caso de un objeto de larga duración, esta solicitud se concede casi siempre. Una vez resuelto el acceso, el controlador de acceso libera la protección de aislamiento adquirida anteriormente en el objeto.
Rutinas de protección de aislamiento principales
Para empezar a compartir un objeto, el controlador que posee el objeto llama a la rutina ExInitializeRundownProtection para inicializar la protección de aislamiento en el objeto. Después de llamarla, otros controladores que acceden al objeto pueden adquirir y liberar la protección de aislamiento en el objeto.
Un controlador que tiene acceso al objeto compartido llama a la rutina ExAcquireRundownProtection para pedir la protección de aislamiento en el objeto. Una vez resuelto el acceso, este controlador llama a la rutina ExReleaseRundownProtection para liberar la protección de aislamiento en el objeto.
Si el controlador propietario determina que se debe eliminar el objeto compartido, este controlador espera a eliminar el objeto hasta que se resuelvan todos los accesos pendientes del objeto.
Para preparar que se elimine el objeto compartido, el controlador propietario llama a la rutina ExWaitForRundownProtectionRelease para esperar a que el objeto esté en aislamiento. Al llamarla, ExWaitForRundownProtectionRelease espera a que se liberen todas las instancias de protección de aislamiento concedidas previamente en el objeto, pero impide que se concedan nuevas solicitudes para la protección de aislamiento en el objeto. Una vez que se resuelve el último acceso protegido y se liberan todas las instancias de protección de aislamiento, ExWaitForRundownProtectionRelease devuelve la operación y el controlador propietario puede eliminar el objeto de forma segura.
ExWaitForRundownProtectionRelease bloquea la ejecución del subproceso del controlador que realiza la llamada hasta que todos los controladores que tienen activada la protección de aislamiento en el objeto compartido liberan esta protección. Para evitar que ExWaitForRundownProtectionRelease bloquee la ejecución durante períodos demasiado largos, los subprocesos de controladores que acceden al objeto compartido deben evitar que se suspendan mientras mantienen la protección de aislamiento en el objeto. Por este motivo, el acceso a los controladores debe llamar a ExAcquireRundownProtection y ExReleaseRundownProtection dentro de una zona crítica o protegida, o al ejecutarse en IRQL = APC_LEVEL.
Usos de la protección de aislamiento
La protección de aislamiento es especialmente útil para dar acceso a un objeto compartido que casi siempre está disponible, pero que en ocasiones se tiene que eliminar y reemplazar. Los controladores que acceden a datos o que llaman a rutinas de este objeto no deben intentar acceder al objeto después de eliminarlo. De lo contrario, estos accesos no válidos pueden generar un resultado impredecible, daños en los datos o incluso errores del sistema.
Por ejemplo, el controlador de un antivirus normalmente se queda cargándose en la memoria cuando el sistema operativo se está ejecutando. En ocasiones, es posible que este controlador se deba descargar y reemplazar por una versión actualizada del controlador. Otros controladores envían solicitudes de E/S al controlador del antivirus para acceder a los datos y rutinas de este controlador. Antes de enviar una solicitud de E/S, un componente de kernel, como un administrador de filtros del sistema de archivos, puede hacer uso de la protección de aislamiento para evitar la descarga prematura del controlador del antivirus mientras ejecuta la solicitud de E/S. Una vez completada la solicitud de E/S, se puede liberar la protección de aislamiento.
La protección de aislamiento no serializa los accesos en un objeto compartido. Si dos o más controladores de acceso pueden incluir simultáneamente la protección de aislamiento en un objeto y los accesos al objeto deben serializarse, se debe usar algún otro mecanismo, como un bloqueo de exclusión mutua, para serializar los accesos.
Estructura EX_RUNDOWN_REF
La estructura EX_RUNDOWN_REF realiza un seguimiento del estado de la protección de aislamiento en un objeto compartido. Esta estructura es opaca para los controladores. Las rutinas de protección de aislamiento facilitadas por el sistema usan esta estructura para contar el número de instancias de protección de aislamiento que están actualmente en ejecución en el objeto. Estas rutinas también usan esta estructura para realizar un seguimiento del objeto para comprobar que está en aislamiento o está en proceso.
Para empezar a compartir un objeto, el controlador que posee el objeto llama a ExInitializeRundownProtection para inicializar la estructura EX_RUNDOWN_REF asociada al objeto. Después de inicializarla, el controlador propietario puede hacer que esta estructura esté disponible para otros controladores que necesitan acceder al objeto. Los controladores de acceso pasan esta estructura como parámetro a las llamadas de ExAcquireRundownProtection y ExReleaseRundownProtection que adquieren y liberan la protección de aislamiento en el objeto. El controlador propietario pasa esta estructura como parámetro a la llamada de ExWaitForRundownProtectionRelease que espera a que el objeto esté aislado para que se pueda eliminar de forma segura.
Comparación con los bloqueos
La protección de aislamiento es una de las distintas maneras de garantizar el acceso seguro a un objeto compartido. Otro método consiste en usar un bloqueo de software de exclusión mutua. Si un controlador necesita acceder a un objeto que está bloqueado en ese momento por otro controlador, el primer controlador debe esperar a que el segundo controlador libere el bloqueo. Sin embargo, si se adquieren y liberan bloqueos, esto puede conllevar a un cuello de botella en el rendimiento y los bloqueos pueden consumir grandes cantidades de memoria. Si se usan incorrectamente, los bloqueos pueden provocar que los controladores que compiten por los mismos objetos compartidos se bloqueen entre sí. Normalmente, las operaciones para detectar y evitar interbloqueos requieren que se desvíen recursos informáticos sustanciales.
A diferencia de los bloqueos, la protección de aislamiento tiene requisitos de tiempo de procesamiento y memoria relativamente de impacto leve. Hay un número de referencia simple que está asociado al objeto para asegurarse de que la eliminación del objeto queda pospuesta hasta que se completen todos los accesos pendientes del objeto. Con este método, se pueden usar instrucciones atómicas de hardware interbloqueadas en lugar de bloqueos de software de exclusión mutua para garantizar el acceso seguro a un objeto. Las llamadas para adquirir y liberar la protección de aislamiento suelen ser muy rápidas. Las ventajas de usar un mecanismo de impacto leve, como la protección de aislamiento, pueden ser importantes para un objeto compartido que tiene una larga vida útil y se comparte entre muchos controladores.
Otras rutinas de protección de aislamiento
Existen otras rutinas de protección de aislamiento, además de las mencionadas anteriormente. Estas rutinas adicionales las pueden usar algunos controladores.
La rutina ExReInitializeRundownProtection permite asociar la estructura EX_RUNDOWN_REF previamente usada a un nuevo objeto e inicializar la protección de aislamiento en el objeto en cuestión.
La rutina ExRundownCompleted actualiza la estructura EX_RUNDOWN_REF para indicar que se ha completado el aislamiento del objeto asociado.
Las rutinas ExAcquireRundownProtectionEx y ExReleaseRundownProtectionEx son similares a las rutinas ExAcquireRundownProtection y ExReleaseRundownProtection. Estas cuatro rutinas incrementan o reducen el número de instancias de protección de aislamiento que están activas en un objeto compartido. Mientras que ExAcquireRundownProtection y ExReleaseRundownProtection incrementan y reducen este número en uno, ExAcquireRundownProtectionEx y ExReleaseRundownProtectionEx incrementan y reducen el número por cantidades arbitrarias.
Protección de aislamiento con uso de caché
Una referencia de aislamiento es una estructura de datos compacta y rápida, pero puede crear contención de caché cuando muchos procesadores intentan adquirir la referencia al mismo tiempo. Esto puede afectar al rendimiento y la escalabilidad del controlador.
Para evitar este problema, puede usar una referencia de aislamiento compatible con la memoria caché para distribuir el seguimiento de referencias en varias líneas de caché. Esto reduce la contención del caché y mejora el rendimiento del controlador en equipos con varios procesadores.
Para usar una referencia de aislamiento compatible con la memoria caché, siga estos pasos:
- Cree un objeto EX_RUNDOWN_REF_CACHE_AWARE realizando una de las siguientes acciones:
- Llame a ExAllocateCacheAwareRundownProtection. Tenga en cuenta que esto sirve para realizar la inicialización.
- También, para controlar la asignación de memoria, puede llamar a ExSizeOfRundownProtectionCacheAware, asignar un búfer del tamaño devuelto y luego pasar ese búfer y tamaño a ExInitializeRundownProtectionCacheAware.
- Pida protección de aislamiento en el objeto antes de acceder a él llamando a la rutina ExAcquireRundownProtectionCacheAware. Esta rutina devuelve TRUE si se concede la solicitud o FALSE si el objeto está aislado.
- Libere la protección de aislamiento en el objeto después de acceder a él llamando a la rutina ExReleaseRundownProtectionCacheAware.
- Espere a que el objeto quede aislado antes de eliminarlo llamando a la rutina ExWaitForRundownProtectionReleaseCacheAware. Esta rutina bloquea el subproceso actual hasta que se liberan todas las instancias de protección de aislamiento en el objeto.
- Si el controlador ha llamado a ExAllocateCacheAwareRundownProtection anteriormente, se debe llamar a ExFreeCacheAwareRundownProtection para liberar la referencia de aislamiento.
Para reutilizar una referencia de aislamiento compatible con la memoria caché, siga estos pasos:
- Después de llamar a ExWaitForRundownProtectionReleaseCacheAware, llame a ExRundownCompletedCacheAware para indicar que se ha completado el aislamiento del objeto anterior.
- Llame a ExReInitializeRundownProtectionCacheAware para reinicializar la referencia después de que quede aislado el objeto asociado.
- Ahora el controlador podrá llamar de nuevo a ExAcquireRundownProtectionCacheAware.
Una referencia de aislamiento compatible con la memoria caché tiene la ventaja de mejorar el rendimiento y la escalabilidad en determinadas situaciones, pero consume más memoria que una referencia de aislamiento normal. Debe tener en cuenta esta compensación al elegir entre los dos tipos de referencias de aislamiento.