Objetos semáforos
Cualquier controlador puede usar un objeto semáforo para sincronizar las operaciones entre sus subprocesos creados por el controlador y otras rutinas de controlador. Por ejemplo, un subproceso dedicado al controlador podría colocarse en un estado de espera cuando no hay solicitudes de E/S pendientes para el controlador y las rutinas de distribución del controlador podrían establecer el semáforo en el estado Señalizado justo después de poner en cola un IRP.
Las rutinas de envío de controladores de nivel superior, que se ejecutan en el contexto del subproceso que solicita una operación de E/S, pueden usar un semáforo para proteger un recurso compartido entre las rutinas de envío. Las rutinas de distribución de controladores de nivel inferior para las operaciones de E/S sincrónicas también pueden usar un semáforo para proteger un recurso compartido entre ese subconjunto de rutinas de envío o con un subproceso creado por el controlador.
Cualquier controlador que use un objeto semáforo debe llamar a KeInitializeSemaphore antes de esperar o liberar el semáforo. En la ilustración siguiente se muestra cómo un controlador con un subproceso puede usar un objeto semáforo.
Como se muestra en la ilustración anterior, este controlador debe proporcionar el almacenamiento para el objeto semáforo, que debe estar residente. El controlador puede usar la extensión de dispositivo de un objeto de dispositivo creado por el controlador, la extensión del controlador si usa un objeto de controlador o un grupo no paginado asignado por el controlador.
Cuando la rutina AddDevice del controlador llama a KeInitializeSemaphore, debe pasar un puntero al almacenamiento residente del controlador para el objeto semáforo. Además, el autor de la llamada debe especificar un count para el objeto semáforo, como se muestra en la ilustración anterior, que determina su estado inicial (distinto de cero para Signaled).
El autor de la llamada también debe especificar un límite para el semáforo, que puede ser cualquiera de los siguientes:
Límite = 1
Cuando este semáforo se establece en el estado Signaled, un único subproceso en espera de que el semáforo se establezca en el estado Signaled se puede ejecutar y puede acceder a cualquier recurso protegido por el semáforo.
Este tipo de semáforo también se denomina semáforo binario porque un subproceso tiene o no tiene acceso exclusivo al recurso protegido por semáforos.
Límite > 1
Cuando este semáforo se establece en el estado Signaled, algunos subprocesos que esperan que el objeto semáforo se establezca en el estado Signaled se pueda ejecutar y pueda acceder a cualquier recurso protegido por el semáforo.
Este tipo de semáforo se denomina semáforo de recuento porque la rutina que establece el semáforo en el estado Signaled también especifica cuántos subprocesos en espera pueden cambiar de esperar a listo. El número de estos subprocesos en espera puede ser el límite establecido cuando se inicializó el semáforo o algún número menor que este límite preestablecido.
Algunos controladores intermedios o de dispositivo tienen un único subproceso creado por el controlador; incluso menos tienen un conjunto de subprocesos que pueden esperar a que se adquiera o libere un semáforo. Algunos controladores proporcionados por el sistema usan objetos semáforos y, de los que sí, incluso menos usan un semáforo binario. Aunque un semáforo binario puede parecer similar en la funcionalidad de un objeto de exclusión mutua, un semáforo binario no proporciona la protección integrada contra interbloqueos que un objeto de exclusión mutua tiene para subprocesos del sistema que se ejecutan en máquinas SMP.
Después de cargar un controlador con un semáforo inicializado, puede sincronizar las operaciones en el semáforo que protege un recurso compartido. Por ejemplo, un controlador con un subproceso dedicado al dispositivo que administra la puesta en cola de IRP, como el controlador del controlador de disquete del sistema, podría sincronizar la puesta en cola de IRP en un semáforo, como se muestra en la ilustración anterior:
El subproceso llama a KeWaitForSingleObject con un puntero al almacenamiento proporcionado por el controlador para el objeto semáforo inicializado para colocarse en un estado de espera.
Los IRP comienzan a entrar en que requieren operaciones de E/S del dispositivo. Las rutinas de distribución del controlador insertan cada IRP en una cola interbloqueada bajo el control de bloqueo de número y llaman a KeReleaseSemaphore con un puntero al objeto semáforo, un aumento de prioridad determinado por el controlador para el subproceso (Incremento, como se muestra en la ilustración anterior), un ajuste de 1 que se agrega al contador del semáforo cuando se pone en cola cada IRP y una espera booleana establecida en FALSE. Un recuento de semáforos distinto de cero establece el objeto semáforo en el estado Señalizado, cambiando así el estado del subproceso en espera para que esté listo.
El kernel envía el subproceso para su ejecución tan pronto como un procesador esté disponible: es decir, ningún otro subproceso con una prioridad más alta está actualmente en estado listo y no hay rutinas en modo kernel que se ejecuten en un IRQL superior.
El subproceso quita un IRP de la cola interbloqueada bajo el control de bloqueo de número, lo pasa a otras rutinas de controlador para su posterior procesamiento y vuelve a llamar a KeWaitForSingleObject . Si el semáforo todavía se establece en el estado Signaled (es decir, su recuento sigue siendo distinto de cero, lo que indica que hay más IRP en la cola interbloqueada del controlador), el kernel cambia de nuevo el estado del subproceso de esperar a listo.
Al usar un semáforo de recuento de esta manera, este subproceso de controlador "sabe" que hay un IRP que se va a quitar de la cola interbloqueada cada vez que se ejecuta ese subproceso.
Para obtener información específica sobre cómo administrar IRQL al llamar a KeReleaseSemaphore, consulta la sección Comentarios de KeReleaseSemaphore.
Cualquier rutina de controlador estándar que se ejecute en un IRQL mayor que PASSIVE_LEVEL no puede esperar un intervalo distinto de cero en cualquier objeto distribuidor sin bajar el sistema; consulte Kernel Dispatcher Objects (Objetos de distribuidor de kernel ) para obtener más información. Sin embargo, esta rutina puede llamar a KeReleaseSemaphore mientras se ejecuta en un IRQL menor o igual que DISPATCH_LEVEL.
Para obtener un resumen de las IRQL en las que se ejecutan las rutinas de controlador estándar, consulte Administración de prioridades de hardware. Para conocer los requisitos de IRQL de una rutina de soporte técnico específica, consulte la página de referencia de la rutina.