Ottenere la mutua esclusione

Ci sono diversi modi per realizzare la mutua esclusione:

  • Disabilitare gli interrupt: non è una soluzione ottima, infatti non risolve il problema della mutua esclusione multi core, inoltre dare all’utente la possibilità di disabilitare gli interrupt può portare da abusi o bug da parte del programmatore
  • Variabili di lock: funzionano bene solo se gestite a livello kernel, un programmatore potrebbe sbagliare
  • Alternanza stretta: spiegazione Un altro algoritmo utilizzato è quello di Peterson

Riusciamo a gestire N processi, abbiamo una variabile turn che prende il valore del processo scelto per entrare nella sezione critica

Istruzioni TSL e XCHG

DANGER

Mi sono distratto :)

Sleep e wakeup

è importante per un processo avere la possibilità di bloccarsi per risolvere il problema dell’inversione di priorità e questo lo fa usando le primitive:

  • sleep: sospende l’esecuzione del chiamante, questo potrà tornare ad avere la CPU solo quando sarà risvegliato. Manderà il processo nello stato di “blocked”
  • wakeup: sveglia il processo, facendolo tornare nello stato di “ready”

Consideriamo il problema del produttore-consumatore (ha commentato dicendo che si trova spesso al laboratorio) in pratica abbiamo:

  • produttore
  • consumatore
  • buffer limitato Il produttore inserisce continuamente dati nel buffer, il consumatore consuma continuamente dati dal buffer, è importante ricordare che il consumatore non può consumare ciò che non esiste, di seguito la prima soluzione a questo problema:
# Definizione del Produttore (inserisce un item in loop)
function producer()
    while (true) do
        item = produce_item()
        if (count == N) sleep()
        insert_item(item)
        count = count + 1
        if (count == 1)
            wakeup(consumer)

# Definizione del Consumatore (consuma un item in loop)
function consumer()
    while (true) do
        if (count == 0) sleep()
        item = remove_item()
        count = count - 1
        if (count == N - 1)
            wakeup(producer)
        consume_item(item)

Questa soluzione ci porta lo stesso al deadlock, quindi nasce la soluzione dove viene implementato l’utilizzo di un flag di attesa chiamato wakeup

Semafori

Generalizzando il concetto di sleep e wakeup otteniamo un semaforo, un semaforo è composto da:

  • una variabile S: che non può mai diventare negativa
  • funzioni wait e signal: queste due operazioni incrementano/decrementano la variabile S. Se si prova ad effettuare il wait sul semaforo a 0 l’operazione diventa bloccante Per il corretto funzionamento, bisogna fare in modo che down e up siano atomiche, per evitare problemi con race condition sul semaforo.

Esistono diversi tipi di semaforo:

  • Numerico: assume da 1-N, che si presa ai problemi di conteggio delle risorse, bloccando il thread quando questo esaurisce la risorsa
  • Mutex: é un semaforo cui assume valori da , usato come flag per garantire la mutua esclusione Di seguito la soluzione del problema produttore-consumatore