Oggetti semafori

Qualsiasi driver può usare un oggetto semaforo per sincronizzare le operazioni tra i thread creati dal driver e altre routine del driver. Ad esempio, un thread dedicato al driver potrebbe entrare in uno stato di attesa quando non sono presenti richieste di I/O in sospeso per il driver e le routine dispatch del driver potrebbero impostare il semaforo sullo stato Segnalato subito dopo aver accodato un IRP.

Le routine dispatch dei driver di livello più alto, che vengono eseguiti nel contesto del thread che richiede un'operazione di I/O, possono usare un semaforo per proteggere una risorsa condivisa tra le routine dispatch. Le routine di invio dei driver di livello inferiore per le operazioni di I/O sincrone possono anche usare un semaforo per proteggere una risorsa condivisa tra il sottoinsieme di routine dispatch o con un thread creato dal driver.

Qualsiasi driver che usa un oggetto semaforo deve chiamare KeInitializeSemaphore prima di attendere o rilasciare il semaforo. La figura seguente illustra come un driver con un thread può usare un oggetto semaforo.

diagramma che illustra l'attesa di un oggetto semaforo.

Come illustrato nella figura precedente, tale driver deve fornire lo spazio di archiviazione per l'oggetto semaforo, che deve essere residente. Il driver può usare l'estensione del dispositivo di un oggetto dispositivo creato dal driver, l'estensione del controller se usa un oggetto controller o un pool non di paging allocato dal driver.

Quando la routine AddDevice del driver chiama KeInitializeSemaphore, deve passare un puntatore alla risorsa di archiviazione residente del driver per l'oggetto semaforo. Inoltre, il chiamante deve specificare un conteggio per l'oggetto semaforo, come illustrato nella figura precedente, che determina lo stato iniziale (diverso da zero per Signaled).

Il chiamante deve anche specificare un limite per il semaforo, che può essere uno dei seguenti:

  • Limite = 1

    Quando questo semaforo è impostato sullo stato Signaled, un singolo thread in attesa che il semaforo venga impostato sullo stato Signaled diventa idoneo per l'esecuzione e può accedere a qualsiasi risorsa protetta dal semaforo.

    Questo tipo di semaforo è detto anche semaforo binario perché un thread ha o non ha accesso esclusivo alla risorsa protetta dal semaforo.

  • Limite > 1

    Quando questo semaforo è impostato sullo stato Signaled, alcuni thread in attesa che l'oggetto semaforo venga impostato sullo stato Signaled diventa idoneo per l'esecuzione e può accedere a qualsiasi risorsa protetta dal semaforo.

    Questo tipo di semaforo viene chiamato semaforo di conteggio perché la routine che imposta il semaforo sullo stato Signaled specifica anche il numero di thread in attesa che i relativi stati vengano modificati dall'attesa alla preparazione. Il numero di thread in attesa può essere il limite impostato quando il semaforo è stato inizializzato o un numero minore di questo limite predefinito.

Pochi driver di dispositivo o intermedi hanno un singolo thread creato dal driver; anche meno hanno un set di thread che potrebbero attendere l'acquisizione o il rilascio di un semaforo. Pochi driver forniti dal sistema usano oggetti semaforo e, di quelli che lo fanno, anche meno usano un semaforo binario. Anche se un semaforo binario potrebbe sembrare simile alla funzionalità di un oggetto mutex, un semaforo binario non fornisce la protezione predefinita contro i deadlock che un oggetto mutex ha per i thread di sistema in esecuzione nei computer SMP.

Dopo il caricamento di un driver con un semaforo inizializzato, può sincronizzare le operazioni sul semaforo che protegge una risorsa condivisa. Ad esempio, un driver con un thread dedicato al dispositivo che gestisce l'accodamento dei provider di integrazione, ad esempio il driver del controller floppy di sistema, potrebbe sincronizzare l'accodamento IRP in un semaforo, come illustrato nella figura precedente:

  1. Il thread chiama KeWaitForSingleObject con un puntatore alla risorsa di archiviazione fornita dal driver per l'oggetto semaforo inizializzato per inserirlo in uno stato di attesa.

  2. I runtime di integrazione iniziano a venire in che richiedono operazioni di I/O del dispositivo. Le routine dispatch del driver inseriscono ogni IRP in una coda interlocked sotto il controllo spin-lock e chiamano KeReleaseSemaphore con un puntatore all'oggetto semaforo, un boost di priorità determinato dal driver per il thread (Incremento, come illustrato nella figura precedente), una regolazione di 1 aggiunta al conteggio del semaforo perché ogni IRP è in coda e un valore Boolean Wait impostato su FALSE. Un semaforo diverso da zero Count imposta l'oggetto semaforo sullo stato Signaled, modificando così lo stato del thread in attesa su pronto.

  3. Il kernel invia il thread per l'esecuzione non appena è disponibile un processore, ovvero nessun altro thread con priorità più alta è attualmente nello stato pronto e non sono presenti routine in modalità kernel da eseguire in un irQL superiore.

    Il thread rimuove un IRP dalla coda interlocked sotto il controllo spin-lock, lo passa ad altre routine driver per un'ulteriore elaborazione e chiama di nuovo KeWaitForSingleObject . Se il semaforo è ancora impostato sullo stato Signaled (ovvero il numero rimane diverso da zero, a indicare che nella coda interlocked del driver sono presenti più irp), il kernel modifica nuovamente lo stato del thread dall'attesa alla preparazione.

    Usando un semaforo di conteggio in questo modo, tale thread driver "sa" esiste un IRP da rimuovere dalla coda interlocked ogni volta che viene eseguito il thread.

Per informazioni specifiche per la gestione di IRQL quando si chiama KeReleaseSemaphore, vedere la sezione Osservazioni di KeReleaseSemaphore.

Qualsiasi routine del driver standard eseguita in un IRQL maggiore di PASSIVE_LEVEL non può attendere un intervallo diverso da zero su qualsiasi oggetto dispatcher senza arrestare il sistema; per informazioni dettagliate, vedere Oggetti dispatcher kernel . Tuttavia, tale routine può chiamare KeReleaseSemaphore durante l'esecuzione in un IRQL minore o uguale a DISPATCH_LEVEL.

Per un riepilogo dei runtime di integrazione in cui vengono eseguite le routine del driver standard, vedere Gestione delle priorità hardware. Per i requisiti IRQL di una routine di supporto specifica, vedere la pagina di riferimento della routine.