Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
La funzione compare_and_swap
La funzione compare_and_swap è un tipo di istruzione che va a scambiare dei parametri che vengono passati all'istruzione. Vengono passati tre parametri, ed il valore del primo parametro viene scambiato con il valore del terzo parametro se il valore del primo parametro è uguale a quello del secondo parametro.
Anche qui se la funzione restituisce falso allora si entra in sezione critica, se restituisce vero significa che c'è un altro processo in sezione critica e quindi il processo che vuole entrare in sezione critica non entra.
Quando la compare_and_swap restituisce falso allora significa che non c'è nessuno in sezione critica, mentre se restituisce vero allora c'è un processo in sezione critica e bisogna attendere all'interno del ciclo.
Dopo che termina la sezione critica dobbiamo sbloccare lock.
Questo codice soddisfa il requisito di mutua esclusione, ma non gli altri due requisiti di progresso e attesa limitata, in quanto, è...
possibile che un processo che entri in sezione critica possa uscire e rientrare subito dopo, perché per esempio ha due sezioni critiche molto vicine tra loro. A causa di questo possibile evento è possibile che i processi che devono entrare in sezione critica debbano attendere parecchio. Esistono delle tecniche e chiamate di sistema per condividere risorse tra processi. Tuttavia servono dei meccanismi di più alto livello al fine di dare gli strumenti adeguati ai programmatori a livello utente. Abbiamo la seguente tecnica di mutua esclusione con attesa limitata con compare_and_swap(): Un thread indica la sua intenzione di accedere alla sezione critica impostando waiting[i] su true. Viene inizializzata una variabile key a 1. Il thread entra in un ciclo while che continua fintanto che è in attesa (waiting[i]) e key è uguale a 1. All'interno di questo ciclo, viene utilizzata la funzione compare_and_swap per acquisire la lock in modo atomare. Una volta acquisita la lock,Il thread imposta waiting[i] su false e accede alla sezione critica. Dopo la sezione critica, il thread cerca di individuare il prossimo thread (indicato da j) che desidera accedere alla sezione critica. Se nessun altro thread è in attesa, rilascia la lock; altrimenti, rilascia il diritto di accesso al prossimo thread. Il ciclo continua in modo infinito, garantendo che i thread possano accedere alla sezione critica in modo ordinato e sicuro.
Variabili atomiche
Le variabili atomiche sono introdotte in quanto le istruzioni atomiche potrebbero risultare abbastanza complesse da usare per il programmatore. Alle variabili atomiche vengono associate delle operazioni atomiche. Un esempio di variabile atomica è il lock mutex; questa variabile deve essere bloccata da un processo per permettergli di entrare in sezione critica. Si capisce dunque che il lock mutex serve per mutua esclusione; quindi, permette di mandare in ingresso in sezione critica un processo per volta. A questo punto tutte le
tecniche che abbiamo visto presentano vari problemi, e tutte tra queste presentano un problema che non ancora è stato menzionato: l'attesa attiva. Con tutte le implementazioni che abbiamo visto, abbiamo notato come un processo che non entra in sezione critica rimane bloccato all'interno del ciclo ad eseguire sempre iterazioni di un ciclo che ovviamente non ha nessuna finalità per l'utente (ricordiamo che la CPU deve impiegare le sue risorse per l'utente e non per la macchina stessa). Per questo motivo vengono introdotti i semafori, che si comportano in modo simile ad un lock mutex ma in realtà ci permettono di gestire molti casi di sincronizzazione. Approfondimento: L'attesa attiva è una tecnica utilizzata nei sistemi operativi e nella programmazione concorrente per gestire la sincronizzazione tra processi o thread. Invece di bloccare attivamente un processo o un thread in attesa di una certa condizione, l'attesa attiva implica cheIl processo o il thread continui a eseguire istruzioni in modo ripetuto fino a quando la condizione desiderata è soddisfatta. L'attesa attiva può essere utile in determinate situazioni, ma può anche comportare un utilizzo elevato della CPU, poiché il thread che aspetta attivamente continua a eseguire istruzioni invece di "dormire" in modo passivo. Va utilizzata con cautela e in modo ponderato, tenendo conto degli effetti sulla prestazione complessiva del sistema. In alcune situazioni, possono essere preferibili approcci di attesa passiva, in cui un thread viene sospeso e risvegliato solo quando la condizione di attesa è soddisfatta, per ridurre il consumo di risorse della CPU.
Semafori
I semafori sono uno strumento che ci permettono di andare a gestire vari problemi di sincronizzazione. Il semaforo è un tipo di variabile atomica a cui è possibile accedere, dopo l'inizializzazione, solo tramite operazioni atomiche che sono:
di accedere alla sezione critica), mentre l'operazione di signal incrementa il valore del semaforo, restituendo una risorsa (o permettendo ad un altro processo di accedere alla sezione critica). Ecco un esempio di utilizzo dei semafori: ```htmlwait() e signal(). Abbiamo due tipi di semafori:
- Semaforo contatore: Generalmente vengono utilizzati per andare a gestire le risorse disponibili, difatti sono inizializzati da un valore N che indica il numero di risorse disponibili per i processi.
- Semaforo binario: Questo serve per gestire la mutua esclusione come fosse unlock mutex, quindi, permette di accedere ad un processo alla volta all'interno della propria sezione critica e generalmente viene inizializzato ad 1.
I semafori vedremo che sono più semplici da usare per i programmatori, ed inoltre non comportano il problema dell'attesa attiva, in quanto i processi verranno direttamente mandati in attesa.
Ricapitoliamo:
- Semaforo S - variabile interna
- Inizializzazione
- Operazioni atomiche
- Due operazioni di modifica del semaforo S: wait() e signal()
Notiamo come l'operazione di wait decrementa il valore del semaforo; quindi, preleva una risorsa (o permette ad un processo di accedere alla sezione critica), mentre l'operazione di signal incrementa il valore del semaforo, restituendo una risorsa (o permettendo ad un altro processo di accedere alla sezione critica).
Ecco un esempio di utilizzo dei semafori:
```dientrare nella sua sezione critica), mentre al contrario l'operazione di signal incrementa il valore del semaforo; quindi, restituisceuna risorsa (o notifica l'uscita di un processo dalla sua sezione critica).Tutte le modifiche ai valori dei semafori sono operazioni atomiche e dunque non interrompibili tramite prelazioni, e il valore deisemafori può essere cambiato da un processo alla volta.L'implementazione che abbiamo appena visto in realtà presenta il problema dell'attesa attiva, ma è possibile fareun'implementazione che elimina il problema, andando ad associare ad ogni semaforo una coda d'attesa per i processi.Possiamo allora definire il semaforo non più come un valore intero, ma come una struttura composta da un valore intero e da unpuntatore che punta alla testa della lista dei processi che ha in attesa.Sono possibili oltre la wait e la signal anche altre due operazioni atomiche:
- Block(), inserisce il
processo nella coda d'attesa del semaforo.
- Wake up(), preleva un processo dalla coda d'attesa e lo pone nella coda dei processi in stato di pronto.
A questo punto possiamo vedere come cambiano le implementazioni delle operazioni di wait e signal:
I problemi dei semafori sono generalmente dovuti ad errori del codice, ad esempio, se dovessimo omettere una signal, ci sarebbe un processo che rimarrebbe per sempre bloccato nella coda di attesa del semaforo (starvation).
Oppure quello che si può presentare è un deadlock, ovvero due o più processi attendono indefinitivamente un evento che può essere causato solo da un processo in attesa.
Per tale motivo una regola base per la sincronizzazione della sezione critica è che, quando un processo deve accedere in sezione critica, basta che prima di entrare utilizza una wait e prima di uscire utilizza una signal.
Per la sincronizzazione delle risorse non si può dire a priori perché dipende
dalle politiche di accesso e altro. Problemi di sincronizzazione A questo punto dopo aver parlato dei semafori possiamo vedere quali problemi di sincronizzazione permettono di risolvere i semafori grazie al loro utilizzo. Cominciamo a vedere questi classici problemi di sincronizzazione:- Problema del produttore/consumatore con buffer limitato:
- Un buffer con N elementi;
- Un semaforo
mutex inizializzato ad 1;
- Un semaforo full inizializzato a 0;
- Un semaforo empty inizializzato ad N.
Il semaforo binario mutex serve per la gestione dell'accesso alla sezione critica, mentre i semafori full ed empty servono per tenere conto rispettivamente degli elementi pieni e vuoti all'interno del buffer.
Il semaforo full è inizializzato a 0 ed indica quindi che non ci sono elementi all'interno del buffer, mentre il semaforo empty è inizializzato ad N ed indica che ci sono N elementi liberi nel buffer (dove N è la dimensione del buffer).
Vediamo i processi: In questo processo viene inizialmente prodotto un elemento, dopo di che fa il controllo sul buffer.
Se il buffer è vuoto la wait decrementa ad un numero che è maggiore o al più uguale a 0 se il buffer contiene un solo elemento, mentre se non ci sono elementi disponibili blocca il processo produttore (numero negativo).
Quando si deve inserire un elemento nel buffer
scrittura). Per risolvere il problema dei lettori/scrittori possiamo utilizzare i seguenti tag HTML: - Utilizziamo il tag `` per creare un paragrafo che descrive il problema dei lettori/scrittori: ```html
Nel problema dei lettori e scrittori abbiamo due processi (o thread) che agiscono su una risorsa condivisa, e questi due processi possono eseguire due operazioni atomiche, che sono rispettivamente lettura e scrittura (i lettori possono fare l'operazione di lettura, gli scrittori possono fare operazione di lettura e scrittura).
``` - Utilizziamo il tag `- ` per creare una lista puntata per descrivere le operazioni dei processi:
```html
- I lettori possono fare l'operazione di lettura
- Gli scrittori possono fare operazione di lettura e scrittura
- I lettori possono fare l'operazione di lettura
- Gli scrittori possono fare operazione di lettura e scrittura
` per creare un paragrafo che descrive la soluzione al problema dei lettori/scrittori: ```html
Per risolvere il problema dei lettori/scrittori possiamo utilizzare i seguenti tag HTML:
``` - Utilizziamo il tag `` per evidenziare il codice HTML:
```html
<p>, <ul>, <li>
```
- Utilizziamo il tag `` per creare un paragrafo finale:
```html
Questi sono solo alcuni esempi di come utilizzare i tag HTML per formattare il testo. È possibile utilizzare molti altri tag per creare una formattazione più complessa e personalizzata.
```
Il testo formattato con i tag HTML sarà quindi:
```html
si entra in sezione critica tramite il semaforo binario mutex (garantisce la mutua esclusione). Infine, si fa una signal di full, dove il valore di full viene incrementato (tiene conto degli elementi presenti nel buffer).
Il processo consumatore, in primo luogo, vede se sono presenti elementi all'interno del buffer, se non ci sono elementi la variabile full assume un valore negativo e wait blocca il processo consumatore.
Per l'accesso in memoria si entra in sezione critica. Infine, facciamo una signal sulla variabile empty per incrementare empty in quanto avendo tolto un elemento dal buffer i posti vuoti sono aumentati.
Nel problema dei lettori e scrittori abbiamo due processi (o thread) che agiscono su una risorsa condivisa, e questi due processi possono eseguire due operazioni atomiche, che sono rispettivamente lettura e scrittura (i lettori possono fare l'operazione di lettura, gli scrittori possono fare operazione di lettura e scrittura).
Per risolvere il problema dei lettori/scrittori possiamo utilizzare i seguenti tag HTML:
<p>, <ul>, <li>
Questi sono solo alcuni esempi di come utilizzare i tag HTML per formattare il testo. È possibile utilizzare molti altri tag per creare una formattazione più complessa e personalizzata.
```scrittura). Si deve permettere ai lettori di legge