Programmazione preventiva con "non disturbare"

4

In questa risposta sul threading si dice:

In preemptive scheduling, a thread can be interrupted at any time, either by a timer interrupt or any other interrupt or during a system call. The part of the system that performs the context switch must save all the registers and other settings, and restore the context of the new thread.

It's harder to write programs for systems with preemptive scheduling: they're prone to race conditions. On the other hand, these systems are more robust in that even if a thread is buggy and goes into an infinite loop, that doesn't prevent other threads from running.

È possibile creare uno scheduler preventivo in cui i thread possono appendere un cartello "non disturbare" sulla loro porta. Se il segno è presente, lo scheduler non esegue un cambio di contesto. Sarebbe un po 'come una versione preistorica della parola chiave synchronized di Java.

Immagino che potrebbe essere più facile da programmare perché puoi contrassegnare le sezioni critiche e farle sempre eseguire direttamente una dopo l'altra. Ad esempio, se stai cambiando i registri di configurazione di più microcontrollori, è possibile che a metà di questo cambiamento le impostazioni non siano come vorrai da molto tempo. Non vuoi che un cambio di contesto avvenga proprio in quel momento.

È stato fatto qualcosa del genere?

Per quanto riguarda l'implementazione, sembra che impostare e cancellare un po 'quando si entra o si lasci la sezione critica sarebbe sufficiente. Lo scheduler controlla questo bit prima di eseguire un cambio di contesto e non agisce se è 0 . Sarebbe davvero abbastanza?

Uno scheduler più avanzato potrebbe tenere traccia di quanto tempo un thread si trova nella sezione critica. Se quello è "troppo lungo", può ancora eseguire l'interruttore di contesto, possibilmente notificando prima il thread. Questo "troppo lungo" può essere definito dallo schedulatore o dal thread: vedo vantaggi per entrambi gli approcci.

    
posta Keelan 06.08.2015 - 08:18
fonte

2 risposte

6

C'è molta varietà di programmatori perché c'è una grande varietà di esigenze, dai sistemi in tempo reale in cui l'intero sistema è scritto in una volta sola e l'ordine preciso e i tempi della pianificazione sono noti quando il dispositivo è costruito, per sistemi di fascia alta in cui tutta la filettatura è preventiva tranne per alcune sezioni molto brevi nel kernel.

È certamente possibile che un thread dichiari che si trova in una sezione critica e non vuole essere interrotto. Per definizione, questo non è più uno scheduler completamente preventivo, ma una sorta di modalità ibrida basata sulla prelazione, ma in cui la prevenzione può essere disabilitata.

In effetti, praticamente ogni sistema contiene almeno un codice di basso livello che non deve essere prevenuto perché sta facendo qualcosa all'hardware, come nell'esempio che si dà di modificare registri multipli che devono essere eseguiti in un colpo solo. Questo non è limitato ai microcontrollori. I gestori di interrupt e il codice di commutazione del contesto contengono quasi sempre una sezione critica, anche se si tratta di poche istruzioni. A livello di metallo nudo, una sezione critica significa semplicemente disabilitare gli interrupt. I gestori di interrupt iniziano con gli interrupt disabilitati o, in caso contrario, esiste almeno un meccanismo che impedisce allo stesso interrupt di interrompere un'istanza precedente di se stesso.

I sistemi operativi progettati per hardware single-core possono utilizzare sezioni critiche per implementare meccanismi di trasmissione dei messaggi, poiché il passaggio di un messaggio di solito comporta la modifica di più strutture di dati (associate al mittente e al destinatario) in modo coerente. Sui sistemi multi-core, questo non può essere fatto perché il mittente e il ricevitore potrebbero essere in esecuzione su diversi core.

A un livello superiore, esistono sistemi operativi in cui i thread possono garantire che non verranno interrotti e l'API offre chiamate di sistema come start_critical_section / end_critical_section . Questo può essere fatto solo se si ritiene che il thread non entri in un loop infinito o che curi la CPU. Una tale sezione critica potrebbe infatti essere interrotta da interrupt, ma lo scheduler ritorna allo stesso thread dopo l'interruzione.

Sono possibili sezioni critiche che sono interrompibili dopo un timeout. Non so quanto siano comuni; Avrebbero senso in sistemi "soft real-time" come l'elaborazione multimediale in cui i thread hanno bisogno di elaborare fotogrammi audio o video in modo tempestivo, e la caduta di un fotogramma è disapprovata ma non esclusa.

Informare un thread che è stato preventivato è difficile da fare in modo utile. Impostare un po 'in una posizione ben nota è facile. Il problema è come reagisce il thread. È stato indicato che non voleva essere interrotto, quindi presumibilmente non controllerà quel bit. Un possibile progetto prevede che il thread abbia i checkpoint; ad esempio, in un ciclo esterno durante l'elaborazione dei frame, un thread di elaborazione dei media può controllare se il frame corrente è stato eliminato. Un altro possibile progetto è quello di uccidere il thread (ma eventualmente lasciare lo stack dietro per l'analisi) ed eseguire un altro thread come gestore di eccezioni. Ancora un altro design è di sollevare un'eccezione all'interno del thread; ciò richiede un accoppiamento stretto tra il meccanismo di pianificazione e l'ambiente di esecuzione del thread.

Esistono altri modi per limitare i modi in cui i thread possono essere anticipati ad un livello elevato. Uno semplice è prioritario: uno scheduler predisporrà solo un thread per eseguire un thread con priorità più alta. Se il thread A non vuole essere interrotto dal thread B, A dichiara una priorità più alta di B.

In un sistema multi-applicazione in cui ogni applicazione può avere più thread, può avere senso avere uno scheduler ibrido che preveda preventivamente le applicazioni, ma cooperando tra i thread della stessa applicazione. In questo modo, un thread di applicazione che non restituisce mai impedirà l'esecuzione solo dei thread nella stessa applicazione. Le applicazioni possono utilizzare la memoria condivisa con gli switch di contesto solo in momenti prevedibili per i suoi meccanismi interni, mentre le comunicazioni tra le applicazioni avvengono solo tramite primitive di messaggistica del sistema operativo.

    
risposta data 06.08.2015 - 10:04
fonte
1

Un esempio di un sistema che funzionava in questo modo è il kernel di Linux, che aveva una coppia di funzioni chiamate cli () e sti (). Sui sistemi a processore singolo, questi hanno semplicemente disabilitato e riattivato gli interrupt, impedendo così l'interruzione del thread corrente (su macchine multiprocessore erano un po 'più complessi). Sono stati deprecati all'inizio dello sviluppo del kernel e completamente rimossi durante le versioni di sviluppo 2.5.x del kernel.

    
risposta data 14.08.2015 - 22:02
fonte

Leggi altre domande sui tag