1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: –...

36
1.6.1 1.6: Processi Concorrenti Programmi concorrenti Chiamate di sistema Unix per il controllo di processi Creazione, terminazione di processi Sincronizzazione sulla terminazione, segnalazione di eventi Comunicazione Esempi di codice Sincronizzazione tra processi Mutua esclusione Produttore-consumatore Primitive di sincronizzazione; semafori Implementazione Esempi di codice

Transcript of 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: –...

Page 1: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.1

1.6: Processi Concorrenti● Programmi concorrenti

● Chiamate di sistema Unix per il controllo di processi

– Creazione, terminazione di processi

– Sincronizzazione sulla terminazione, segnalazione di eventi

– Comunicazione

– Esempi di codice

● Sincronizzazione tra processi

– Mutua esclusione

– Produttore­consumatore

● Primitive di sincronizzazione; semafori

– Implementazione

– Esempi di codice

● Stallo e blocco indefinito 

Page 2: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.2

Programmi Concorrenti● Programmi:

– Sequenziali ­  singolo processo

– Concorrenti ­  più processi (o thread) che competono tra loro per risorse di sistema, si sincronizzano, comunicano tra loro.

● Più versatili ed efficienti● Applicazioni più complesse e sofisticate

● Requisiti: normale linguaggio sequenziale + primitive (chiamate di sistema o API) per

– creazione/terminazione processi

– sincronizzazione/comunicazione/segnalazione tra processi

Page 3: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.3

Programmi Concorrenti

P0

(sh) 

P0

(sh) 

P1

(ls) 

P0 

P0 

P1  P2  P3  P4 

Sincronizzazione sulla terminazione

Comunicazione dati

● Esempi:

Page 4: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.4

Chiamate di Sistema Unix● pid = fork(); creazione

– crea nuovo processo (child ­  figlio), duplicando quello originale (parent ­  padre)– ritorna

● al figlio: 0● al padre: il PID del figlio appena creato● in caso di errore, ­1 (al padre)

– il processo figlio eredita tutto l'ambiente del padre, compresi i canali aperti (tutto tranne il PID)

– il figlio si trova come se avesse eseguito tutto il programma fino al fork, come il padre; continua ad eseguire (indipendentemente)

– N.B. ­ il figlio riceve una copia dell'ambiente del padre (programma, dati etc.); non c'è condivisione di dati tra padre e figlio.

– es.:pid = fork();if(pid diverso da 0){ /* padre */

if(pid uguale a ­1) { errore(); exit(1); }...

} else {/* figlio */

...}

● Linux supporta i kernel thread con la chiamata clone(2)

Page 5: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.5

Chiamate di Sistema Unix (Cont.)● exit(code); terminazione

– termina il processo– flush dell'I/O pending (stdout etc.), chiude tutti i file, esce– trasmette code al Kernel (codice d'uscita ­  exit code) ­  convenzione: code = 0 ⇒ 

OK ; code ≠ 0 ⇒ errore (vari codici possibili)– non ritorna mai

● pid = wait(status_address); sincronizzazione su terminazione– aspetta finchè uno dei processi figli termina (sincronizzazione)– ritorna il PID del primo figlio che ha terminato– se status_address non è nullo, ritorna anche, nell'intero puntato, lo stato d'uscita 

del figlio (exit code e altro).● execl(path, name, arg1, ...);

– sostituisce, nel processo corrente, un nuovo programma all'attuale, e lo esegue– path è il percorso del file eseguibile, name è il nome del programma, arg1 è il 

primo argomento, etc.– il processo rimane lo stesso, quindi tutto l'ambiente è inalterato– "viaggio senza ritorno": se c'è un valore di ritorno, è ­1 (impossibile eseguire il 

nuovo programma)– esistono altre varianti (tutte API che conducono a un'unica chiamata di sistema, 

execve(2) ).– N.B. ­ combinazione di fork() + execl() : due processi che eseguono due 

programmi diversi.

Page 6: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.6

Esempio di Codice  ­  1● Combinazione tipica delle chiamate viste (pseudo­codice):

pid = fork();if(pid diverso da 0){

/* processo padre */if(pid uguale a ­1)

error_exit("cannot fork");...wait(status_address);excode = (estrai codice da status);exit(excode); /* esce col codice del figlio */

}else{

/* processo figlio *//* eventuale ridirezione dell'I/O */execl("/usr/local/bin/program", "program", "­x", "file", NULL);error_exit("cannot exec program");

}

Page 7: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.7

Chiamate di Sistema Unix (Cont.)● kill(pid, sig); segnalazione

– invia il segnale sig al processo con PID pari a pid (segnalazione: comunicazione di evento asincrono)

– processo ricevente: stesso UID del processo mittente (salvo se mittente è root)

– segnale: provoca interrupt software nel processo ricevente

– azione di default all'interrupt:    a) exit    b) core­dump + exit [*]● SIGINT (2)   ­ intr char. da terminale● SIGQUIT (3) ­ quit char. da terminale [*]● SIGKILL (9) ­ terminazione forzata (non intercettabile)● SIGTERM (15) ­ terminazione software (default per kill(1))

– N.B. ­  la chiamata di sistema signal(sig, func) permette di predisporre un'azione specifica (la chiamata alla funzione func) al ricevimento del segnale sig.

Page 8: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.8

Esempio di Codice  ­  2prog() {

pid = fork();

if(pid non uguale a 0) { /* processo padre */

print("PID processo figlio = ", pid);

print("attesa 10 secondi");

sleep(10);   /* attendi 10 sec. senza fare nulla */

print("terminazione del processo figlio");

kill(pid, SIGTERM); /* segnale di terminazione al processo "pid" */

exit(0);

} else { /* processo figlio */

print("processo figlio inizia");

pid = getpid();  /* ottengo il mio proprio identificativo */

loop

{

print("processo figlio", pid, "lavora");

sleep(1);

}

}

}

Page 9: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.9

Come lavora la shell di Unixshell() {

loop {

write(1, "$ ", 2); /* prompt ­  2 caratteri allo stdout */

read(0, line, SIZE); /* leggi riga da stdin */

parse(line); /* separa i token; applica i vari operatori ($, *, ?, [ ], etc.) */

pid = fork();

if(pid non uguale a 0) {  /* processo padre: la shell originaria */

if( line  non termina con '&' ) {

wait(status_address);excode = (estrai codice d'uscita da status);/* tieni conto di  excode per istruzioni di controllo della shell: if, while, etc. */

}

} else {  /* processo figlio */

redirect(); /* applica ridirezioni di stdin, stdout etc. (operatori <, >, >> etc.) */

execl(file, program, args); /* esegui il comando nel processo figlio */

}

}

}

Page 10: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.10

Chiamate di Sistema Unix (Cont.)● pipe(fdc); comunicazione/sincronizzazione

– è il più semplice (e più limitato) meccanismo di comunicazione dati tra processi

– stabilisce un canale di comunicazione tra processi, che può essere condiviso con i processi figli (che ereditano i canali aperti)

– il canale è unidirezionale– fdc[0] serve per leggere dal pipe, fdc[1] per scrivere– il pipe è implementato come un canale di trasmissione di tipo FIFO, di 

dimensione finita (di solito 4 Kbyte)– sincronizzazione automatica di processi scrittori e lettori, che vanno a 

dormire in caso di pipe pieno/vuoto– EOF (0 byte letti) ritornato dalla read() al lettore se il pipe è chiuso in scrittura– N.B. ­  questa descrizione è incompleta per un uso reale.

fdc[1]   

fdc[0]   

fdc[1]   

fdc[0]   

Padre Padre Figlio

fdc[1]   

fdc[0]   

Page 11: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.11

Esempio di Codice  ­  3

Padre(produttore)

Figlio(consum.)

sel file pattern file  grep pattern  

stdout  fdc[1]  fdc[0] 

Page 12: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.12

Esempio di Codice  ­  3 (Cont.)sel(file, pattern) {

pipe(fdc);

pid = fork();

if(pid diverso da 0)

{          /* padre ­  produttore */

ifd = open(file, read­only);

loop(nread = read(ifd, buffer, 1024); finchè nread diverso da 0)

write(fdc[1], buffer, nread);

close(fdc[1]); /* così il figlio troverà EOF dopo lo svuotamento della pipe */

close(ifd);

wait(status_address); /* sincronizzati sulla terminazione di grep */

code = (estrai exit code da status);

exit(code);

} else { /* figlio ­  consumatore */

redirect(fdc[0] sul f.d. 0, cioè lo stdin);

execl("/usr/bin/grep", "grep", pattern);

}

}

Page 13: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.13

Sincronizzazione tra Processi● Due o più processi interagiscono tra loro quando si contendono l'uso di 

risorse, permanenti o consumabili.

● Due paradigmi di base:

– Mutua esclusione

– Produttore­consumatore

● Tutti i casi sono riconducibili a produzione e consumo di risorse consumabili.

– Vincolo di sincronizzazione tra P1 e P2 :  "P1  è il produttore di una risorsa consumabile R e P2 è il consumatore di R".

● R è una risorsa software creata ad hoc dal Kernel.

Page 14: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.14

Mutua Esclusione● Paradigma della mutua esclusione:

– risorsa permanente R contesa da più processi P1 ... Pn

– garantire che 

● Ad ogni istante vi sia al max. un processo Pi che occupi R (Mutua Esclusione)

● Ogni processo richiedente ottenga l'uso di R entro un intervallo limitato di tempo (Attesa Limitata)

● Generalizzazione: esecuzione di una parte di codice che deve gestire dati in modo esclusivo  problema della sezione critica.

– L'accesso concorrente a dati condivisi può provocare un'inconsistenza dei dati stessi, se non si adotta una disciplina di accesso.

● Caso tipico: quando si vuole rendere seriale una risorsa permanente del sistema (area RAM, disk controller, etc.)

Page 15: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.15

Problema della Sezione Critica● N processi che si contendono l'uso di dati condivisi

● Ogni processo ha una parte di codice, chiamata sezione critica, nella quale accede ai dati condivisi.

● Problema della sezione critica – garantire che quando un processo Pi sta eseguendo codice nella propria sezione critica, nessun altro processo possa fare altrettanto → trovare un protocollo adeguato.

● Struttura del generico processo Pi

for(;;){    entry section          critical section    exit section           non­critical section}

Page 16: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.16

Produttore­Consumatore● Paradigma produttore­consumatore:

– Risorse consumabili (dati) R prodotte in loop da uno o più Pp e consumate da uno o più Pc

– Dati normalmente trasmessi tramite un buffer di dimensione finita (canale di comunicazione a capacità limitata)  problema del buffer limitato (bounded­buffer problem); talvolta il canale è concettualmente a capacità illimitata.

● Il processo consumatore va comunque a dormire nel caso di risorsa non disponibile (canale vuoto)

● Il processo produttore va a dormire, solo nel caso di capacità limitata, se il canale è pieno.

Page 17: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.17

Produttore­Consumatore (Cont.)● Esempio tipico: due processi comunicano attraverso un buffer comune, 

di dimensione limitata● Dati condivisi:

#define N ...;typedef ... item;item buffer[N];int in = 0, out = 0,counter = 0;   /* in: next free slot in buffer ­                             out: first full slot */

Page 18: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.18

Produttore­Consumatore (Cont.)● Processo produttore:

item nextp;for(;;){

…produce an item in nextp;…while ( counter == N )    /* (while buffer full) */        no­op;buffer [in] =nextp;in = ++in % N;++counter;

}

Page 19: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.19

Produttore­Consumatore (Cont.)● Processo consumatore:

item nextc;for(;;){

while ( counter == 0 ) /* (while buffer empty) */        no­op;nextc = buffer [out];out = ++out % N;­­counter;…consume the item in nextc; …

}● Problema: a causa dell'esecuzione concorrente, le istruzioni:

++counter ­­counter

devono essere istruzioni atomiche, cioè eseguite atomicamente (mutua esclusione), per evitare race conditions (altrimenti il risultato è indefinito).

• N.B. ­ race = corsa

Page 20: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.20

Produttore­Consumatore (Cont.)– Varianti:

●  Un produttore, un consumatore– es. 1: pipe (canale a capacità limitata)– es. 2: processo che legge caratteri da tastiera (canale 

concettualmente a capacità illimitata).● Più produttori, un consumatore

– es.: spooler di stampa● Più produttori, più consumatori

– es.: programma concorrente con più processi che eseguono in multiprogrammazione la stessa funzione.

● Il server HTTP Apache è normalmente configurato in modo da preallocare da 4 a 10 processi server in parallelo, per essere pronto a picchi di carico.

Page 21: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.21

Hardware di Sincronizzazione● L'hardware può aiutare a risolvere il problema dell'atomicità.● Istruzione “test+modify” di una variabile, in modo atomico:

boolean TestAndSet(boolean target){

boolean result = target;target = TRUE;return result;

}● Dati condivisi:

boolean lock = FALSE;● Processo Pi

for(;;){

while (TestAndSet(lock))no­op;

critical sectionlock = FALSE;non­critical section

}● L'implementazione hardware di instruzioni atomiche del tipo visto non è 

semplice.

Page 22: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.22

Semaforo● I problemi visti diventano più complessi quando si estendono a un 

numero arbitrario di processi concorrenti

● Occorre disporre di uno strumento di sincronizzazione di uso generale.

● Mutua esclusione di risorsa permanente R: può ottenersi associando risorse consumabili XR ; ogni processo

– acquisisce una XR prima di utilizzare R

– rilascia XR dopo aver utilizzato R.

● XR: tipo di risorsa fittizia generata dal Kernel, solo per regolare il traffico di processi:

– affluiscono verso la risorsa permanente R (acquisizione di XR)

– se ne distaccano (rilascio di XR)

     chiamato, con evidente analogia, semaforo. E' questo lo strumento generale ricercato.

Page 23: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.23

Primitive di Sincronizzazione● lock() / unlock(): primitive di implementazione di semafori (la risorsa 

consumabile). Risolvono qualunque problema di mutua esclusione.

● Pseudo­codice:

lock(X) {

loop(X non è disponibile)

aspetta;consuma X;

}

unlock(X) {

produci X; /* sblocca eventuale processo in attesa     nella chiamata a lock() */

}

● N.B. ­  lock() e unlock() sono chiamate spesso, nella letteratura tecnica, wait() e signal(), o anche P() e V().

Page 24: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.24

Primitive di Sincronizzazione (Cont.)● send() / receive(): primitive di implementazione di comunicazione 

sincronizzata di messaggi (la risorsa consumabile). Risolvono problemi di tipo produttore­consumatore.

● Pseudo­codice (caso di canale a capacità limitata):

send(m,dest) {

loop(canale pieno)

aspetta;inserisci <m,dest> nel canale; /* sblocca eventuale processo in attesa 

    nella chiamata a receive() */

}

receive(m,mitt) {

loop(canale vuoto)

aspetta;estrai <m,mitt> dal canale; /* sblocca eventuale processo in attesa 

    nella chiamata a send() */

}

Page 25: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.25

Esempio: Sezione Critica di n Processi● Variabile condivisa:

semaphore mutex = 1; /* mutex: mutual exclusion */● Processo Pi

for(;;)

{

wait(mutex);critical sectionsignal(mutex);non­critical section

}

Page 26: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.26

Implementazione dei Semafori● Nello pseudo­codice visto per lock()/unlock() (ma anche per 

send()/receive() ), l'istruzione "aspetta" può significare in pratica due cose:

– No­op (loop continuo, ovvero busy­waiting; impegno di CPU) ­> spinlock (spin while locked)

– Vai a dormire (il processo passa in stato Waiting; sarà svegliato successivamente; non impegna CPU)    semaforo bloccante

● In generale, molto più adatto ad ambienti multitasking

Page 27: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.27

Implementazione dei Semafori (Cont.)● Un semaforo spinlock è normalmente una variabile intera, a cui si accede 

esclusivamente tramite due operazioni atomiche:– wait (S):   while (S <= 0)

                                              no­op;            ­­S;

– signal (S):     ++S;

• Può assumere due valori (semaforo binario):

– 0    risorsa occupata (semaforo bloccato)

– 1   risorsa diponibile (semaforo libero)

Page 28: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.28

Implementazione dei Semafori (Cont.)Implementazione a livello Kernel di un semaforo bloccante:

● Semaforo definito come un record (struttura di dati):struct semaphore{        int value;        process *proclist; /* puntatore a lista di Process Descriptor */};

● Basato su due semplici operazioni:–  sleep() sospende il processo che la invoca (il processo va a dormire)–  wakeup(P) riprende l'esecuzione di un processo sospeso P (il processo vene 

risvegliato).● N.B. ­ queste due operazioni possono essere implementate facilmente in 

qualunque sistema multitasking; in particolare, Unix dispone di funzioni sleep() e wakeup() nel Kernel (N.B. ­  nulla a che vedere con la funzione sleep(3) ).

Page 29: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.29

Implementazione dei Semafori (Cont.)● Le primitive di semaforo possono ora definirsi così:

wait(S): if (­­S.value < 0)                 {                           add this process to S.proclist;

                        sleep();                 }

signal(S):  if (S.value++ < 0)                 {                           remove a process P from S.proclist;

             wakeup(P);                 }

● Diversamente dall'implementazione vista in precedenza, questo semaforo può assumere valori negativi (semaforo contatore); questi corrispondono al numero di processi in attesa della risorsa.

● N.B. ­ la distinzione tra semaforo binario e semaforo contatore è indipendente da quella tra semaforo spinlock e semaforo bloccante.

Page 30: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.30

Sezione Critica di n Processi: ScenarioSem.value Resource (Free, Busy)1 F

P1: wait() 0P1: go B...P2: wait() ­1P2: sleep()P3: wait() ­2P3: sleep()...P1: signal() ­1 FP1: wakeup(P2)P2: go B…P2: signal() 0 FP2: wakeup(P3)P3: go B...P3: signal() 1 F

Page 31: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.31

Sincronizzazione Generica: Scenario

● Exegui B in Pj solo dopo aver eseguito A in Pi

● Si usa un semaforo flag initializzato a 0● Codice:

Pi Pj

 ... ...

A wait(flag)

signal(flag) B

Page 32: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.32

Implementazione in Unix● Unix System V: IPC (InterProcess Communication) ­  insieme di 

chiamate di sistema per sincronizzazione e comunicazione tra processi

– Comprende: a) code di messaggi  b) memoria condivisa  c) semafori

– Implementato ormai in tutti i tipi di Unix, incluso Linux

● Un esempio di implementazione di lock() / unlock() in Unix a livello utente:

– Sfrutta l'apertura di file, in modo esclusivo

– open(file, O_CREAT+O_EXCL): ha successo solo se il file non esiste (e viene dunque creato ex­novo)

– Un semaforo = un file; file esiste: risorsa occupata; file non esiste: risorsa libera.

Page 33: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.33

Esempio di Codice  ­  4/* name: nome del file, cioè del semaforo */

lock(name) {

loop {

fd = open(name, O_CREAT+O_EXCL);

if(errore: il file esiste già)

sched_yield();else if(altro tipo di errore)

return errore;else

return fd; /* file creato in modo esclusivo = risorsa acquisita;     restituisci descrittore del semaforo */

}

}

● N.B. ­  sched_yield(): chiamata sistema per rilascio volontario della CPU ­   il processo è messo in Ready state, in fondo alla ready queue.

Page 34: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.34

Esempio di Codice  ­  4 (Cont.)/* name: nome del file, cioè del semaforo; fd: descrittore del semaforo */

unlock(name, fd) {

close(fd);

if(errore)

return errore; /* uso errato del semaforo */

else {

unlink(name);  /* file rimosso = risorsa rilasciata; da questo     momento può essere ricreato tramite lock() */

return OK;

}

}

● N.B. ­  l'uso di sched_yield() può provocare spreco di CPU (a forza di context switch), se il semaforo è tenuto bloccato per molto tempo (secondi).

Page 35: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.35

Problemi di sincronizzazione● L'uso delle primitive viste può portare a problemi in caso di errori di 

programmazione (uso improprio delle primitive)

● Stallo (deadlock): due o più processi attendono indefinitamente un evento che può essere causato solo da un altro dei processi in attesa.

– Esempio: processi PA  e  PB ; semafori R1 e R2

● Soluzione: acquisire semafori in un ordine prefissato (e rilasciarli nell'ordine inverso).

● Responsabilità del programmatore, non del S.O.

PA

lock(R1);lock(R2);...unlock(R1);unlock(R2);

PB

lock(R2);lock(R1);...unlock(R2);unlock(R1);

Page 36: 1.6: Processi Concorrenti · 2009. 1. 28. · 1.6.2 Programmi Concorrenti Programmi: – Sequenziali singolo processo – Concorrenti più processi (o thread) che competono tra loro

1.6.36

Problemi di sincronizzazione (Cont.)● Blocco indefinito (starvation): uno o più processi possono non essere 

mai rimossi dalla coda dei processi in attesa di acquisizione di un semaforo, perchè altri processi in attesa hanno sempre maggior priorità.

– Ciò può avvenire ad esempio se la coda è gestita in ordine LIFO (Last­In, First­Out).

– Problema analogo a quello già visto nel caso dello scheduling di CPU con priorità