Come creare un semaforo in bash

Contenuti

Bash Shell

Stai sviluppando un'applicazione multithread?? Presto o tardi, probabilmente dovrai usare un semaforo. In questo articolo, imparerai cos'è un semaforo, come creare / pratica uno in bash e altro.

Che cos'è un? Semafori?

Un semaforo è un costrutto di programmazione utilizzato nei programmi per computer che impiegano più thread di elaborazione. (thread di elaborazione del computer che eseguono il codice sorgente dallo stesso programma o insieme di programmi) ottenere l'uso esclusivo di una risorsa comune in un dato momento. . Detto in modo molto più semplice, pensa a questo come “uno allo stesso tempo, Per favore”.

Un semaforo è stato definito per la prima volta alla fine degli anni '90. 1960 dal compianto informatico Edsger Dijkstra di Rotterdam nei Paesi Bassi. Probabilmente hai usato spesso i semafori nella tua vita senza renderti conto specificamente che lo stavi facendo!!

Resta in Olanda per un po', un paese pieno di piccoli corsi d'acqua e tanti ponti mobili (chiamato ponte levatoio in inglese americano), puoi vedere molti grandi esempi del mondo reale di un semaforo; considera la maniglia di un operatore a ponte levatoio: su o giù. Quella maniglia è la variabile del semaforo che protegge la via navigabile o l'autostrada dagli incidenti. A) Sì, la via navigabile e le strade potrebbero essere viste come altre variabili protette dal semaforo.

Se la maniglia è alzata e il ponte è aperto, l'uso esclusivo dell'intersezione acqua / la strada è data alla nave o alle navi che passano attraverso il canale d'acqua. Quando la maniglia è abbassata, il ponte viene chiuso e viene dato l'uso esclusivo dell'acqua di intersezione / strada per le auto che passano sul ponte.

La variabile semaforo può controllare l'accesso a un altro insieme di variabili. Come esempio, lo stato della maniglia impedisce il $cars_per_minute e $car_toll_booth_total_income variabili in modo che non vengano aggiornate, eccetera.

Possiamo portare l'esempio un po' oltre e spiegare che quando la maniglia si alza o si abbassa, entrambi i capitani delle barche nell'acqua e le persone che guidano auto e camion sulla strada possono vedere una luce corrispondente: tutti gli operatori possono leggere una variabile comune per uno stato specifico.

Anche se lo scenario qui descritto non è solo un semaforo, ma allo stesso tempo è un semplice mutex. Un mutex è un altro costrutto di programmazione comune molto simile a un semaforo, con la condizione aggiuntiva che un mutex può essere sbloccato solo dallo stesso task o thread che lo ha bloccato. mutex significa “si escludono a vicenda”.

Per questo caso, questo vale per il nostro esempio, poiché l'operatore del ponte è l'unico con il controllo sul nostro semaforo e mutex up / fuori uso. al contrario, se la guardiola alla fine del ponte avesse un interruttore di esclusione del ponte, avremmo ancora una configurazione a semaforo, ma non un mutex.

Entrambi i costrutti vengono utilizzati regolarmente nella programmazione del computer quando si utilizzano più thread per garantire che solo una singola procedura o attività acceda a una determinata risorsa in qualsiasi momento. Alcuni semafori possono essere commutati in modo ultrarapido, come esempio, se utilizzato in software di trading sui mercati finanziari multi-thread, e alcuni possono essere molto più lenti, cambiano stato solo ogni pochi minuti, come quando viene utilizzato su un ponte levatoio automatizzato o su un passaggio a livello.

Ora che abbiamo una migliore comprensione dei semafori, implementiamone uno in bash.

Implementa un semaforo a Bash: È facile sul?

L'implementazione di un semaforo in Bash è così semplice che può essere eseguita anche direttamente dalla riga di comando, O così sembra …

Iniziamo in modo semplice.

BRIDGE=up
if [ "${PONTE}" = "fuori uso" ]; poi eco "Le auto possono passare!"; altro eco "Le navi possono passare!"; fi
BRIDGE=down
if [ "${PONTE}" = "fuori uso" ]; poi eco "Le auto possono passare!"; altro eco "Le navi possono passare!"; essere

Esempio di riga di comando di aggiornamento e output dello stato del bridge

In questo codice, la variabile BRIDGE ha il nostro stato di ponte. Quando lo mettiamo dentro up, Le navi possono passare e quando le mettiamo dentro down, le macchine possono passare. Allo stesso tempo potremmo leggere il valore della nostra variabile in qualsiasi momento per vedere se il ponte è veramente alto o basso.. La condivisione / Comune, in questa circostanza, è il nostro ponte.

ciò nonostante, questo esempio è a thread singolo e, è per questo, non troviamo mai comeaiuta i sindacati situazione. Un altro modo di pensare a questo è che la nostra variabile non può mai essere up e down esattamente nello stesso momento in cui il codice viene eseguito in sequenza, in altre parole, Passo dopo passo.

Un'altra cosa a cui prestare attenzione è che non controlliamo realmente l'accesso a un'altra variabile (come farebbe regolarmente un semaforo), quindi il nostro BRIDGE variabile non è veramente una vera variabile semaforo, anche quando si avvicina.

In sintesi, non appena introduciamo vari thread che possono influenzare il BRIDGE variabile ci imbattiamo in problemi. Come esempio, cosa succede se, subito dopo BRIDGE=up comando, un altro thread problemi BRIDGE=down che in seguito risulterebbe nel messaggio Cars may pass! Uscita, anche se il primo thread si aspetterebbe che il bridge sia up, e in effetti il ​​ponte è ancora in movimento. Pericoloso!

Puoi vedere come le cose possono rapidamente diventare torbide e confuse, per non parlare del complesso, quando si lavora con abbastanza thread.

La situazione in cui più thread tentano di aggiornare la stessa variabile contemporaneamente o almeno abbastanza vicino in tempo da consentire a un altro thread di rovinare la situazione (che nel caso dei ponti levatoi può richiedere molto tempo) è chiamato condizione di gara: due thread in competizione per aggiornare o segnalare qualche variabile o stato, con il risultato che uno o più thread possono andare abbastanza male.

Possiamo migliorare molto questo codice facendo scorrere il codice nelle procedure e utilizzando una vera variabile semaforo che limiterà l'accesso al nostro BRIDGE variabile a seconda della situazione.

Creazione di un semaforo bash

Implementa un multithreading completo, rendilo sicuro (una definizione informatica per descrivere il software che è thread-safe o sviluppato in modo tale che i thread non possano influenzarsi negativamente o erroneamente a vicenda quando non dovrebbero) Non è un compito facile. Anche un programma ben scritto che utilizza i semafori non è garantito per essere assolutamente thread-safe..

Più discussioni ci sono, e maggiore è la frequenza e la complessità delle interazioni tra i fili, le condizioni di gara sono più probabili.

Per il nostro piccolo esempio, vedremo la definizione di un semaforo Bash quando uno degli operatori del ponte levatoio abbassa la maniglia del ponte, indicando che vuoi abbassare il ponte. I lettori accaniti potrebbero aver notato il riferimento a operatoreS invece di operatore: ora ci sono diversi operatori che possono abbassare il ponte. In altre parole, ci sono più thread o attività in esecuzione contemporaneamente.

#!/bin/bash

BRIDGE_SEMAPHORE=0

lower_bridge(){  # Un operatore ha abbassato una delle maniglie di manovra del ponte (come un nuovo stato).
  # Supponiamo che sia stato precedentemente concordato tra gli operatori che non appena uno degli operatori 
  # sposta un handle di operazione bridge che il loro comando deve essere eseguito, o prima o poi
  # quindi, we commence a loop which will wait for the bridge to become available for movement
  while true; do
    if [ "${PONTE_SEMAPHORE}" -Eq 1 ]; then
      echo "Semaforo del ponte bloccato, spostamento del ponte o altro problema. In attesa 2 minuti prima di ricontrollare."
      dormire 120
      Continua  # Continue loop
    elif [ "${PONTE_SEMAPHORE}" -Eq 0 ]; then   
      echo "Comando del ponte inferiore accettato, blocco del semaforo e abbassamento del ponte."
      BRIDGE_SEMAPHORE=1
      execute_lower_bridge
      wait_for_bridge_to_come_down
      BRIDGE='down'
      echo "Ponte abbassato, almeno assicurando 5 passano i minuti prima del successivo movimento consentito del ponte."
      dormire 300
      eco "5 Passarono i minuti, sblocco del semaforo (rilascio del controllo del ponte)"
      BRIDGE_SEMAPHORE=0
      break  # Exit loop
    fi
  done
}

Un'implementazione del semaforo in Bash

Qui abbiamo un lower_bridge funzione che farà un certo numero di cose. Primo, supponiamo che un altro operatore abbia recentemente spostato il ponte all'ultimo minuto. Come tale, c'è un altro thread che esegue il codice in una funzione simile a questa chiamata raise_bridge.

In realtà, quella funzione ha finito di alzare il ponte, ma ha istituito un'attesa obbligatoria di 5 minuti che tutti gli operatori hanno concordato in anticipo e che sono stati codificati nel codice sorgente: impedisci al ponte di salire / scendi tutto il tempo. Allo stesso tempo puoi vedere questa attesa obbligatoria di 5 minuti implementati in questa funzione come sleep 300.

Quindi, quando quello raise_bridge la funzione è attiva, avrà impostato la variabile del semaforo BRIDGE_SEMAPHORE un 1, nello stesso modo in cui lo facciamo nel codice qui (subito dopo echo "Lower bridge command accepted, locking semaphore and lowering bridge" comando), e – attraverso il primo if controllo condizionale in questo codice: il ciclo infinito presente in questa funzione continuerà (rif continue Nel codice) fare il ciclo, con pause di 2 minuti, Come la BRIDGE_SEMAPHORE la variabile è 1.

Non appena quello raise_bridge Lo spettacolo finisce di sollevare il ponte e termina la sua sospensione di cinque minuti, stabilirà il BRIDGE_SEMAPHORE un 0, permettendo al nostro lower_bridge la funzione co avvia l'esecuzione delle funzioni execute_lower_bridge e successivi wait_for_bridge_to_come_down allo stesso tempo che abbiamo bloccato di nuovo il nostro semaforo per 1 per evitare che altre funzioni prendano il sopravvento sul ponte.

ciò nonostante, ci sono carenze in questo codice e possibili race condition che possono avere conseguenze di vasta portata per gli operatori di ponti. Riesci a vedere qualcuno??

il "Lower bridge command accepted, locking semaphore and lowering bridge" non thread-safe.

Se un altro thread, come esempio raise_bridge è in esecuzione contemporaneamente e sta tentando di accedere a BRIDGE_SEMAPHORE variabile, potrebbe essere (quando BRIDGE_SEMAPHORE=0 ed entrambi i thread in esecuzione raggiungono i rispettivi echoÈ esattamente nello stesso momento che gli operatori del ponte vedono “Il comando del ponticello inferiore è accettato, Il comando del ponticello inferiore è accettato” e “Il comando del ponticello inferiore è accettato, Il comando del ponticello inferiore è accettato”. Direttamente uno dopo l'altro sullo schermo! Paura, no?

Ancora più spaventoso è il fatto che entrambi i thread possano continuare BRIDGE_SEMAPHORE=1, Ed entrambi i thread possono continuare a essere eseguiti! (Non c'è niente che impedisca loro di farlo) Il motivo è che non c'è ancora molta protezione per tali scenari. Anche se questo codice implementa un semaforo, non è assolutamente thread-safe. Come è stato detto, la codifica multi-thread è complessa e richiede molta esperienza.

Anche se il tempo necessario in questa circostanza è minimo (1-2 le righe di codice richiedono solo pochi millisecondi per essere eseguite), e dato il numero probabilmente basso di operatori di ponti, la possibilità che ciò accada è molto piccola. ciò nonostante, il fatto che sia praticabile è ciò che lo rende pericoloso. Creare codice thread-safe in Bash non è un compito facile.

Questo potrebbe essere ulteriormente migliorato, come esempio, introducendo un pre-blocco e / o introducendo qualche tipo di ritardo con una nuova verifica successiva (anche se questo richiederà probabilmente una variabile aggiuntiva) o facendo un regolare ricontrollo prima dell'effettiva esecuzione del ponte. , eccetera. Un'altra alternativa è creare una coda prioritaria o una variabile contatore che controlli quanti thread hanno bloccato il controllo del bridge, eccetera.

Un altro approccio comunemente usato, come esempio, durante l'esecuzione di più script bash che potrebbero interagire, è da usare mkdir oh flock come operazioni di blocco della base. Ci sono diversi esempi di come implementarli disponibili online, come esempio, Quali comandi Unix possono essere usati come semaforo / blocco?.

Fine

In questo articolo, diamo un'occhiata a cos'è un semaforo. Allo stesso tempo abbiamo toccato brevemente il tema di un mutex. In sintesi, analizziamo l'implementazione di un semaforo in Bash usando l'esempio pratico di diversi operatori di bridge che gestiscono un bridge mobile / ponte levatoio. Allo stesso tempo, esploriamo quanto sia complessa l'implementazione di una soluzione affidabile basata sui semafori.

Se ti è piaciuto leggere questo articolo, dai un'occhiata ai nostri reclami, errori e crash: qual è la differenza? Articolo.

Iscriviti alla nostra Newsletter

Non ti invieremo posta SPAM. Lo odiamo quanto te.