78
Introduzione ai Sistemi Operativi DI ANNA MARIA ROSATI ANGELO ROSIELLO Tratto dal corso di Laboratorio Sofware del Prof. Fornaciari © Copyright 2005 Angelo Rosiello & Anna Maria Rosati Sono riservati tutti i diritti di traduzione in lingue straniere, di copia o riproduzione e di adattamento totale o parziale sotto qualsiasi forma (inclusi supporti magnetici, digitali o stampati). Per informazioni aggiuntive, rivolgersi agli autori: [email protected] , [email protected]

Introduzione ai Sistemi Operativi - camillobella.it

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Introduzione ai Sistemi Operativi - camillobella.it

Introduzione ai

Sistemi Operativi

DI

ANNA MARIA ROSATI ANGELO ROSIELLO

Tratto dal corso di Laboratorio Sofware

del Prof. Fornaciari

© Copyright 2005 Angelo Rosiello & Anna Maria Rosati Sono riservati tutti i diritti di traduzione in lingue straniere, di copia o riproduzione e di adattamento totale o parziale sotto qualsiasi forma (inclusi supporti magnetici, digitali o stampati). Per informazioni aggiuntive, rivolgersi agli autori: [email protected], [email protected]

Page 2: Introduzione ai Sistemi Operativi - camillobella.it

2

Indice Introduzione 3 1 Processi e Thread

1. I processi 81.1 Modello dei processi a stati 91.2 Modalità di esecuzione del kernel 141.3 La sincronizzazione 151.4 Tipologie di interazione tra processi 161.5 Requisiti per la mutua esclusione 161.6 Corsa critica e sezione critica 171.7 Soluzioni alla mutua esclusione 17

2. I thread 18Problemi

2 La Comunicazione tra i Processi

1. Lo scambio di messaggi 252. I segnali 273. Le pipe e le named-pipe 274. La memoria condivisa 285. Tracing e debugging 296. Socket 297. Remote Procedure Call 30Problemi

3 Il Deadlock

1. Definizioni 341.1 Condizioni 34

2. Strategie di gestione del deadlock 352.1 Individuazione e risoluzione del deadlock 352.2 Evitare dinamicamente il deadlock 372.3 Prevenzione del deadlock 38

3. Il deadlock nei sistemi distribuiti 383.1 Strategie di gestione del deadlock nei sistemi distribuiti 39

3.1.1 Individuazione e risoluzione del deadlock 393.1.2 Prevenzione del deadlock 403.1.3 Evitare dinamicamente il deadlock 40

3.2 Il deadlock nella comunicazione di messaggi 413.2.1 Mutual waiting 41

Page 3: Introduzione ai Sistemi Operativi - camillobella.it

3

3.2.2 Non disponibilità nei buffer dei messaggi 41Problemi 42

4 La Mutua Esclusione Distribuita

1. Algoritmi centralizzati 442. Algoritmi di elezione 44

2.1 Algoritmo del bullo 452.2 Algoritmo dell’anello 45

3. Algoritmi distribuiti 453.1 Token passing 463.2 Algoritmo completamente distribuito 46

Problemi 48

5 Le Architetture Multiprocessore 1. Sistemi operativi per architetture parallele 502. I sistemi multiprocessore a memoria condivisa 50

2.1 I sistemi multiprocessore a memoria condivisa: aspetti hardware 502.2 I sistemi multiprocessore a memoria condivisa: aspetti software 52

3. I sistemi multicomputer a scambio di messaggi 543.1 I sistemi multicomputer a scambio di messaggi: aspetti hardware 543.2 I sistemi multicomputer a scambio di messaggi: aspetti software 54

4. I sistemi distribuiti 56Problemi 57

6 Allocazione del processore nei sistemi distribuiti 1. Introduzione 592. Obiettivi degli algoritmi di allocazione dei processori (PA) 593. Classificazione degli algoritmi di PA 604. Gli algoritmi di PA 61

4.1 Algoritmo probes 614.2 Algoritmo deterministico 614.3 Algoritmo centralizzato 624.4 Algoritmo bidding 624.5 Algoritmo gerarchico 624.6 Coscheduling 63

5. La migrazione dei processi 645.1 Le politiche di migrazione 645.2 Negoziazione della migrazione 65

Problemi 67

Page 4: Introduzione ai Sistemi Operativi - camillobella.it

4

7 Scheduling del Processore 1. Definizioni 692. Criteri di scheduling 693. Tipologie di scheduling 704. Algoritmi di scheduling 70

4.1 Algoritmi di scheduling nei sistemi batch 714.1.1 First-come First-served 714.1.2 Shortest job first 724.1.3 Shortest remaining time next 72

4.2 Algoritmi di scheduling nei sistemi interattivi 734.2.1 Schedulazione round-robin 734.2.2 Schedulazione con priorità 744.2.3 Code multiple 744.2.4 Shortest process next 754.2.5 Schedulazione garantita 754.2.6 Schedulazione a lotteria 754.2.7 Schedulazione fair-share 75

4.3 Algoritmi di schedluing nei sistemi real-time 76Problemi 77

Bibliografia 78

Page 5: Introduzione ai Sistemi Operativi - camillobella.it

5

Introduzione I calcolatori moderni sono sistemi complessi con cui è difficile, se

non impossibile, un interfacciamento diretto. Il sistema operativo è uno strumento che virtualizza le caratteristiche dello hardware sottostante, offrendone una visione di una macchina astratta più potente e più semplice da utilizzare di quella fisicamente disponibile.

Il sistema operativo persegue, quindi, due obiettivi principali, cioè estendere la macchina e gestire le risorse.

Per quanto concerne l’estensione della macchina, basti pensare all’ISA1 della maggior parte dei calcolatori ed al fatto che essa è molto primitiva e non consente di svolgere in modo agevole anche semplici operazioni, quali possono essere ad esempio una lettura o scrittura su file. Uno dei compiti del sistema operativo è proprio quello di presentare al programmatore un’immediata e gradevole interfaccia a file, che nasconda la complessità dello hardware sottostante. Da questo punto di vista il sistema operativo si presta bene a presentare all’utente una macchina virtuale facile da programmare.

D’altro canto il sistema operativo deve occuparsi di gestire le risorse della macchina. Infatti, al fine di ottenere una maggiore flessibilità e potenza, i calcolatori moderni dispongono di un’enorme quantità di periferiche e risorse “interne”, che devono opportunamente essere controllate. Di conseguenza, il sistema operativo deve garantire un’allocazione ordinata, nello spazio e nel tempo, delle risorse ai processi ed ai processori, che entrano in competizione per l’utilizzo.

In definitiva, i compiti del sistema operativo non si limitano ai due principali sopra citati (i.e. estendere la macchina e gestire le risorse), ma sono ampliati in conseguenza alle decisioni contingenti che interessano i progettisti durante la fase di design, relativamente a scopi e obiettivi, e di manutenzione, sia essa correttiva, perfettiva o adattativa. Alcuni sistemi operativi (e.g. kernel Linux), per far fronte a cambiamenti contestuali o nei requisiti, permettono addirittura di aggiungere funzionalità al kernel[§kernel] dinamicamente, aumentandone di gran lunga la flessibilità e l’adattabilità.

Lo scopo di questo breve trattato è di fornire una visione introduttiva, ma non per questo semplicistica, dei sistemi operativi moderni. Le nozioni di base, relative all’architettura dei calcolatori, vengono date per assunte.

1 L’insieme di hardware e di istruzioni a disposizione di un programmatore in linguaggio assembler forma il livello ISA(instruction set architecture).

Page 6: Introduzione ai Sistemi Operativi - camillobella.it

6

Nel primo capitolo[§Processi e Thread] sono introdotti i processi ed i thread, analizzandone i problemi di sincronizzazione, mutua esclusione e possibili soluzioni. Nel secondo capitolo[§La Comunicazione tra i Processi] vengono trattate le principali modalità di comunicazione fra i processi. Il problema del deadlock locale e distribuito viene affrontato in maniera formale nel capitolo terzo[§Il Deadlock], presentando i possibili metodi che ne consentano l’identificazione e risoluzione o di evitarlo dinamicamente. Al capitolo quattro[§La Mutua Esclusione Distribuita] è rimandata la discussione sulla mutua esclusione distribuita, in cui si illustrano gli algoritmi principali adottati come solutori. I sistemi operativi per le architetture multiprocessore sono trattati nel capitolo cinque[§Le Architetture Multiprocessore], mentre l’allocazione dei processi ai processori nei sistemi distribuiti nel capitolo sei[§Allocazione del processore nei sistemi distribuiti]. In conclusione, nel capitolo sette[§Scheduling del Processore] vengono passati in rassegna i principali algoritmi di schedulazione del processore per i sistemi operativi di tipo batch, interattivo e real-time.

Anna Maria Rosati e Angelo Rosiello

Milano, Marzo 2005

Page 7: Introduzione ai Sistemi Operativi - camillobella.it

apitolo I

Processi e Thread

SOMMARIO:

1. I PROCESSI

1.1. MODELLO DEI PROCESSI A STATI

1.2. IMMAGINE DEL PROCESSO E P.C.B

1.3. MODALITÀ DI ESECUZIONE DEL KERNEL

1.4. LA SINCRONIZZAZIONE

1.5. TIPOLOGIE DI INTERAZIONE TRA PROCESSI

1.6. REQUISITI PER LA MUTUA ESCLUSIONE

1.7. CORSA CRITICA E SEZIONE CRITICA

1.8. SOLUZIONI ALLA MUTUA ESCLUSIONE

2. I THREAD.

Page 8: Introduzione ai Sistemi Operativi - camillobella.it

8

1 I processi

Un processo è un’unità fondamentale di computazione, definito come un programma eseguito dal sistema operativo; esso rappresenta l’entità attiva di base per la maggior parte dei sistemi operativi esistenti. In alternativa al termine processo, a volte si adotta il termine task, ma si tratta solo di una sottile differenza nella terminologia, il concetto è il medesimo.

Un processo dispone di un proprio spazio degli indirizzi, che raggruppa fra loro risorse correlate. Lo spazio degli indirizzi include text-segment, data-segment, stack-segment, come rappresentato nella Figura 1.1, ed altre risorse.

Figura 1.1: Sezioni di un processo in memoria.

Il text-segment di un processo di norma è accessibile in sola lettura,

ovvero non è possibile apportare delle modifiche, e contiene le istruzioni macchina che sono eseguite dall’hardware. Il data-segment include i dati del programma ed è possibile che includa diversi sotto-segmenti. Lo heap cresce dinamicamente, durante l’esecuzione del processo, per allocare dati. Lo stack contiene le variabili automatiche e gli stack-frame delle funzioni, infatti, è principalmente impiegato per gestire le chiamate a funzione. Lo spazio di “Gap” in Figura 1.1, indica che stack ed heap crescono dinamicamente durante l’esecuzione di un processo; all’interno di quest’area sono spesso allocati i segmenti di memoria condivisa.

Ad ogni processo è associato di base un thread di esecuzione che fa uso dello stack e dei registri.

Page 9: Introduzione ai Sistemi Operativi - camillobella.it

9

I sistemi operativi moderni sono multi-programmati, cioè eseguono più processi contemporaneamente, attraverso un dispatcher che ne gestisce l’avvicendamento ed impedisce la monopolizzazione del tempo del processore.

I processi eseguiti dal sistema operativo, possono essere distinti in tre principali categorie: • batch: dispone di tutti gli input allo start-up e produce tutti gli

output alla fine, senza bisogno di alcuna interazione; • interattivi: interagiscono con utenti umani al terminale; • real-time: interagiscono con dispositivi o sistemi esterni

aventi restrizioni di tempo, che devono essere soddisfatte per operare correttamente.

1.1 Modello dei processi a stati Per realizzare la multi-programmazione è necessario stabilire un

modello che rappresenti lo stato dei processi. Nel modello a stati più semplice un processo può essere in due stati:

1) Running: il processo è in esecuzione; 2) Not-Running: il processo aspetta di passare nello stato

Running.

Dispatch

Not Running

Running

Ingresso Uscita

Pausa

Figura 1.2: Modello a due stati. Di norma, esiste una coda in cui vengono messi i processi “Not-

running” che aspettano di passare allo stato di Running. L’implementazione della coda è un aspetto molto delicato, non è

Page 10: Introduzione ai Sistemi Operativi - camillobella.it

10

infatti possibile adottare una strategia FIFO2, altrimenti un processo bloccato, che ad esempio aspetta un input, bloccherebbe il sistema. Per tali ragioni occorre passare ad un modello degli stati più complesso.

Il modello a cinque stati prevede l’espansione dello stato “Not-

Running” e l’aggiunta di altri casi non contemplati nel modello a due stati:

1) New: il processo è stato creato; 2) Ready: il processo è pronto per passare allo stato di running; 3) Running: il processo è in esecuzione; 4) Blocked: il processo aspetta che si verifichi un evento per

passare allo stato di Ready; 5) Exit: il processo ha terminato la sua esecuzione.

Release

Event Occurs Event

Wait

Time-out

Dispatch

Admit

Blocked Exit

Running

Ready

New

Figura 1.3: Modello a cinque stati.

In questo caso viene considerato anche il vero e proprio ciclo di vita del processo, grazie agli stati new ed exit. Per la gestione di più

2 First in First Out: il primo arrivato è anche il primo ad essere servito.

Page 11: Introduzione ai Sistemi Operativi - camillobella.it

11

processi attivi contemporaneamente, viene utilizzata una coda per i ready e una per i bloccati, organizzate secondo una politica FIFO. Un’alternativa più efficiente consiste nell’utilizzare più code, per i processi bloccati, a cui è associato un particolare evento.

La soluzione a cinque stati non è ancora quella ottimale, occorre

aggiungere due ulteriori fasi che consentano di “spostare” (in inglese to swap) un processo su disco per liberare spazio. I due nuovi stati sono:

1) Blocked-suspend: il processo su disco aspetta il verificarsi di

un qualche evento; 2) Ready-suspend: il processo è sul disco, ma non è in attesa di

alcun evento, ovvero è già pronto.

Figura 1.4: Modello a sette stati.

Suspend

Running

Blocked Ready

Ready-suspend

New

Blocked-suspend

Suspend

Activate

Release

ActivateSuspend

Dispatch

Time-out

Event Occurs

Event Occurs

Event Wait

Admit

Admit

Exit

Page 12: Introduzione ai Sistemi Operativi - camillobella.it

12

Le cause della sospensione di un processo sono molteplici e possono variare relativamente alla specifica implementazione del sistema operativo. Di fatto, i casi più significativi sono i seguenti:

• il processo viene spostato in memoria secondaria (swapping); • il sistema operativo può sospendere un processo che sta

arrecando problemi; • l’utente può sospenderne l’esecuzione per poterlo debuggare; • un processo può essere eseguito periodicamente ed essere

sospeso mentre aspetta il prossimo intervallo di tempo (temporizzazione);

• il processo padre ne richiede esplicitamente la sospensione.

1.2 Immagine del processo e P.C.B. Per implementare il modello a processi, il sistema operativo

mantiene una tabella dei processi che contiene una elemento per ogni processo, ovvero il Process Control Block (PCB), o comunque un puntatore al PCB.

L’immagine di un processo utente in memoria virtuale presenta cinque sezioni fondamentali, come è possibile evincere dalla Figura 1.5.:

1) lo spazio condiviso con altri processi; 2) il text-segment che contiene il codice del processo, cioè le

istruzioni da eseguire; 3) il data-segment includente i dati del processo; 4) lo stack-segment che è lo stack del processo(necessario per

chiamate a procedure, passaggio di parametri, etc.); 5) il process control block, cioè la struttura dati usata dal sistema

operativo per controllare il processo.

Page 13: Introduzione ai Sistemi Operativi - camillobella.it

13

Figura 1.5: Processi utente in memoria virtuale. Il Process Control Block è una struttura dati complessa e cambia da

sistema operativo a sistema operativo, ma generalmente contiene tre categorie di informazioni:

Spazio degli indirizzi condiviso

Spazio privato degli indirizzi utente

User Stack

Process Control Block

• informazioni di identificazione del processo: numero unico identificatore che lo individua nella tabella dei processi, processo padre, utente.

• informazioni relative allo stato del processo: registri, PSW, stack pointers.

• informazioni circa il controllo del processo: stato del processo, priorità, evento atteso, flag, segnali, messaggi, etc.

Il sistema operativo deve occuparsi di gestire i processi assolvendo un insieme di compiti:

Creazione del processo: • aggiungere l’identificatore nella tabella dei processi; • allocare spazio per il processo e la sua immagine; • iniziallizare il PCB e gli stack pointers; • altro. Terminazione del processo: • il processo esegue l’ultima istruzione; • idati dei figli vengono passati al padre;

Page 14: Introduzione ai Sistemi Operativi - camillobella.it

14

• le risorse del processo sono deallocate dal sistema operativo; • scheduling e dispatching; • sincronizzazione dei processi ed IPC; • gestione del PCB.

Come già detto in precedenza, il processore esegue più processi simultaneamente, le cause dello switching di processi sono: • interrupt (causati da un evento esterno): scaduto il tempo di

clock, I/O, Memory fault; • trap (causati da eventi interni): errore, se l’errore è fatale il

processo è mosso nello stato di Exit; • System call: il processo invoca una procedura del kernel. Un processo può cambiare modalità di esecuzione (interrupt cycle)

o stato. Nel primo caso è opportuno: 1) salvare il contesto di esecuzione del programma in corso; 2) settare il PC all’inizio della routine di gestione dell’interrupt; 3) cambiare da modalità utente a modalità kernel. Nel secondo caso, cioè nel cambiamento di stato, è necessario: 1) salvare il contesto di esecuzione del processore; 2) aggiornare il PCB col nuovo stato; 3) muovere il PCB alla coda opportuna; 4) selezionare un altro processo da eseguire; 5) aggiornare il PCB del processo selezionato; 6) impostare il contesto del nuovo processo.

1.3 Modalità di esecuzione del kernel Il kernel è il sistema operativo. Le principali funzionalità offerte

sono di gestire la schedulazione della CPU, la memoria, fornire servizi quali il filesystem, garantire la sicurezza, etc. Per ottenere un quadro generale di ciò che il sistema operativo deve fare, una semplice regola è quella di definire compito del kernel ciò che implica “azioni” nel kernel-space. Il kernel usualmente interagisce in maniera diretta con l’hardware sottostante consentendo una visione astratta della macchina, di alto livello. Esistono tre principali modalità di esecuzione del sistema operativo:

Page 15: Introduzione ai Sistemi Operativi - camillobella.it

15

1) unica entità fuori dai processi utente: il codice del sistema operativo è eseguito separatamente come un’unica entità che opera in modalità kernel. Il concetto di processo esiste solo nella modalità utente.

2) All’interno dei processi utente: il sistema operativo opera all’interno del contesto dei processi e i processi sono eseguiti in modalità privilegiata quando svolgono funzioni del kernel. Un processo può eseguire sia un programma utente che un programma kernel.

3) Entità basata su processi: le principali funzionalità del kernel sono organizzate in processi che vengono invocati dal sistema operativo.

1.4 La sincronizzazione

La sincronizzazione fra processi è un fattore importante per il design del kernel, infatti l’esistenza di vari processi in esecuzione su di un sistema può causare problemi di conflitto sulle risorse comuni. Eventualmente, i processi possono cooperare fra loro per uno scopo comune migliorando le prestazioni purchè siano controllati, in particolar modo, quando utilizzano risorse condivise.

I principali problemi legati all’esecuzione in parallelo dei processi riguardano i seguenti aspetti: • la condivisione delle risorse globali: se due processi

condividono la stessa variabile globale, l’ordine in cui vi accedono in lettura e scrittura è critico;

• la gestione dell’allocazione delle risorse; • difficoltà nell’individuazione degli errori nei programmi: gli

effetti dell’esecuzione di più processi in parallelo, tipicamente, non sono nè deterministici nè riproducibili, rendendo complessa l’attività di testing, analisi e debugging.

Per risolvere queste problematiche, il sistema operativo deve:

• tenere traccia dei processi attivi; • allocare e deallocare le risorse (CPU time, memoria,

dispositivi di I/O, file); • proteggere i dati e le risorse;

Page 16: Introduzione ai Sistemi Operativi - camillobella.it

16

• fare in modo che i risultati di un processo siano indipendenti dalla velocità di esecuzione, dato che altri processi condividono il tempo di CPU.

1.5 Tipologie di interazione tra processi

Una primaria classificazione dei modi in cui i processi interagiscono fra loro, è possibile grazie al grado di coscienza che essi hanno dell’esistenza degli altri processi. Esistono tre possibili gradi:

1) processi del tutto indipendenti: non hanno la consapevolezza dell’esistenza di altri processi e possono entrare in concorrenza per l’acquisizione delle risorse comuni. Si verificano problemi di mutua esclusione, deadlock, starvation.

2) Processi consapevoli indirettamente: hanno la consapevolezza dell’esistenza di altri processi in maniera indiretta attraverso l’uso di risorse comuni ma gestite in maniera da cooperare fra loro e non interferire l’un l’altro. Si verificano problemi di mutua esclusione, deadlock, starvation, incoerenza dei dati.

3) Processi consapevoli direttamente: hanno la consapevolezza dell’esistenza di altri processi con i quali comunicano, cooperando insieme. Si verificano problemi deadlock e starvation.

1.6 Requisiti per la mutua esclusione

I principali requisiti per la mutua esclusione sono i seguenti: • solo un processo alla volta può entrare nella sezione critica

per una risorsa o oggetto condiviso; • un processo che si blocca nella sua zona non critica non deve

interferire con l’esecuzione di altri processi; • non si deve ritardare all’infinito l’accesso alla zona critica di

un processo che ne ha richiesto l’ingresso;

Page 17: Introduzione ai Sistemi Operativi - camillobella.it

17

• quando nessun altro processo è nella sua zona critica, il primo processo che ne richiede l’ingresso deve poterci entrare senza alcun ritardo;

• un processo può rimanere nella sua zona critica per un tempo finito.

1.7 Corsa critica e sezione critica

La sezione critica di un processo è quella parte di codice che utilizza delle risorse comuni ad altri processi e che può condurre a corse critiche. L’accesso alle zone critiche deve avvenire in mutua esclusione e possono esistere più zone critiche, per lo stesso processo, facenti riferimento a risorse diverse.

1.8 Soluzioni alla mutua esclusione

Esistono vari approcci per risolvere il problema della mutua esclusione: software, hardware e come supporto a livello del sistema operativo o del linguaggio di programmazione.

Software L’algoritmo di Dekker (si veda Stallings[2002]) è stato riportato

da Dijkstra nel 1965 come soluzione al problema della mutua esclusione, senza far ricorso all’alternanza stretta, ma impiegando l’attesa attiva (busy waiting). Nel 1981 l’algoritmo di Peterson, riportato in Figura 1.6 (da Tanenbaum[2002]), per la sua semplicità rese obsoleta la soluzione di Dekker.

Hardware

L’approccio hardware prevede due soluzioni: 1) disabilitare gli interrupt: tutti gli interrupt sono disabilitati quando

un processo entra nella sua sezione critica. L’efficienza dell’esecuzione del programma risulta fortemente degradata.

Page 18: Introduzione ai Sistemi Operativi - camillobella.it

18

Un’altra possibile complicazione è che un programma non esca mai dalla zona critica, mandando in stallo la macchina. Inoltre bisogna considerare che in presenza di più processori, non è comunque garantita la mutua esclusione, perché gli interrupt sono disabilitati solo sul processore che esegue il processo in oggetto.

2) Introdurre speciali istruzioni macchina: molte macchine,

principalmente quelli progettati pensando a processori multipli, adottano l’istruzione test&set-lock:

TSL RX, LOCK test&set-lock

Essa consiste nell’assegnare alla variabile lock il valore 1 quando si desidera entrare in zona critica, ma prima di entrarvi si confronta il vecchio valore di lock per vedere se era già stato posto ad 1, in tal caso non si entra in sezione critica e si itera (busy waiting) la procedura fino a che non si trova il valore di lock pari a 0. Perché il sistema funzioni è richiesta l’alternanza stretta, e comunque deadlock e starvation potrebbero verificarsi.

#define N 2 //Numero di processi int turno; boolean interessato[N]; void entra_nella_regione_critica(int processo) { int altri; altri=1-processo; interessato[processo]=VERO; turno=processo; while(turno==processo && interessato[altri]==VERO)} void esci_dalla_regione_critica(int processo) { interessato[processo]=FALSO; }

Figura 1.6: L’algoritmo di Peterson.

Page 19: Introduzione ai Sistemi Operativi - camillobella.it

19

Supporto del sistema operativo o del linguaggio di programmazione Sia la soluzione di Peterson che quella di test&set-lock sono corrette, ma hanno entrambe il difetto di ricorrere all’attesa attiva, deteriorando in questo modo le prestazioni della CPU e provocando effetti inaspettati. Infatti, si supponga ad esempio che due processi, uno H (ad alta priorità, eseguito non appena passa allo stato di pronto) ed uno L (a bassa priorità) si trovino ad un certo istante con L in zona critica ed H che passa allo stato di pronto. H comincerà l’attesa attiva senza consentire ad L di lasciare la zona critica. Questo fenomeno è detto inversione delle priorità.

Un’alternativa al busy-waiting è l’impiego di due chiamate di sistema dette sleep e wakeup che provocano l’addormentamento del processo chiamante finchè qualcuno non lo risvegli, attraverso la wakeup.

I semafori di Dijkstra (Dijkstra[1965]) rappresentano la soluzione più generale al problema della concorrenza evitando il busy waiting. L’idea di base è quella di consentire a due o più processi di cooperare fra loro attraverso un’appropriata struttura di segnali. Per realizzare la segnalazione sono utilizzate delle variabili dette semafori, argomento delle primitive signal() e wait(), generalizzazioni di sleep&wakeup. Il semaforo è inizializzato ad un valore non negativo. L’operazione wait decrementa il semaforo che una volta raggiunto un valore negativo blocca il processo invocante la wait. L’operazione signal incrementa il semaforo, se il vecchio valore era negativo, un processo bloccato dalla wait viene riattivato. Le primitive wait e signal sono atomiche, cioè non possono essere interrotte fino a loro completamento. Una versione più semplificata ma non meno potente dei semafori generali è quella dei semafori binari, che possono assumere solo i valori 0 ed 1. Dato che più processi posso essere bloccati da un unico semaforo, bisogna stabilire un ordine per il loro risveglio. Una soluzione consiste nel collocarli in una coda FIFO (strong semaphores) e un’altra nel non specificare l’ordine (weak semaphores), non garantendo però l’assenza da starvation.

Il concetto di monitor venne introdotto da Hoare ed Hansen, per facilitare la scrittura di programmi corretti. Tale soluzione richiede

Page 20: Introduzione ai Sistemi Operativi - camillobella.it

20

il supporto del linguaggio di programmazione, essendo un modulo software che garantisce la mutua esclusione. Il monitor è un oggetto astratto che garantisce ai suoi utilizzatori l’accesso in maniera esclusiva alle operazioni invocate. In ogni istante, solo un processo può essere attivo in un monitor e vi entra chiamando una delle sue procedure, esposte dalla sua interfaccia. I dati locali del monitor sono modificabili solo da procedure interne al monitor stesso. I monitor risolvono il problema della mutua esclusione, ma è opportuno anche definire dei meccanismi che consentano ad un processo di bloccarsi, quando la risorsa che richiede non è disponibile (ad esempio quando il buffer è vuoto nel problema del produttore-consumatore). La soluzione consiste nell’introduzione di variabili di condizione, manipolate attraverso le procedure wait e signal, che agiscono come code, dove i processi possono essere sospesi o risvegliati. Un processo che chiama una signal lascia automaticamente il monitor (Hansen[1975]), per evitare di avere due processi attivi nel monitor.

2 I thread Ogni processo possiede almeno un thread di controllo, che è l’entità

schedulata dalla CPU per l’esecuzione. In un processo possono esistere più thread che condividono lo stesso spazio degli indirizzi, ma in contesti diversi. Infatti, ad ogni thread è associato un program counter ed uno proprio stack.

Thread diversi, in uno stesso processo, non sono indipendenti come lo sono processi distinti, dal momento che condividono lo stesso spazio di indirizzamento e che quindi un thread può leggere, scrivere e persino cancellare lo stack di un altro thread.

Analogamente ai processi sono previsti degli stati di esecuzione, quali quello di pronto, attivo, sospeso e terminato.

Page 21: Introduzione ai Sistemi Operativi - camillobella.it

21

Processo

Stack del Thread 1

Stack del Thread 2

Thread 2

Thread 1

Figura 1.7: i thread in un processo.

I vantaggi legati alla creazione di più thread, in un singolo processo, invece di più processi, riguardano sia il risparmio delle risorse che il miglioramento delle prestazioni.

Per evidenziare le occasioni in cui è conveniente impiegare i thread, al posto di più processi, sono stati formulati alcuni pattern ricorrenti: • team model: utile per partizionare un’attività. Si spezzetta

l’attività primaria in sotto attività, svolte da thread diversi. • architettura a dispatcher: esiste un thread detto dispatcher che

riceve le richieste di servizio e le delega ai thread che le eseguono. I thread vengono eventualmente creati sul momento.

• architettura a pipeline: utile per gestire una catena di algoritmi, ciascuna dei quali utilizza come dato in ingresso l’uscita di un altro algoritmo.

Vi sono due modi principali per implementare i thread, entrambi adottanti un thread package per creazione, terminazione e controllo:

1) thread nello spazio utente: il thread package è realizzato nell’user space. Viene implementato un sistema di supporto run-time sopra il kernel per gestire lo scheduling dei thread. Ogni processo possiede una tabella dei propri thread simile alla tabella dei processi del kernel.

Questa implementazione è molto performante e permette di aggiungere il supporto dei thread ad un sistema operativo che non li prevede, ma è difficile garantire il multi-threading e soprattutto evitare che un thread bloccato non stalli l’intero processo3.

3 Un thread deve rilasciare autonomamente la CPU.

Page 22: Introduzione ai Sistemi Operativi - camillobella.it

22

2) Thread nel kernel: il thread package è realizzato nel kernel space. Meno efficienti dei thread nello user space, a causa delle chiamate di sistema, ma più facilmente gestibili (è applicabile lo scheduling round-robin).

Negli ultimi anni sono stati studiati molti metodi per cercare di combinare i vantaggi dei thread a livello utente con quelli dei kernel thread: • molti a uno: molti user thread sono mappati su un solo kernel

thread; • uno a uno: un user thread su un kernel thread (Windows,

OS/2). • molti a molti.

Dato che i thread devono potere cooperare fra loro, essi adottano gli stessi meccanismi di mutua esclusione, sincronizzazione e comunicazione utilizzati dai processi (a tal proposito si veda il Capitolo 2).

Page 23: Introduzione ai Sistemi Operativi - camillobella.it

23

Capitolo I

Problemi

Dare una definizione di processo e scrivere un programma in cui

un processo padre crea un figlio, mediante la chiamata di sistema

fork( ). Stampare a video il pid del processo figlio e commentarne

il risultato.

Discutere le sezioni di un processo in memoria.

Indicare le cause e le conseguenze della multi-programmazione.

Cos’è il modello dei processi a stati? Qual è la sua funzione?

Elencare le cause della sospensione di un processo.

Definire ed illustrare la struttura del process control block.

Quali sono le principali modalità di esecuzione del kernel?

Un processo padre può terminare un processo figlio? Se sì,

implementare un programma esemplificativo.

A quale scopo i processi interagiscono fra loro?

Quali sono le principali modalità di interazione fra i processi?

Dare una definizione di corsa critica e mutua esclusione.

Illustrare e commentare le principali soluzioni al problema della

mutua esclusione.

Discutere le differenze ed analogie fra processi e thread.

Fornire un esempio in cui è ritenuto utile un approccio

architetturale basato su più thread.

Page 24: Introduzione ai Sistemi Operativi - camillobella.it

24

apitolo II

La Comunicazione tra i Processi

SOMMARIO:

1. LO SCAMBIO DI MESSAGGI

2. I SEGNALI

3. LE PIPE E LE NAMED-PIPE

4. LA MEMORIA CONDIVISA

5. TRACING E DEBUGGING

6. SOCKET

7. REMOTE PROCEDURE CALL.

Page 25: Introduzione ai Sistemi Operativi - camillobella.it

25

La comunicazione tra i processi può avvenire in locale, tra processi in spazi di indirizzi separati, oppure in un contesto distribuito, tra differenti computer connessi ad una rete.

Le problematiche relative all’IPC4 trattano innanzitutto le modalità con cui un processo può passare informazioni ad un altro, nonchè il fatto che i processi non si debbano intralciare quando sono occupati in attività critiche. I principali meccanismi di comunicazione tra i processi sono riportati nei paragrafi successivi, è opportuno vagliarle criticamente e tenere presente che è possibile riscontrare delle piccole variazioni nelle implementazioni pratiche.

1 Lo scambio di messaggi

Lo scambio di messaggi è il metodo di comunicazione più usato per i processi, sia all’interno dello stesso computer, sia in un sistema distribuito, e consente di realizzare la sincronizzazione e la mutua esclusione. Lo scambio di messaggi avviene mediante la chiamata di due primitive send(destination, message) e receive(source, message). La comunicazione può essere sincrona (bloccante) oppure asincrona (non bloccante), a seconda che siano bloccanti o meno la send() e la receive().

Nel caso in cui la send() che la receive() siano entrambi bloccanti, il trasmettitore ed il ricevente sono entrambi bloccati finchè il messaggio non è stato ricevuto, creando quindi una forte sincronizzazione (rendez-vous) ed una comunicazione non “bufferizzata”.

Figura 2.1: Rendez-vous. L’indirizzamento del messaggio può avvenire in maniera diretta,

mediante un identificatore del processo, oppure, più convenientemente, in maniera indiretta, con l’impiego di mailbox

rendez-vous

Message Buffer Message Buffer

4 Termine largamente usato in letteratura, “Inter process communication”, cioè la comunicazione inter-processi.

Page 26: Introduzione ai Sistemi Operativi - camillobella.it

26

condivise, cioè sostanzialmente delle code verso cui vengono inviati i messaggi, raccolti da coloro che ricevono.

Ogni mailbox può essere ad uso privato per una coppia trasmettitore-ricevitore, ma condivisibile tra diversi trasmettitori e ricevitori.

Figura 2.2: Comunicazione indiretta tra processi per mezzo di una mailbox. Una porta, così come riportato in Figura 2.3, è una mailbox

associata ad un ricevitore e trasmettitori multipli, usata soprattutto per applicazioni di tipo client-server. Essa viene posseduta e creata dal processo ricevitore e viene distrutta quando il ricevitore termina.

Figura 2.3: Comunicazione indiretta tra processi per mezzo di una porta. Più in generale, ogni mailbox viene distrutta su richiesta del

processo proprietario, oppure quando quest’ultimo termina. Il formato del messaggio è costituito da un header, che include

informazioni di controllo, la lunghezza del messaggio, ID sorgente/destinazione ed un body, ovvero il contenuto del messaggio vero e proprio.

La mutua esclusione con lo scambio di messaggi prevede:

Mailbox

P1

Pn

Q1

Qn

Porta

P1

Pn

Q1

• creazione di una mailbox mutex condivisa da n processi; • send() non bloccante; • receive() bloccante quando il mutex è vuoto;

Page 27: Introduzione ai Sistemi Operativi - camillobella.it

27

• qualora un processo x che esegue la receive() entri nella sezione critica, tutti gli altri processi sono bloccati, finchè x non ritrasmette il messaggio.

Il problema del produttore-consumatore, legati ad un buffer, si

risolve agevolmente con lo scambio di messaggi, dato che esso consente di scambiare dati ed inviare segnali contemporaneamente. L’approccio si basa sull’utilizzo di due mailbox, mayconsume e mayproduce. I dati man mano che vengono creati dal produttore sono inviati come messaggi alla mailbox mayconsume, dove i consumatori possono prelevarli, finchè ci sono, esattamente come in un meccanismo di buffer e coda di messaggi. Inizialmente la mailbox mayproduce è riempita con un numero di messaggi nulli pari alla capacità del buffer, e viene usata dai consumatori per inoltrare le richieste al produttore, che le raccoglie. Il numero dei messaggi in mayproduce si riduce quindi con ogni produzione e cresce con ogni messaggio consumato.

Unix fornisce ai processi le chiamate di sistema msgsnd() e msgrcv() per realizzare lo scambio di messaggi.

2 I segnali

Un segnale è un messaggio dato da un singolo bit di informazione, che consente di notificare asincronamente, sia per il ricevitore che per il trasmettitore, l’occorrenza di particolari eventi, quali ad esempio timer, eccezioni di programmi, completamento di operazioni I/O, etc. I segnali possono essere inviati da un processo all’altro, oppure dal kernel internamente. Il processo che riceve il segnale può rispondere eseguendo una funzione di signal handler, cioè di gestione del segnale, o ignorare del tutto l’evento.

3 Le pipe e le named-pipe

Nei sistemi operativi Unix-like le pipe sono dei buffer circolari, di dimensione variabile, scritti da un processo e letti da un altro (unidirezionali), l’uno inconsapevole dell’esistenza dell’altro. Esse si basano sul modello di produttore-consumatore e sono accessibili solo in mutua esclusione, mediante un descrittore, proprio

Page 28: Introduzione ai Sistemi Operativi - camillobella.it

28

come se fossero dei file. Il produttore è bloccato se non c’è spazio sufficiente per scrivere, mentre il consumatore è bloccato se tenta di leggere più byte di quanti ce ne siano nella pipe.

Esistono due tipi di pipe: named e unnamed, o anche semplicemente pipe. Mentre le named-pipe possono essere condivise da processi non correlati tra loro, mediante un file nel filesystem, le semplici pipe sono destinate solo a processi correlati tra loro (e.g. padre-figlio).

Per evitare deadlock è necessario che tutti i processi chiudano i descrittori che non gli servono.

4 La memoria condivisa

I processi, di norma, hanno spazi di indirizzamento distinti e non sono presenti aree fisiche comuni a piu' processi. Tuttavia, il meccanismo di memoria condivisa consente di allocare dinamicamente segmenti di memoria condivisa e di inserirli, attraverso opportune operazioni, all'interno dello spazio di indirizzamento dei processi che ne facciano richiesta. Quando un processo ha terminato di usare il segmento puo' staccarlo dal suo spazio di indirizzamento o, se ne e' il creatore, restituirlo al sistema. L'area di memoria condivisa viene a fare parte dell’immagine di un processo utente in memoria, insieme a text-segment, data-segment, e stack-segment(si veda la sezione [§1.2]).

L’uso della memoria condivisa è la forma di comunicazione più veloce di IPC offerta da Unix, poiché funziona senza oggetti intermediari. Tuttavia, uno “svantaggio” di questo meccanismo è che il sistema operativo non offre la gestione della mutua esclusione, quindi i processi stessi devono occuparsene direttamente.

Figura 2.4: Vista logica dei processi e memoria condivisa.

P3 P2P1

Memoria Condivisa

Page 29: Introduzione ai Sistemi Operativi - camillobella.it

29

Figura 2.5: Creazione di una pipe in Linux.

#define INPUT 0 #define OUTPUT 1 void main() {

int descrittori[2]; pid_t pid; char buffer[32];

pipe(descrittori); pid=fork(); if(pid==0) { //se sono nel figlio… close(descrittori[INPUT]); write(descrittori[OUTPUT], “test”, strlen(test)); } else { //altrimenti se sono nel padre… close(descrittori[OUTPUT]); read(descrittori[INPUT], buffer, sizeof(buffer)); } }

5 Tracing e debugging La maggior parte dei sistemi operativi mette a disposizione una

particolare chiamata di sistema che consente il tracciamento del flusso di esecuzione di un processo. Lo scopo è quello di supportare il programmatore nell’attività di testing, analisi e debugging di una applicazione.

Un esempio concreto è la syscall ptrace [Linux Manual 1999], che consente ad un processo di osservare e controllare l’esecuzione di un altro processo, ma anche di esaminare e modificare i registri e l’immagine di memoria.

6 Socket

Tra le varie forme di IPC le socket sono di gran lunga la più

popolare e consentono una comunicazione bidirezionale, cioè in entrambi i sensi, da punto a punto, tra i processi. Le socket furono inventate a Berkeley come parte dello UNIX BSD e si diffusero rapidamente con internet. Di fatto, esse rappresentano un sistema di comunicazione orientato alla connessione attraverso cui i processi

Page 30: Introduzione ai Sistemi Operativi - camillobella.it

30

aprono un “socket”,ovvero un canale di comunicazione, comunicano e quando hanno finito chiudono il canale di trasmissione. Si distinguono tre principali tipologie di socket: • Stream socket: bidirezionali, affidabili, supportanti il

protocollo TCP; • Datagram socket: bidirezionali ma non affidabili, supportanti

il protocollo UDP; • Raw socket: permettono l’accesso diretto a protocolli di

comunicazione sottostante.

7 Remote Procedure Call

Nata come variante del modello a scambio di messaggi, la chiamata

a procedura remota è il metodo attualmente più usato per incapsulare la comunicazione tra processi, in un sistema distribuito. Il meccanismo, introdotto nel 1984 da Birrell e Nelson [Birrell, Nelson 1984], consente ai processi risiedenti su macchine differenti di interagire usando semplicemente delle chiamate a procedure, tramite delle interfacce standardizzate, proprio come se risiedessero tutti sulla stessa macchina.

Quando un processo P effettua la chiamata di una procedura remota, lo stub S locale impacchetta un messaggio, che identifica la procedura remota chiamata e che comprende i parametri passati. Lo stub della macchina remota riceve il messaggio, invoca ed esegue localmente la procedura richiesta, raccoglie ed impacchetta i valori restituiti ed invia il messaggio di risposta. Lo stub S, dopo aver ricevuto il pacchetto, legge i valori e li restituisce al processo P chiamante.

Esistono due tipi di chiamate a procedura remota: • sincrona/bloccante: sono le RPC tradizionali, richiedono che il

processo chiamante attenda finchè il processo chiamato non restituisce il valore, quindi non sfruttano completamente il parallelismo in sistemi distribuiti.

• Asincrona/non-bloccante: offrono maggiore flessibilità ed un alto grado di parallelismo, in quanto il processo chiamante non deve necessariamente aspettare la risposta del processo remoto. Se viene effettuata una RPC sincrona dopo una serie di RPC asincrone, il processo chiamato risponderà alla RPC sincrona solo dopo aver riposto alle altre chiamate asincrone precedenti.

Page 31: Introduzione ai Sistemi Operativi - camillobella.it

31

Figura 2.6: Schema architetturale del meccanismo RPC.

Page 32: Introduzione ai Sistemi Operativi - camillobella.it

32

Capitolo II

Problemi

Dare una soluzione al problema del produttore-consumatore con lo scambio di messaggi.

Elencare le principali modalità di comunicazione tra i processi.

Qual è la forma di comunicazione più veloce nei sistemi Unix?

Scrivere un programma minimale che riceva dall’utente un intero, crei un figlio e scambi con esso tale intero per mezzo di memoria condivisa. Il figlio riceva tale intero dal padre e lo stampi a video. Per la sincronizzazione scegliere il meccanismo che si preferisce.

Si spieghi verbalmente in che cosa consiste la memoria condivisa e come essa viene realizzata dal sistema operativo. Si scrivano due distinti programmi minimali che scambiano fra loro un numero intero. Il primo programma (che chiameremo tx.c) legga da terminale utente il numero intero (per esempio con una scanf()), quindi lo scriva in memoria condivisa e avvisi il secondo programma che il numero `e pronto, inviandogli il segnale SIGUSR1. Il secondo programma (che chiameremo rx.c), alla ricezione del segnale, estragga il numero dalla memoria condivisa e lo stampi a video. Si assuma per semplicità che il PID del secondo processo sia 7834.

Illustrare il meccanismo di comunicazione pipe e named-pipe.

Spiegare come avviene una chiamata a procedura remota, aiutandosi mediante uno schizzo dell’architettura.

Implementare utilizzando le socket, un server che accetta connessioni sulla porta 80. I client devono potersi collegare ed inviare messaggi, ricevendo come risposta un anagramma del messaggio trasmesso.

Page 33: Introduzione ai Sistemi Operativi - camillobella.it

33

apitolo III

Il Deadlock

SOMMARIO:

1. 1 DEFINIZIONI 1.1. CONDIZIONI

2. STRATEGIE DI GESTIONE DEL DEADLOCK 2.1. INDIVIDUAZIONE E RISOLUZIONE DEL DEADLOCK 2.2. EVITARE DINAMICAMENTE IL DEADLOCK 2.3. PREVENZIONE DEL DEADLOCK

3. IL DEADLOCK NEI SISTEMI DISTRIBUITI 3.1. STRATEGIE DI GESTIONE DEL DEADLOCK NEI SISTEMI DISTRIBUITI

3.1.1. INDIVIDUAZIONE E RISOLUZIONE DEL DEADLOCK 3.1.2. EVITARE DINAMICAMENTE IL DEADLOCK 3.1.3. PREVENZIONE DEL DEADLOCK

3.2. IL DEADLOCK NELLA COMUNICAZIONE DI MESSAGGI 3.2.1. MUTUAL WAITING 3.2.2. NON DISPONIBILITÁ NEI BUFFER DEI MESSAGGI

Page 34: Introduzione ai Sistemi Operativi - camillobella.it

34

1 Definizioni Per comprendere appieno il problema “deadlock” occorre

innanzitutto definire il concetto di risorsa. Per risorsa si intende qualsiasi componente hardware o software accessibile da un processo in maniera esclusiva. Le risorse possono essere distinte in due classi:

preemptable (rilasciabili): possono essere portate via dal loro possessore senza problemi;

non-preemptable (non rilasciabili): la computazione fallisce se portate via dal proprietario.

La starvation si verifica quando alcuni processi, anche non coinvolti nel deadlock, non riescono mai ad ottenere le risorse e a continuare la propria esecuzione venendo rimandati indefinitamente, in favore di altri processi. Questo fenomeno è evitabile adottando politiche di First Come First Served (FCFS) o di round-robin (si veda il capitolo 7).

1.1 Condizioni

Un insieme S di processi è in deadlock se ciascuno di essi sta aspettando un evento che solo un altro processo in S può scatenare.

Le condizioni necessarie affinchè si verifichi il deadlock sono: 1) mutua esclusione: le risorse sono disponibili o assegnate ad un

solo processo; 2) hold&Wait: i processi che già possiedono delle risorse possono

richiederne delle nuove; 3) no preemption: le risorse prese precedentemente possono essere

rilasciate solo spontaneamente dal proprietario; 4) attesa circolare: ci deve essere una catena circolare di processi,

ciascuno in attesa della risorsa posseduta dal processo successivo.

I deadlock possono essere individuati mediante un grafo orientato, cioè il grafo di allocazione delle risorse, in cui i nodi Pi sono i processi e i nodi Ri le risorse, mentre gli archi (Pi, Ri) indicano che Pi è bloccato in attesa di Ri (fig. 1b) e gli archi (Ri, Pi) che Pi è il proprietario di Ri (fig. 1a).

Page 35: Introduzione ai Sistemi Operativi - camillobella.it

35

Se esistono dei cicli si è in presenza di deadlock. Nell’esempio riportato nella figura sottostante, R1, P1, R3 e P2 sono in deadlock, infatti esiste un ciclo che coinvolge proprio tutti questi nodi.

Fig 2: il grafo di allocazione delle risorse in presenza di un deadlock

Una volta definito il problema deadlock, occorre stabilire come

gestirlo. È sempre possibile, naturalmente, ignorare il problema, soprattutto se la frequenza dei deadlock è bassa e non si prevedono danni critici al sistema. Inoltre, si possono introdurre delle restrizioni, impedendo, ad esempio, ai processi il forking dinamico. In alternativa a questo approccio lassista, si può adottare una delle strategie illustrate nei paragrafi immediatamente seguenti.

R Pheld by

Fig 1a: un processo P possiede la risorsa R

R2

R3

P1

P2

P3

Fig 1b: un processo P è bloccato in attesa di una risorsa R

waiting forP R

R1

2 Strategie di gestione del deadlock

2.1 Individuazione e risoluzione del deadlock

Non si cerca di evitare il deadlock ma solo di individuarlo per poi risolverlo. Tale strategia si differenzia a seconda che si considerino una risorsa per ogni classe oppure risorse multiple per ogni classe. Nell’individuazione con una risorsa per ogni classe, si disegna e si tiene aggiornato il grafo associato ad una risorsa, monitorando le condizioni di deadlock. Per quanto riguarda l’individuazione con risorse multiple per ogni classe, si introducono E[E1,..Ej,..Em] e A[A1,..Aj,..Am] rispettivamente

Page 36: Introduzione ai Sistemi Operativi - camillobella.it

36

il vettore delle risorse esistenti ed il vettore delle risorse disponibili, dove n è il numero dei processi e m è il numero delle risorse. Si definisce inoltre C[cij] la matrice nxm di allocazione corrente delle risorse, dove cij indica il numero delle istanze della risorsa j-esima posseduta dal processo i-esimo. Sia inoltre R[rij] la matrice nxm delle richieste, dove rij indica il numero delle risorsa j-esima richiesta dal processo i-esimo. Sussiste quindi la relazione:

∑ni=1 cij + Aj = Ej

Inizialmente tutti i processi non sono marcati. I passi dell’algoritmo sono: – si marcano tutti i processi che hanno una riga di tutti 0 nella

matrice C. Questi processi non possono andare in deadlock, perchè non allocano risorse.

– Si trova il processo i-esimo tale che la riga i-esima di R ≤ A. Tale condizione implica il fatto che il processo può essere soddisfatto dalle risorse disponibili. Se tale processo non esiste, vuol dire che sicuramente c’è un deadlock e l’algoritmo termina. In caso contrario, si prosegue al passo successivo.

– Se la riga i-esima esiste, si marca il processo i-esimo e si aggiorna A, aggiungendo i valori della riga i-esima di C, simulando il rilascio delle risorse.

– Si ricomincia dal secondo passo, fino alla terminazione, cioè quando i processi sono tutti marcati o il deadlock è stato rilevato.

La risoluzione dopo l’individuazione del deadlock avviene nei seguenti modi: ripristino con preemption: le risorse possono essere portate via dal

proprietario ed assegnate ad un nuovo processo. Difficilmente ha successo, solitamente è richiesto un intervento manuale.

Ripristino con rollback: periodicamente vengono fatti dei checkpoint, cioè delle immagini della memoria insieme allo stato della risorsa correntemente allocata per il processo. Quando viene trovato un deadlock, ogni processo viene riportato con un rollback nello stato precedente, liberando la risorsa. È necessario tenere conto del trade-off tra la frequenza dei deadlock e la densità dei checkpoint.

Ripristino con terminazione dei processi: i processi coinvolti nel ciclo del deadlock i processi che possiedono una risorsa richiesta vengono portati ad uno stato precedente all’acquisizione della

Page 37: Introduzione ai Sistemi Operativi - camillobella.it

37

risorsa, rilasciandola. In aggiunta, si terminano tutti i processi non appartenenti al ciclo ma che occupano risorse che potrebbero servire ai processi in deadlock.

2.2 Evitare dinamicamente il deadlock

Per determinare l’allocazione delle risorse si può adottare l’algoritmo del banchiere, che presuppone i concetti di stato sicuro e stato non sicuro.

Il sistema si dice che è in uno stato sicuro, se il deadlock è assente ed esiste un modo per soddisfare le richieste pendenti di esecuzione dei processi in un qualche ordine.

Lo stato, invece, non è sicuro, quando i processi proseguono la loro esecuzione, ma non viene garantita la loro terminazione. Il sistema, perciò, consente, almeno in linea di massima, l’allocazione delle risorse richieste, solo se essa conduce il sistema in uno stato sicuro.

L’algoritmo del banchiere nel caso delle singole risorse prevede che ogni processo possieda un numero massimo di risorse. Le risorse sono esaminate nell’ordine di arrivo, se una richiesta da parte di un processo P conduce in uno stato non sicuro il sistema, il processo P viene posto in stato di attesa. Alcuni problemi di applicazione dell’algoritmo sono rappresentati dalla difficoltà di predire le risorse necessarie, dal numero dei processi che varia dinamicamente e dal numero e dal tipo di risorse disponibili che variano anch’essi dinamicamente, ad esempio a causa di un malfunzionamento.

Nel caso delle risorse multiple, invece, si applica l’algoritmo del banchiere per le risorse multiple, facendo riferimento alle matrici A, E, C, definite nel paragrafo precedente. L’algoritmo consta dei seguenti passi:

si cerca la riga i-esima di R, che individua il processo i-esimo, tale per cui R ≤ A;

se tale riga esiste, si marca il processo (e la riga) corrispondente e si aggiorna A, aggiungendo i valori della riga i-esima di C, simulando il rilascio delle risorse. Se la riga non esiste, il deadlock è assicurato e nessun processo terminerà correttamente.

Si ricomincia dall’inizio, fino a che tutti i processi (e le righe) sono marcati ed il sistema è quindi in uno stato sicuro.

Page 38: Introduzione ai Sistemi Operativi - camillobella.it

38

2.3 Prevenzione del deadlock Si cerca di fare in modo che almeno una di queste condizioni non sia soddisfatta:

a. mutua esclusione: quando è possibile si rendono le risorse condivisibili e si adotta uno spooler5 per ogni dispositivo, laddove possibile, che mette in coda le richieste.

b. Hold&wait: ogni processo P deve chiedere prima dell’esecuzione le risorse necessarie, altrimenti è sospeso. É difficile prevedere in anticipo le risorse necessarie, talvolta si rischia di richiedere risorse non utilizzate poi successivamente, non ottimizzando così al meglio l’uso delle risorse. Una variante consiste nel rilasciare temporaneamente le risorse possedute dal processo P, prima di effettuare una richiesta, e nel tentare di prendere tutte le richieste atomicamente.

c. no preemption: applicabile solo in casi rari; d. attesa circolare: le risorse sono ordinate numericamente, ogni

processo può possedere solo una risorsa per volta. Il grafo di allocazione è aciclico se in qualsiasi momento un processo P non può essere in attesa di una risorsa già assegnata, anche se in pochi casi è possibile trovare un ordine che soddisfi tutte le richieste dei processi. L’utilizzo di una sola risorsa per volta limita anche semplici operazioni come la copia di un disco.

Per aggiornare un database, si può procedere adottando un algoritmo a due fasi. Nella prima fase, il processo che deve modificare il database effettua un lock di tutti i record, uno alla volta. Se durante questa fase alcuni record sono in stato di busy, i lock effettuati vengono rilasciati ed il processo riavvia la prima fase. Nella seconda fase, i record vengono aggiornati ed il lock viene rilasciato. Questo algoritmo è indicato per i processi che possono essere riavviati senza effetti indesiderati, inoltre non è difficile prevedere le risorse necessarie per operazioni sui database.

3 Il deadlock nei sistemi distribuiti Nei sistemi distribuiti oltre ai deadlock che hanno origine

dall’allocazione delle risorse, esistono anche i deadlock originati dalla comunicazione di messaggi.

5 i.e.: processo demone unico gestore di un dispositivo.

Page 39: Introduzione ai Sistemi Operativi - camillobella.it

39

Le possibili strategie per la gestione dei deadlock in ambiente distribuito sono quelle viste già in precedenza: ignorare il problema (naturalmente è sempre possibile), individuazione e risoluzione del deadlock, prevenzione del deadlock ed evitare dinamicamente il deadlock.

In caso di individuazione di possibile deadlock, le politiche suicide da adottare sono: • wait-die: un processo P può essere bloccato solo se è più

vecchio del processo che possiede la risorsa necessaria a P. In altre parole, in caso di conflitto hanno la precedenza i processi più giovani. • Wound-wait: un processo P può prevaricare i processi più

giovani, che si mettono in attesa dopo il loro restart.

3.1 Strategie di gestione del deadlock nei sistemi

distribuiti

3.1.1 Individuazione e risoluzione del deadlock

La difficoltà è data dal fatto che ogni sito è a conoscenza solo delle proprie risorse, mentre il deadlock può coinvolgere risorse distribuite. Le strategie sono diverse, a seconda che il sistema di controllo sia centralizzato, gerarchico o distribuito.

In un sistema centralizzato, un nodo è responsabile del controllo ed avendo un quadro completo della situazione è in grado di individuare un deadlock. In generale si spende molto tempo ad inviare messaggi con un timestamp alle macchine coinvolte in un possibile deadlock, in modo da avere una situazione aggiornata del sistema. I pregi di questa soluzione sono la semplicità e la facilità di implementazione. I difetti riguardano il sovraccarico di informazioni circolanti, la vulnerabilità se il nodo fallisce e l’individuazione di falsi deadlock, dovuta al ritardo nella ricezione delle informazioni che modificano il grafo ed all’inconsistenza nell’aggiornare l’intero grafo.

In un sistema gerarchico, la struttura dei siti è organizzata ad albero, ogni nodo è in grado di trovare il deadlock che coinvolge i suoi nodi dipendenti. Il sistema non è vulnerabile se un nodo fallisce e l’attività di ripristino è ridotta se i deadlock potenziali sono localizzati. In aggiunta, esso è difficilmente configurabile, i deadlock individuati sono solo quelli nei livelli più bassi ed esiste un sovraccarico di informazioni.

Page 40: Introduzione ai Sistemi Operativi - camillobella.it

40

In un sistema distribuito, tutti i processi cooperano per trovare il deadlock, facendo circolare un considerevole carico di informazioni. L’algoritmo è invocato ogni volta che un processo P è in attesa di risorse. Un messaggio sonda è inviato a tutti i processi che possiedono le risorse, contenente l’identificatore del processo P bloccato, del processo trasmettitore del messaggio e del ricevitore. Quando il messaggio arriva al ricevitore, se il processo è in attesa di nuove risorse, esso invia un nuovo messaggio ai processi che possiedono le risorse, mantendo lo stesso identificatore del processo bloccato. Se il messaggio, invece, ritorna al primo trasmettitore, significa che esiste un ciclo di attesa, cioè un deadlock. Questo algoritmo non è vulnerabile se un nodo fallisce, non implica nessun sovraccarico dei nodi e la risoluzione dei deadlock è semplificata. D’altro canto, è un algoritmo difficile da progettare ed i nodi potrebbero non essere in grado di identificare tutti i nodi coinvolti nel deadlock.

Una volta individuato il deadlock, si deve decidere se terminare i processi o ripristinare lo stato precedente, a seconda che il sistema sia rispettivamente normale o basato sulle transazioni.

3.1.2 Prevenzione del deadlock É applicabile soprattutto in sistemi transazionali. La condizione di Hold&Wait può essere prevenuta imponendo che un processo richieda tutte le risorse in una volta e bloccando il processo finchè tutte le richieste possano essere accolte simultaneamente. Il sistema è chiaramente inefficiente, trascorre un tempo lungo prima che le risorse siano liberate e concesse contemporaneamente. La condizione di attesa circolare è prevenuta, invece, ordinando attentamente le richieste ed i permessi delle risorse. Per sistemi transazionali con un tempo globale, se un processo Pi ha bisogno di una risorsa posseduta da un altro processo Pj, confronta il proprio timestamp con quello di Pj per vedere qual è il più grande. Se Pi non è in grado di restare in attesa, termina l’esecuzione.

3.1.3 Evitare dinamicamente il deadlock Non è una strategia molto usata, poichè è difficile prevedere l’allocazione delle risorse. Ogni nodo, infatti, deve tenere traccia dello stato globale del sistema, cercando in mutua esclusione di trovare uno

Page 41: Introduzione ai Sistemi Operativi - camillobella.it

41

stato sicuro. Tale attività implica un notevole overhead di elaborazione per un sistema distribuito con tanti processi e risorse.

3.2 Il deadlock nella comunicazione di messaggi 3.2.1 Mutual Waiting Tipicamente un processo può andare avanti se uno qualsiasi dei

messaggi che sta aspettando arriva, oppure, banalmente, quando arrivano tutti.

Il deadlock nella comunicazione di messaggi di un insieme S di processi si manifesta quando: • tutti i processi in S sono bloccati, in attesa di messaggi; • S contiene tutti i successori di ogni membro di S; • nessun messaggio è in transito tra i membri di S. 3.2.2 Non disponibilità nei buffer dei messaggi Problema molto noto nelle reti a scambio di pacchetti. Il più

semplice è il deadlock diretto, cioè quando A ha il buffer pieno di pacchetti per B e B ha il buffer pieno di pacchetti per A. Un modo per evitare ciò è non permettere che i buffer siano dedicati ad una sola connessione.

Il deadlock indiretto, invece, avviene quando per ogni nodo X la coda verso il nodo adiacente Y è piena con pacchetti destinati al nodo successivo ad Y. La soluzione è l’impiego di un insieme strutturato di buffer, organizzati gerarchicamente.

In generale, l’uso di buffer di dimensione finita per la

comunicazione tra processi è a rischio di deadlock. Infatti, se l’invio non è bloccante, i messaggi in uscita devono essere salvati in un buffer, che, una volta pieno, porrà in blocco il processo.

Inoltre se due processi comunicano utilizzando buffer separati e provano entrambi ad inviare prima di ricevere, quando i loro buffer sono pieni, ecco che il sistema entra in deadlock.

Le soluzioni possibili al problema prevedono la definizione di un limite superiore al numero di messaggi scambiati per ogni coppia di processi (diventando però causa di un’inutile perdita di spazio) e l’impiego di tecniche euristiche.

Page 42: Introduzione ai Sistemi Operativi - camillobella.it

42

Capitolo III

Problemi Quali sono le condizioni necessarie affinchè sia presente un deadlock?

Cosa si può fare in modo che la condizione di attesa circolare non sia soddisfatta?

In che cosa differiscono le strategie di gestione del deadlock?

Illustrare l’algoritmo del banchiere nel caso delle risorse multiple.

Che cosa deve accadere per avere un deadlock nella comunicazione di messaggi?

Definire i concetti di risorsa “preemptable” e risorsa “non-preemptable”.

Page 43: Introduzione ai Sistemi Operativi - camillobella.it

43

apitolo IV

La Mutua Esclusione

Distribuita

SOMMARIO:

1. ALGORITMI CENTRALIZZATI

2. ALGORITMI DI ELEZIONE

2.1. L’ALGORITMO DEL BULLO

2.2. L’ALGORITMO DELL’ANELLO

3. ALGORITMI DISTRIBUITI

3.1. IL TOKEN PASSING

3.2. L’ALGORITMO COMPLETAMENTE DISTRIBUITO.

Page 44: Introduzione ai Sistemi Operativi - camillobella.it

44

La mutua esclusione distribuita può essere realizzata attraverso vari algoritmi, di fatto bisogna garantire le condizioni di mutua esclusione discusse in [§1.6].

1 Algoritmi centralizzati Un particolare processo, detto processo coordinatore[§44], gestisce

l’interazione fra gli altri processi. Il processo coordinatore controlla l’accesso alla sezione critica. Un

processo chiede con un messaggio l’autorizzazione ad utilizzare una risorsa e si blocca finchè non l’ottiene. L’autorizzazione è concessa attraverso un reply del coordinatore. Quando il processo rilascia la risorsa invia un altro messaggio al coordinatore.

Problemi: • il coordinatore rappresenta il collo di bottiglia; • se il coordinatore fallisce, il sistema si blocca, bisogna perciò

individuare il fallimento ed eleggere un nuovo coordinatore. • Può fallire anche il client che detiene la risorsa; per ovviare al

problema si può inviare un messaggio al client a cui esso deve rispondere, per stabilire se è ancora vivo.

Nell’algoritmo centralizzato non c’è possibilità di starvation, se il coordinatore è equo.

2 Algoritmi di elezione

Nei casi in cui si adotta un coordinatore centralizzato, questo può fallire, bisogna quindi accorgersene ed attivare uno ed un solo sostituto. Di fatto, il problema consiste nell’individuazione di un nuovo processo che possa coordinare altri processi simili, per offrire un determinato servizio.

Si assume che sia nota l’identità di tutti i processi e che i processi abbiano un identificatore unico “id” di priorità, di cui tutti gli altri processi sono a conoscenza. L’elezione assicura un accordo fra i processi circa il processo coordinatore che ha sempre il maggior

Page 45: Introduzione ai Sistemi Operativi - camillobella.it

45

numero di priorità non faulty. Al termine della fase di elezione, l’identità del coordinatore è resa nota a tutti i processi del sistema.

2.1 Algoritmo del bullo

Un processo P che sospetti dell’assenza di un coordinatore (cioè il coordinatore non risponde alle richieste) può indire una elezione, inviando un messaggio a tutti i processi aventi id più alto. Se nessuno risponde, allora P è il vincitore e sarà il nuovo coordinatore, altrimenti perderà ed aspetterà la notifica del nuovo coordinatore. Allo stesso tempo P blocca tutti i processi aventi id a lui inferiore e da cui riceve messaggi di elezione. Alla fine rimane un solo processo vincente che informerà gli altri inviando un messaggio. Quando si inserisce un nuovo processo o un processo viene risvegliato o fatto ripartire, la prima azione compiuta è una nuova richiesta di elezione.

2.2 Algoritmo dell’anello

I processi sono ordinati (logicamente o fisicamente) ad anello, ciascuno dei quali conosce il proprio successore. Quando un processo P sospetta la mancanza di un coordinatore invia un messaggio di elezione, contenente il suo id, al processo che lo segue nell’anello; se quest’ultimo è giù viene inviato al successivo e così via. In questo modo ciascun nodo dell’anello riceve e propaga il messaggio al prossimo nodo, aggiungendovi il proprio id. Quando il messaggio ripasserà per P, esso raccoglierà il nodo con l’id più alto ed invierà il messaggio eletto agli altri nodi comunicando il coordinatore.

3 Algoritmi distribuiti Nella classe degli algoritmi distribuiti, tutti i processi cooperano

comunicando fra loro, per risolvere un dato problema. Gli algoritmi di gran lunga più usati sono il token passing e l’algoritmo completamente distribuito.

Page 46: Introduzione ai Sistemi Operativi - camillobella.it

46

3.1 Token passing

L’algoritmo del token passing prevede che i processi formino un anello logico in cui ogni processo conosca l’indirizzo del processo che lo segue. I passi del procedimento sono tre:

1. un token circola nell’anello; 2. il processo che acquisisce il token può acquisire ed usare la

risorsa; 3. dopo aver usato la risorsa, o se non ne ha bisogno, passa il

token.

Questo algoritmo è equo ma se il token va perso il sistema si stalla, in questo caso il token deve essere rigenerato. Se un processo fallisce, anche l’anello deve esserere rigenerato.

Figura 4.1: Il token passing.

Token

P1

P2 P3

P4

P6 P5

Risorsa A

Risorsa B

3.2 Algoritmo completamente distribuito

I sistemi distribuiti non hanno un clock globale, ma in alcuni casi è necessario considerare un ordinamento globale di tutti gli eventi del sistema. A questo scopo si utilizza un clock logico mantenuto da ogni processo per mezzo di time-stamp associati ad ogni messaggio. Perché questo

Page 47: Introduzione ai Sistemi Operativi - camillobella.it

47

metodo funzioni è necessario che ciascun messaggio sia inviato a tutti i processi del sistema. Per gestire la mutua esclusione sono realizzati i seguenti passi:

1. quando un processo p vuole entrare nella sua zona critica invia un messaggio di richiesta a tutti i processi del sistema, inserendovi il time-stamp;

2. un processo q che riceve il messaggio di richiesta ha 2 possibili alternative, o invia un reply a p autorizzandolo ad ottenere la risorsa oppure posticipa il reply ad un momento successivo.

3. Il processo p può entrare nella sua zona critica se e solo se ha ricevuto il reply da tutti i processi del sistema.

4. Quando un processo p esce dalla sua zona critica invia il reply agli eventuali processi in attesa di risposta e a cui aveva rimandato il tempo di risposta.

La decisione relativa alle alternative del punto (2) è effettuata in relazione al seguente flusso di controllo:

a) se il processo q era già nella sua zona critica, differisce l’invio del reply;

b) se q non vuole entrare in una sua zona critica invia il reply immediatamente;

c) se q vorrebbe entrare nella sua zona critica ma non ha ancora ricevuto tutti i reply necessari allora confronta il suo time-stamp con quello di p. Nel caso in cui Time-Stamp(p) < Time-Stamp(q) allora q invia reply a p, altrimenti lo sospende.

L’algoritmo completamente distribuito consente di ottenere la

mutua esclusione, escludendo il verificarsi di starvation e deadlock. L’unico lato sconveniente è che richiede la partecipazione di tutti i processi del sistema, quindi ogni processo deve conoscere l’identità degli altri. Qualora un processo fallisca, tutto il sistema decade, occorre dunque disporre di alcuni meccanismi per identificare una failure e notificare la situazione agli attori del sistema.

Page 48: Introduzione ai Sistemi Operativi - camillobella.it

48

Capitolo IV

Problemi

Descrivere gli algoritmi centralizzati ed i principali problemi che li caratterizzano.

Illustrare l’algoritmo del bullo.

Discutere vantaggi e svantaggi dell’algoritmo dell’anello.

Quali sono i passi del procedimento del token passing?

Argomentare, a grandi linee, circa l’algoritmo completamente distribuito, sottolinenando, in particolar modo, le motivazioni che inducono all’esistenza del time-stamp.

Page 49: Introduzione ai Sistemi Operativi - camillobella.it

49

apitolo V

Le Architetture Multiprocessore

SOMMARIO:

1. SISTEMI OPERATIVI PER ARCHITETTURE PARALLELE

2. I SISTEMI MULTIPROCESSORE A MEMORIA CONDIVISA

2.1. I SISTEMI MULTIPROCESSORE A MEMORIA CONDIVISA: ASPETTI HARDWARE

2.2. I SISTEMI MULTIPROCESSORE A MEMORIA CONDIVISA: ASPETTI SOFTWARE

3. I SISTEMI MULTICOMPUTER A SCAMBIO DI MESSAGGI

3.1. I SISTEMI MULTICOMPUTER A SCAMBIO DI MESSAGGI: ASPETTI HARDWARE

3.2. I SISTEMI MULTICOMPUTER A SCAMBIO DI MESSAGGI: ASPETTI SOFTWARE

4. I SISTEMI DISTRIBUITI

Page 50: Introduzione ai Sistemi Operativi - camillobella.it

50

1 Sistemi operativi per architetture parallele A seconda delle tecnologie di interconnessione, si possono

distinguere diverse tipologie di architetture di sistema: i sistemi multiprocessore a memoria condivisa, i sistemi multicomputer a scambio di messaggi, i sistemi distribuiti.

Nei paragrafi seguenti verranno descritti i sistemi sopra elencati, mettendo in luce gli aspetti hardware e gli aspetti software di ciascuno.

2 I sistemi multiprocessore a memoria condivisa

Nei sistemi multiprocessore a memoria condivisa due o più CPU comunicano tramite la memoria condivisa, leggendo e scrivendo le stesse locazioni di memoria. I programmi vedono lo stesso spazio di indirizzamento virtuale, quindi potrebbe verificarsi la situazione in cui un processore modifica un dato e poi legge lo stesso, riscontrando un valore diverso da quello scritto, dato che tale dato è stato sovrascritto da un altro processore. Questa proprietà, se gestita correttamente, costituisce la base per la comunicazione tra i processori: un processore scrive una zona di memoria, che verrà letta da un altro processore. I sistemi operativi per questo tipo di sistema sono simili a quelli tradizionali, fatta eccezione per alcuni aspetti quali la gestione delle risorse, la sincronizzazione e lo scheduling dei processi.

2.1 I sistemi multiprocessore a memoria condivisa:

aspetti hardware

Dal punto di vista dello hardware, ogni parola della memoria può essere letta alla stessa velocità delle altre (Uniform Memory Access, UMA), oppure no (Not Uniform Memory Access, NUMA), quest’ultimo caso è indispensabile, ad esempio, in presenza di più di 100 processori. Le architetture UMA si dividono in:

1. architetture UMA Simmetric Multi Processor (SMP) basate su bus: due o più CPU con moduli di memoria utilizzano lo stesso bus per la comunicazione. Quando una CPU ha bisogno di leggere una parola di memoria, controlla se il bus è occupato.

Page 51: Introduzione ai Sistemi Operativi - camillobella.it

51

Se lo è, deve attendere finchè non diventa disponibile, altrimenti, se è libero, vi scrive l’indirizzo della parola, aspettando che il dato venga messo sul bus. Per ridurre la contesa per l’accesso, è possibile inserire una cache locale per ogni CPU, che soddisfa molte letture e consente di diminuire il traffico sul bus. Aggiungendo la cache, si introducono anche i problemi di coerenza sui dati, in seguito alle scritture sulle cache locali, che invalidano i dati condivisi. Una soluzione più efficiente consiste nel dotare ogni CPU di uno spazio di indirizzamento privato di memoria, oltre alla cache locale. Questo caso prevede che la memoria condivisa venga usata solo per i dati condivisi e richiede una cooperazione attiva del compilatore, che deve collocare tutte le stringhe, le costanti, i dati e lo stack dei programmi nella memoria privata. In ogni caso, utilizzando un bus singolo, si possono avere 16 CPU, fino ad un massimo di 32.

2. architetture UMA basate su Crossbar Switch: consentono di superare le limitazioni imposte dal bus singolo sul numero di CPU supportate. Le n CPU sono connesse a k memorie a livello di circuito da una matrice di commutazione (n x k), in cui ogni intersezione (i,j) rappresenta il collegamento tra la i-esima CPU e la j-esima memoria. Il collegamento può essere aperto o chiuso. Il pregio di una matrice di commutazione è quello di avere una rete non-bloccante, in quanto il collegamento non viene mai negato a causa del traffico, ma viene garantito l’accesso simultaneo tra tutte le CPU e le memorie. Purtroppo, il numero dei punti di intersezione cresce come n².

3. architetture UMA basate su reti di commutazione multistadio: rappresentano un compromesso tra il bus e la matrice di commutazione. Lo switch impiegato ha due ingressi e due uscite, come nella Rete Omega. Il numero di switch per n CPU ed n memorie è di (n/2)log2n, sicuramente meglio di n², ottenuto con la matrice di commutazione. La rete è bloccante, cioè non tutte le richieste possono essere accolte simultaneamente e possono insorgere dei conflitti, perciò è possibile usare più switch per limitare i blocchi.

Le architetture NUMA, invece, consentono di collegare fino a 100 CPU e adottano un meccanismo di accesso alla memoria non omogeneo, in quanto è più veloce l’accesso alla memoria locale.

Page 52: Introduzione ai Sistemi Operativi - camillobella.it

52

Sussiste ancora un unico spazio di indirizzamento accessibile con operazioni di load/store e, quando sono presenti delle cache locali coerenti, il sistema viene detto CC-NUMA (Cache-Coerent NUMA). I sistemi CC-NUMA utilizzano i protocolli a Directory per implementare la cache-coherence. Se invece le cache sono assenti, se cioè il tempo di accesso alla memoria non è mascherato, si ha un sistema NC-NUMA (Not-Cached NUMA).

2.2 I sistemi multiprocessore a memoria condivisa:

aspetti software

Gli aspetti software riguardano le tipologie di sistema operativo, la sincronizzazione e lo scheduling dei processi. Per quanto riguarda le tipologie di SO, si possono avere diverse configurazioni: – ogni CPU ha il proprio SO: la memoria è frazionata e le strutture

dati sono replicate. I pregi sono: è semplice, in quanto ogni porzione di memoria è privata ed estendibile; le risorse sono condivise; le comunicazioni inter-processore sono efficienti. I difetti sono: il carico è sbilanciato, perchè non c’è comunicazione tra i processi; c’è uno spreco di memoria e di risorse, dato che non c’è condivisione di pagine; esistono dei problemi di consistenza dei buffer per i dispositivi di I/O, essendo presenti in più copie, uno per ogni CPU.

– Sistemi master-slave: solo la CPU Master ha il SO che raccoglie tutte le chiamate di sistema, smista i processi, bilanciando il carico, permette la condivisione delle pagine ed elimina i problemi di consistenza, permettendo solo una copia dei buffer dei dispositivi I/O. L’unico difetto è che se il numero di CPU supera il tetto di 5, il Master diventa un collo di bottiglia.

– Sistemi simmetrici: il SO ha una sola copia, ma ogni CPU lo può eseguire, effettuando le proprie chiamate di sistema. I processi e la memoria vengono bilanciati dinamicamente. I problemi sono dati dal fatto che il SO deve essere eseguito in mutua esclusione, diviso in parti indipendenti accessibili in parallelo, con la possibilità che si formino dei colli di bottiglia per l’accesso.

Page 53: Introduzione ai Sistemi Operativi - camillobella.it

53

La sincronizzazione è fondamentale, soprattutto per quanto riguarda l’accesso esclusivo a risorse critiche, dato che non è più sufficiente nè disabilitare semplicemente gli interrupt, nè utilizzare il test-set&lock, che, dovendo lockare anche il bus, causa spreco di risorse e sovraccarico. Esistono vari algoritmi per ridurre lo spreco di risorse, ad esempio le liste di attesa o tentativi ritardati. In merito allo scheduling dei processi, il SO deve decidere quale processo eseguire e su quale CPU eseguirlo. Le possibili strategie sono:

1) time sharing: si utilizza una singola tabella dei processi (con priorità) per tutto il sistema, ogni processore esegue il processo successivo in stato di pronto, bilanciando così il carico complessivo. Tuttavia la cache interna al processore risulta poco sfruttata ed esiste la contesa per l’accesso alla tabella dei processi. È possibile introdurre dei miglioramenti con:

a. affinity scheduling: un processo viene assegnato all’ultimo processore che l’aveva eseguito in precedenza.

b. Two-level algorithm: un gruppo di processi è assegnato ad una CPU che li gestisce con una struttura dati dedicata. Perciò quando una CPU è idle prende un processo da un’altra CPU, bilanciando il carico. Questo metodo consente inoltre di massimizzare la cache affinity e di ridurre la contesa per la tabella dei processi.

2) Space sharing: un gruppo di k processi (thread) correlati viene

assegnato a k CPU disponibili, facendo in modo che in ogni istante l’insieme di CPU è diviso in gruppi che eseguono processi correlati tra loro. È ottimo per lavori batch, in cui si conoscono le relazioni tra i processi. Lo space sharing elimina l’overhead del cambio del contesto, ma le CPU possono rimanere idle per molto tempo. Non considerando le relazioni tra i processi, si possono verificare delle inefficienze dovute alle comunicazioni. Alcuni algoritmi effettuano in contemporanea lo scheduling nel tempo e nello spazio, considerando anche le relazioni tra i processi (thread).

3) Gang scheduling: i gruppi di processi correlati, cioè le gang,

vengono schedulati in modo indivisibile ed ogni membro della gang viene eseguito simultaneamente agli altri in timesharing da più processori. Inoltre i membri della gang hanno time slice

Page 54: Introduzione ai Sistemi Operativi - camillobella.it

54

(porzioni di tempo) coincidenti. Infine, allo scadere di ogni quanto le CPU sono rischedulate.

In definitiva, l’implementazione di questa architettura è alquanto

complessa. 3 I sistemi multicomputer a scambio di messaggi

Nei sistemi multicomputer a scambio di messaggi le varie coppie (CPU, memoria locale) sono collegate tra loro tramite una rete ad alta velocità e non condividono memoria. In pratica sono dei normali calcolatori collegati da una rete di interconnessione.

3.1 I sistemi multicomputer a scambio di messaggi:

aspetti hardware Dal punto di vista hardware, il nodo base di un multicomputer consiste in uno o più processori, in una memoria e in un’interfaccia di rete ed, eventualmente, un hard disk. Per quanto riguarda le tecnologie di interconnessione, ogni nodo è connesso ad altri nodi o ad uno switch, secondo una stella, o un anello, un cubo, un ipercubo, ecc. Le strategie di switching possono essere a pacchetto, a circuito o wormhole routing6, una via di mezzo tra le prime due.

Le interfacce di rete sono dotate di RAM per rendere continuo il flusso di bit in ricezione ed in trasmissione, con eventuali controller DMA o CPU on-board. Le modalità di interazione delle interfacce di rete, infatti, condizionano significativamente il sistema operativo.

3.2 I sistemi multicomputer a scambio di messaggi:

aspetti software

Dal punto di vista del software, la comunicazione avviene:

6 Un pacchetto è diviso in parti più piccole che fluiscono man mano che il percorso viene stabilito.

Page 55: Introduzione ai Sistemi Operativi - camillobella.it

55

1) a basso livello: nella trasmissione vengono effettuate copie dei pacchetti, per minimizzare tale numero si usa un’interfaccia di rete mappata nello user-space. I problemi connessi sono alla condivisione dei processi ed all’accesso da parte del kernel, con la necessità di una doppia interfaccia di rete.

2) a livello utente: i processi si scambiano i messaggi mediante send-receive, che possono essere bloccanti (sincrone) o non (asincrone). Nel caso in cui la comunicazione sia asincrona, è necessario introdurre un buffer per memorizzare il messaggio ed ulteriori meccanismi di gestione.

3) Remote Procedure Call (RPC): vedi capitolo 2, paragrafo 7. 4) Memoria condivisa distribuita: le pagine sono dislocate nelle

varie memorie locali. Quando una CPU richiede una pagina che non ha (page fault remoto), avviene una chiamata a SO, che la recupera e la invia. La gestione del meccanismo è quindi affidata al software (SO), mentre nei sistemi multiprocessore allo hardware.

Per quanto riguarda lo scheduling, ogni CPU ha un insieme di

processi da eseguire e non è possibile uno scambio di processi tra le CPU, visti gli elevati costi di comunicazione. L’allocazione dei processi sui vari nodi deve essere tale da garantire il bilanciamento del carico e la minimizzazione delle comunicazioni tra i nodi, proprio perchè non è possibile intervenire a posteriori.

Gli algoritmi per l’assegnamento del processore sono: graph theoretic deterministic (algoritmo deterministico): cercano

l’allocazione che minimizza il traffico sulla rete, in particolare un insieme di taglio che soddisfi dei vincoli di CPU e di memoria e ne minimizzi altri (vedi capitolo 6, paragrafo 4.2).

Sender-initiated distributed heuristic: se il nodo che esegue un processo è sovraccarico, cerca altri nodi, in modo casuale o seguendo una lista di nodi spesso liberi, con carico inferiore e gli spedisce il processo. Se la ricerca non ha successo dopo k tentativi, il processo viene eseguito localmente.

Receiver-initiated distributed heuristic: duale al precedente, quando un nodo ha carico basso, cerca altri nodi che hanno processi da eseguire, in maniera casuale o mediante un elenco dei nodi spesso sovraccarichi. Il traffico di comunicazione diventa alto quando il sistema è molto scarico.

Page 56: Introduzione ai Sistemi Operativi - camillobella.it

56

Questi sistemi offrono un semplice modello di comunicazione, ma sono difficili e costosi da costruire al crescere del numero dei processori.

Essi sono conosciuti anche con altri nomi, inclusi “Cluster Computers” e “Cluster of Workstations (COWS)”.

4 I sistemi distribuiti

Sono sistemi multicomputer collegati tramite una rete WAN, ma mentre i sistemi multicomputer a scambio di messaggi sono fortemente accoppiati, i sistemi distribuiti sono debolmente accoppiati ed i ritardi di comunicazione sono significativi. In ogni nodo è presente una CPU, la RAM, eventualmente un hard disk ed un insieme di periferiche. A differenza del sistema multicomputer, i nodi di un sistema distribuito possono avere sistemi operativi differenti, ognuno ha il proprio file-system e generalmente sono dislocati in tutto il mondo.

Page 57: Introduzione ai Sistemi Operativi - camillobella.it

57

CAPITOLO V Problemi

Cosa accade in un multiprocessore se due CPU tentano di accedere alla stessa parola di memoria, nello stesso istante?

Illustrare i vantaggi di un’architettura UMA basata su Crossbar Switch rispetto ad una basata su bus.

Descrivere le tecniche di scheduling nei sistemi multiprocessore a memoria condivisa.

Discutere le tipologie di sistema operativo per i sistemi multiprocessore a memoria condivisa.

Come avviene la comunicazione in un sistema multicomputer a scambio di messaggi?

Page 58: Introduzione ai Sistemi Operativi - camillobella.it

58

apitolo VI

Allocazione del processore nei sistemi

distribuiti

SOMMARIO:

1. INTRODUZIONE 2. OBIETTIVI DEGLI ALGORITMI DI ALLOCAZIONE DEI PROCESSORI (PA) 3. CLASSIFICAZIONE DEGLI ALGORITMI DI PA 4. GLI ALGORITMI DI PA

4.1. ALGORITMO PROBES 4.2. ALGORITMO DETERMINISTICO 4.3. ALGORITMO CENTRALIZZATO 4.4. ALGORITMO BIDDING 4.5. ALGORITMO GERARCHICO 4.6. COSCHEDULING

5. LA MIGRAZIONE DEI PROCESSI 5.1. LE POLITICHE DI MIGRAZIONE 5.2. NEGOZIAZIONE DELLA MIGRAZIONE

Page 59: Introduzione ai Sistemi Operativi - camillobella.it

59

1 Introduzione Il sistema consiste in un insieme di processori connessi

completamente, sui quali deve essere allocato un insieme di processi. Le strategie possibili di allocazione sono: • non-migratoria: il processo P resta sulla stessa macchina

finchè non termina l’esecuzione; • migratoria: un processo già attivo su una macchina può

spostarsi su un’altra, trasferendone lo stato. Il carico risulta così meglio bilanciato.

2 Obiettivi degli algoritmi di allocazione dei

processori (PA)

L’uso di algoritmi di allocazione dei processori implica la volontà di ottimizzare il sistema, ponendo l’accento su un particolare aspetto. Gli obiettivi, pertanto, possono essere differenti:

massimizzazione dell’utilizzo della CPU: ogni CPU deve sempre essere impegnata, quindi occorre massimizzare il numero di cicli di CPU impiegati effettivamente per eseguire i processi utente;

equa distribuzione del carico di lavoro tra tutte le CPU; minimizzare il tempo medio di risposta; condivisione del carico: implica una strategia migratoria dei

processi; performance della comunicazione: i processi che richiedono

un’interazione intensa devono essere spostati sullo stesso nodo, in modo da ridurre i costi di comunicazione.

disponibilità: i processi eseguiti per un tempo prolungato possono aver bisogno di spostarsi, dato che la macchina su cui sono collocati non sarà più disponibile.

necessità di caratteristiche particolari: un processo può richiedere una speciale caratteristica hardware o software, messa a disposizione da un determinato sistema.

Page 60: Introduzione ai Sistemi Operativi - camillobella.it

60

3 Classificazione degli algoritmi di PA Gli algoritmi di allocazione dei processori si distinguono in:

– deterministici / euristici: partono dal presupposto di avere completa conoscenza del comportamento futuro dei processi da eseguire. Tale previsione è solitamente improbabile da effettuare.

– Migratori / non migratori: nei sistemi migratori i processi possono essere spostati su un processore differente durante l'esecuzione. Nei sistemi non-migratori l'assegnamento del processo al processore viene fatto subito all'inizio e la decisione non viene modificata in seguito.

– Centralizzati / distribuiti: mentre nei sistemi distribuiti le informazioni sono dislocate nei nodi, nei sistemi centralizzati si concentrano tutte le informazioni in un punto in modo da consentire di prendere decisioni migliori, pagando in robustuzza, con la possibilità di avere un collo di bottiglia.

– Ottimi / sub-ottimi: il calcolo della soluzione ottima è spesso computazionalmente intrattabile, dato che i problemi di ottimizzazione associati allo scheduling spesso sono NPcompleti. Perciò, occorre ripiegare su algoritmi sub-ottimali che hanno una complessità computazionale ragionevole.

– Locali / globali (Transfer Policy): dopo aver creato un processo occorre stabilire se eseguirlo localmente oppure no. La decisione si può basare su criteri locali, ad esempio se il carico del processore locale è inferiore ad una soglia, oppure globali, che implicano la raccolta delle informazioni sul carico degli altri processori per prendere una decisione.

– Sender-initiated / Receiver-initiated: se il processo deve essere trasferito altrove, bisogna decidere dove spedirlo. Il compito può essere affidato al processore locale o a quelli remoti che segnalano la propria disponibilità.

Alcuni problemi riguardano il fatto che gli algoritmi di PA

assumono di conoscere con sicurezza l'utilizzazione di un processore, intesa come frazione del tempo speso dal processore ad eseguire codice, ma come misurare tale parametro? Inoltre la complessità computazionale degli algoritmi di PA spesso non viene considerata, anche se il tempo che si impiega a decidere cosa fare potrebbe essere più utilmente impiegato per farlo.

Page 61: Introduzione ai Sistemi Operativi - camillobella.it

61

Infine, molti degli algoritmi proposti non considerano il tempo necessario per trasferire un processo da un processore ad un altro, ma spesso le reti di comunicazione rappresentano il collo di bottiglia.

4 Gli algoritmi di PA

4.1 Algoritmo probes

Quando il processore su cui viene eseguito un processo diventa sovraccarico, esso decide di trasferire il processo altrove.

Il processore, quindi, “sonda” un altro, chiedendo qual è il suo carico. Se il processore sondato risponde di essere sottoutilizzato, il processo vi viene trasferito, occupando il processore meno utilizzato.

Altrimenti, una nuova sonda è inviata ad un altro processore scelto casualmente, finchè viene trovato un processore sottoutilizzato o si supera il limite massimo di tentativi a disposizione.

4.2 Algoritmo deterministico

Ci sono N processi da distribuire su k processori identici (N>k) e per ogni coppia di processi è nota l’entità del traffico di messaggi che si scambiano. L’obiettivo è distribuire i processi in modo da minimizzare il traffico tra i processori.

Ecco un esempio con 9 processi e 3 processori:

B

H

5

2 4

3

3

C

A

2 1

I

1

D

4

7

5

E

4

1

F

6 3

G

Page 62: Introduzione ai Sistemi Operativi - camillobella.it

62

I problemi connessi a questo algoritmo sono dovuti al fatto che, in primo luogo, conoscere in anticipo il modello di comunicazione tra i processi è nella maggior parte dei casi impossibile.

In secondo luogo, il flusso di dati scambiati tra processi può variare nel tempo, quindi un’allocazione può essere ottimale in una fase della computazione ma non in un'altra.

In generale, trovare una partizione di un grafo in due sottografi con lo stesso numero di nodi tali che il numero di archi che attraversa la partizione sia minimo è un problema NPhard.

4.3 Algoritmo centralizzato

L’obiettivo dell'algoritmo non è massimizzare l'utilizzo delle CPU, ma bilanciare il carico di lavoro fra tutti. Ad ogni workstation viene assegnato un punteggio, che aumenta, se essa spedisce ad altri i processi originati localmente. Il punteggio, invece, diminuisce, se essa esegue processi originati da altre workstation. Quando un processore è in stato di idle, riceve il processo originato dalla workstation col punteggio più basso. Una variante consiste nell’incrementare o decrementare il punteggio di una quantità fissa per ogni secondo di CPU richiesto o offerto.

4.4 Algoritmo bidding

L'algoritmo precedente può essere modificato, assumendo che l'insieme dei processori sia un sistema economico e che i prezzi delle risorse, cioè il tempo di CPU, siano fissati da leggi di mercato. Ogni processore calcola il prezzo del servizio da lui offerto, basandosi sulle richieste e sulla qualità dei servizi supportati. Ogni volta che un processo deve essere eseguito, vengono valutati i prezzi dei vari processori e si sceglie il più conveniente.

4.5 Algoritmo gerarchico

Gli algoritmi centralizzati hanno una scalabilità limitata, dato che il nodo centrale può diventare il collo di bottiglia. Il problema può essere affrontato utilizzando un algoritmo gerarchico, che conserva la semplicità dell'algoritmo centralizzato, ma con migliori possiblità di scalabilità.

Page 63: Introduzione ai Sistemi Operativi - camillobella.it

63

La struttura gerarchica viene realizzata istituendo, ogni k processori, un supervisore che tiene traccia del carico di lavoro di ogni CPU.

Per evitare di avere un solo supervisore al vertice della gerarchia, è possibile modificare l'albero, ponendo al vertice un comitato di supervisori. In caso di fallimento di un supervisore, deve essere eletto un sostituto. Può essere impiegato qualsiasi algoritmo di elezione. Il nuovo supervisore può essere scelto tra i supervisori dello stesso livello, oppure uno del livello superiore, o infine può essere prelevato dai livelli inferiori. Le ipotesi sottostanti al funzionamento dell’algoritmo gerarchico sono: • i processori sono monoprogrammati, cioè eseguono un solo

processo per volta; • le richieste di esecuzione di un processo che richiede k processori

possono apparire in qualsiasi nodo gerarchico. Ogni supervisore controlla il numero di processori disponibili nella partizione di cui è responsabile. Se ricevendo la richiesta di k processori ne ha un numero inferiore a disposizione, la richiesta è passata al livello superiore. Altrimenti, la richiesta viene accettata e suddivisa in parti, in base alla disponibilità di CPU libere che risultano ai livelli inferiori, e le parti vengono propagate verso il basso, fino all’ultimo livello.

4.6 Coscheduling

L'idea di base è che è conveniente eseguire insieme tutti i processi che comunicano tra di loro.

Consideriamo una matrice di N colonne, dove N è il numero di processori, in cui ogni riga della matrice rappresenta un time slot. Ogni cella della matrice può essere vuota o contenere l'identificatore di un processo.

Processori Time

slot 0 1 2 3 4 5 6

0 B C A 1 D F H 2 B D C G H 3 B C E 4 F A

Page 64: Introduzione ai Sistemi Operativi - camillobella.it

64

Ad ogni passo, tutti i processori eseguono i processi assegnati per quel time slot.

5 La migrazione dei processi Il concetto di base è quello di trasferire una quantità sufficiente di

dati relativi allo stato di un processo, dalla macchina sorgente a quella destinataria. Il processo viene interamente eseguito sulla macchina destinataria rendendo opportuna l’eliminazione del processo dalla macchina sorgente e la sua creazione su quella di destinazione (anche il PCB deve essere trasferito). Le motivazioni che rendono indispensabile la migrazione dei processi sono i seguenti: • distribuzione del carico di lavoro: spostare processi da sistemi

fortemente carichi a quelli meno carichi; • performance delle comunicazioni: processi che interagiscono

intensamente possono essere eseguiti sullo stesso nodo, minimizzando i costi di comunicazione. Inoltre, è meglio spostare i processi dove risiedono i dati di cui necessitano, là dove sia rilevante la loro dimensione.

• Disponibilità; • uso di particolari risorse: i processi possono essere avantaggiati

se eseguiti su macchine aventi risorse particolari.

La migrazione può essere proposta dal sistema operativo, quando l’obiettivo è il load balancing, oppure dal processo stesso se ha bisogno di particolari risorse.

5.1 Le politiche di migrazione Le strategie adottate per la migrazione sono essenzialmente cinque: • eager (all): trasferisce l’intero spazio degli indirizzi. Questa

strategia può essere dispendiosa se lo spazio degli indirizzi è grande ed il processo non ne ha interamente bisogno.

• Precopy: mentre lo spazio degli indirizzi viene copiato, il processo continua la sua esecuzione sul nodo sorgente.

Page 65: Introduzione ai Sistemi Operativi - camillobella.it

65

Le eventuali pagine modificate sul nodo sorgente, durante il precopy, devono essere ritrasferite. Di fatto, questa filosofia riduce il tempo in cui un processo non può essere eseguito perché soggetto a migrazione.

• Eager (dirty): il trasferimento riguarda solo la porzione dello spazio degli indirizzi che è in memoria centrale, i restanti blocchi della memoria virtuale vengono trasferiti su richiesta. Questa strategia coinvolge la macchina sorgente per l’intera vita del processo. Ottima per i trasferimenti temporanei.

• Copy-on-reference: si trasferiscono solo i riferimenti delle pagine; è una variante di eager(dirty).

• Flushing: alcune pagine (quelle dirty) vengono spostate dalla memoria principale al disco. Di seguito viene adottata la strategia copy-on-reference. Grazie a questa soluzione è possibile lasciare la memoria libera per altri processi.

Qualora un processo avesse uno spazio degli indirizzi piccolo nella macchina sorgente, si potrà adottare una politica eager (dirty), copy-on-reference, piuttosto che flushing, altrimenti eager (all) o precopy.

5.2 Negoziazione della migrazione

Le regole di migrazione sono responsabilità della starter utility. Ciascuna macchina ha una starter utility che controlla la migrazione dei processi e che è anche responsabile dello scheduling a lungo termine e dell’allocazione di memoria. Una decisione relativa ad una migrazione deve essere presa congiuntamente da due starter utility, ovvero da quella del nodo sorgente e da quella di destinazione. I passi di negoziazione sono i seguenti:

1. la starter utility del nodo sorgente che vuole far migrare un processo P ad un sistema destinazione, invia un messaggio alla starter utility del nodo di destinazione.

2. La starter utility del nodo destinazione dà una risposta positiva, se è pronta.

3. La starter utility del nodo sorgente invia un messaggio di migrazione al suo kernel job (KJ) che converte i messaggi tra i processi in chiamate di sistema.

4. Il kernel della macchina sorgente si offre per inviare il processo P alla destinazione.

Page 66: Introduzione ai Sistemi Operativi - camillobella.it

66

5. Il kernel destinatario consegna l’offerta alla starter utility che può rigettarla se si trova a corto di risorse.

6. La starter utility destinataria invia la MIGRATE_IN. 7. La macchina destinataria accetta l’offerta, allocando le risorse

opportune per il processo migrante. Dopo la migrazione, è importante che la macchina sorgente notifichi ai processi comunicanti con P(migrato), circa l’avvenuta migrazione.

Page 67: Introduzione ai Sistemi Operativi - camillobella.it

67

CAPITOLO VI Problemi

Quali sono i principali obiettivi degli algoritmi di allocazione dei processori?

Come vengono classificati gli algoritmi di allocazione dei processori?

Quali sono i vantaggi dell’algoritmo gerarchico rispetto a quello centralizzato?

Come funziona l’algoritmo bidding?

Che cosa si intende per migrazione dei processi?

Illustrare le politiche di migrazione dei processi.

Quali sono i passi di negoziazione della migrazione dei processi?

Page 68: Introduzione ai Sistemi Operativi - camillobella.it

68

apitolo VII

Scheduling del Processore

SOMMARIO: 1. DEFINIZIONI 2. CRITERI DI SCHEDULING 3. TIPOLOGIE DI SCHEDULING 4. ALGORITMI DI SCHEDULING

4.1. ALGORITMI DI SCHEDULING NEI SISTEMI BATCH 4.1.1. FIRST-COME FIRST-SERVED 4.1.2. SHORTEST JOB FIRST 4.1.3. SHORTEST REMAINING TIME NEXT

4.2. ALGORITMI DI SCHEDULING NEI SISTEMI INTERATTIVI 4.2.1. SCHEDULAZIONE ROUND ROBIN 4.2.2. SCHEDULAZIONE CON PRIORITÀ 4.2.3. CODE MULTIPLE 4.2.4. SHORTEST PROCESS NEXT 4.2.5. SCHEDULAZIONE GARANTITA 4.2.6. SCHEDULAZIONE A LOTTERIA 4.2.7. SCHEDULAZIONE FAIR-SHARE

4.3. ALGORITMI DI SCHEDULING NEI SISTEMI REAL TIME.

Page 69: Introduzione ai Sistemi Operativi - camillobella.it

69

1 Definizioni

Nelle architettture monoprocessore è necessario scegliere il prossimo processo da eseguire, al fine di realizzare la multiprogrammazione. La parte di sistema operativo che si occupa di gestire questo aspetto è detto schedulatore, mentre l’algoritmo usato è chiamato algoritmo di schedulazione.

Le decisioni di scheduling devono essere prese quando un processo: 1) passa dallo stato di running a waiting; 2) passa dallo stato di running a ready; 3) passa dallo stato di waiting a ready; 4) termina.

Lo scheduling per i punti (1) e (2) avviene senza prerilascio, mentre per i restanti c’è il prerilascio.

Il modulo dispatcher dà il controllo della CPU al processo che lo schedulatore a breve termine ha selezionato e ciò include:

1) cambiamento di contesto; 2) passaggio a modalità utente; 3) ripresa del programma dal punto in cui era stato interrotto. La latenza del dispatch è il tempo che occorre al dispatcher per

interrompere un processo ed avviarne l’esecuzione di un altro.

2 Criteri di scheduling I criteri relativi allo scheduling sono i seguenti: • utilizzo della CPU: tenere la CPU quanto più possibile

occupata; • throughput: numero di processi che completano la loro

esecuzione per unità di tempo; • turnaround time: tempo che un processo passa in attesa in coda

ready più tempo di esecuzione della CPU; • tempo di attesa: quantità di tempo di attesa di un processo

nella coda dei processi ready, prima di essere eseguito; • tempo di risposta: tempo trascorso fra l’invio di un comando e

l’arrivo del risultato.

Page 70: Introduzione ai Sistemi Operativi - camillobella.it

70

Ottimizzare lo scheduling significa cercare di massimizzare l’uso della CPU ed il throughput, minimizzando allo stesso tempo il turnaround time, tempo di attesa e tempo di risposta.

3 Tipologie di scheduling

Le principali tipologie di scheduling sono quattro: 1) scheduling a lungo termine: determina quali programmi sono

ammessi al sistema per il processamento, controllando il grado di multiprogrammazione. Più processi sono ammessi e minore sarà il tempo di CPU a loro dedicato.

2) Scheduling a medio termine: parte della funzione di swapping, basato sulla necessità di controllare il grado di multiprogrammazione.

3) Scheduling a breve termine: conosciuto come dispatcher, interviene al verificarsi di uno dei seguenti eventi: • interrupt di clock; • I/O interrupt; • chiamate del sistema operativo; • segnali.

I criteri adottati dal dispatcher riguardano il tempo di risposta (orientato all’utente), l’efficace ed efficiente uso del processore (orientato al sistema), la performance (quantitativa e misurabile come tempi di risposta e throughput) ed aspetti qualitativi.

4) Scheduling I/O: quale I/O device a quale rischiesta pendente di processo.

4 Algoritmi di scheduling Una prima distinzione fra gli algoritmi di scheduling è relativa alla

tipologia di sistema in cui essi sono applicati. Analizziamo tre tipi di sistemi, cioè i batch, gli interattivi ed i real time.

Page 71: Introduzione ai Sistemi Operativi - camillobella.it

71

4.1 Algoritmi di scheduling nei sistemi batch Con il termine batch si fa riferimento alla politica di raccogliere i

lavori da eseguire in gruppi (“lotti“), in modo tale da consentire una più conveniente ed efficiente elaborazione.

Nei sistemi operativi batch, l’obiettivo principale è quello di ottenere un throughtput elevato, cioè massimizzare il numero di job completati in un intervallo di tempo, e di tenere basso il tempo di turnaround, ovvero minimizzare il tempo di permanenza di un job nel sistema. Nelle prossime sezioni saranno presentati gli algoritmi di schedulazione più significativi adottati dai sistemi operativi batch.

4.1.1 First-come First-served

Il primo processo arrivato è il primo ad essere servito. Quando il

processo servito si blocca, la CPU passa al processo successivo in coda, il prerilascio non è contemplato. La grande forza di questo algoritmo è la sua semplicità ed è anche equo, purtroppo però è poco efficiente poiché favorisce i processi CPU-bound7, rendendo l’esecuzione dei processi I/O-bound8, anche se “sulla carta” brevissimi, molto lunga.

A tal proposito, si considerino, ad esempio, un processo CPU-bound che viene eseguito per 1 secondo alla volta e tanti processi I/O-bound, ognuno dei quali deve eseguire 100 letture da disco per terminare. Applicando l’algoritmo FCFS, ogni processo I/O-bound impiegherà 100 secondi per finire (leggendo un blocco al secondo), rallentando in maniera evidente l’esecuzione del processo CPU-bound. Considerato il chart di esecuzione in Figura 7.1, con due job I/O-bound (J1 e J2) ed uno CPU-bound (J3), secondo le condizioni sù descritte, si otterranno i seguenti valori di turnaround e waiting time:

Figura 7.1: Chart di esecuzione con scheduling FCFS.

J2 J3 J1

0 100 200 201

7 Lunghi periodi di elaborazione fra due richieste successive di I/O. 8 Brevi periodi di elaborazione fra due richieste successive di I/O.

Page 72: Introduzione ai Sistemi Operativi - camillobella.it

72

Turnaround-time: (100+200+201)/3 = 702. Waiting-time: (0+100+200)/3 = 100.

4.1.2 Shortest job first

Anche questo algoritmo è senza prerilascio ed ha come presupposto la conoscenza anticipata dei tempi di esecuzione dei processi. L’idea è di eseguire prima il processo che ha durata più breve e poi via via quelli di durata superiore, minimizzando il tempo medio di turnaround. Lo shortest job first funziona bene solo se i job sono tutti presenti contemporaneamente. Per rendere più chiaro il problema della non ottimalità, si consideri, ad esempio, la Figura 7.2, in cui troviamo cinque job, cioè {A, B, C, D, E} con tempi di esecuzione {2,4,1,1,1} e di arrivo {0,0,3,3,3}. All’inizio possono essere eseguiti A e B, dal momento che sono i soli job presenti. Usando la politica SJF i job verranno mandati in esecuzione nell’ordine A,B,C,D,E con un tempo medio di attesa non ottimo pari a 4.6 anziché 4.4(ottimo) ottenibile eseguendo nell’ordine B,C,D,E,A.

Figura 7.2: Esempio di schedulazione shortest job first.

2 4 1 1 1

A B C D E

4.1.3 Shortest remaining time next Una versione con prerilascio dello shortest job first è lo shortest

remaining time next, secondo cui viene eseguito per primo il job avente tempo di esecuzione rimanente più breve. Nel caso in cui un job p fosse in esecuzione ed un nuovo job q arrivasse, avente tempo di esecuzione rimanente inferiore, p verrebbe sospeso ed iniziato il nuovo job. Anche in questo caso il tempo di esecuzione di ciascun job deve essere noto in anticipo. L’algoritmo non è equo e potrebbero sorgere problemi per la contesa delle risorse.

Page 73: Introduzione ai Sistemi Operativi - camillobella.it

73

4.2 Algoritmi di scheduling nei sistemi interattivi I sistemi operativi interattivi consentono l'uso di programmi che

interagiscono con l'utente. Questo tipo di funzionamento si contrappone a quello in cui è necessario predisporre prima tutti i dati necessari e solo alla fine dell'elaborazione si ottiene un risultato, cioè i sistemi batch.

4.2.1 Schedulazione round-robin

Il round-robin è uno degli algoritmi più tradizionali, imparziali ed usati per realizzare la schedulazione. Ad ogni processo viene assegnato un intervallo di tempo, chiamato quanto, durante il quale esso può essere in esecuzione. Se alla fine del quanto il processo è ancora in esecuzione, la CPU viene rilasciata ed assegnata ad un nuovo processo; se il processo si è bloccato o terminato prima dello scadere del quanto, la CPU è comunque assegnata ad un altro processo (i processi sono inseriti in una lista). Assegnare un quanto di tempo troppo breve provoca molti cambi di contesto e peggiora l’efficienza della CPU, ma assegnarlo troppo lungo provoca tempi di risposta decisamente lunghi.

Fra i molteplici vantaggi di questo algoritmo annoveriamo la semplicità di implementazione, poiché lo schedulatore ha bisogno soltanto di una lista di processi da eseguire, che viene aggiornata allo scadere di ogni quanto, come riportato in Figura 7.3.

Figura 7.3: Esempio di schedulazione round robin.

P1 P2 P3 P4 P5

Processo corrente

P2 P3 P4 P5 P1

Processo corrente

Page 74: Introduzione ai Sistemi Operativi - camillobella.it

74

4.2.2 Schedulazione con priorità

La schedulazione round-robin si fonda sull’ipotesi che i processi

abbiano tutti la stessa importanza, ma nella realtà spesso capita di dover associare alcuni gradi di priorità ai processi. In base a questo algoritmo di schedulazione vengono eseguiti i processi a più alta priorità. Per evitare che essi monopolizzino la CPU si può pensare di scalare il grado di priorità ad ogni interruzione del processore o assegnare un tempo massimo di esecuzione al termine del quale viene eseguito il processo successivo a priorità più alta.

Spesso è conveniente suddividere i processi in classi di priorità ed usare la schedulazione a priorità fra le classi, con schedulazione round-robin all’interno di ogni classe. Bisogna tenere presente che se le priorità non vengono aggiornate di tanto in tanto, le classi a bassa priorità non verranno mai scelte (starvation).

4.2.3 Code multiple

Le code multiple organizzano i processi in più code di livello diverso, come in Figura 7.4. I processi della classe più alta vengono eseguiti per un certo periodo di tempo, quelli di classe più basse per periodi maggiori. Ogni qualvolta un processo rimane in esecuzione per tutto il tempo concessogli, scende di livello. In questo modo è possibile gestire al meglio i processi CPU-bound, che occuperanno i livelli più bassi, e quelli I/O-bound che si collocheranno nelle classi più alte. Sono state proposte varie implementazioni di questo algoritmo (Berkeley, CTSS, etc.) ma il concetto di base rimane il medesimo.

Figura 7.4: Schedulazione a code multiple con due classi di priorità .

Priorità 1

Priorità 2

Page 75: Introduzione ai Sistemi Operativi - camillobella.it

75

4.2.4 Shortest process next

Dal momento che lo shortest job first produce sempre il minimo tempo di risposta nei sistemi batch, sarebbe interessante usarlo nei sistemi interattivi.

In effetti è possibile considerare l’esecuzione di ciascun comando come un job a sé stante ma non è possibile conoscere anticipatamente la durata di un processo. A questo scopo vengono realizzate delle stime basate sulla storia del processo, mandando in esecuzione il processo che è stimato più breve.

4.2.5 Schedulazione garantita

Un approccio completamente diverso è quello di fare delle promesse all’utente, relative alle prestazioni. Una promessa relativamente facile da mantenere è la seguente: se ci sono n utenti e/o processi in esecuzione, ognuno di essi riceverà 1/n cicli di CPU. Per mantenere la promessa bisogna memorizzare il tempo di CPU assegnato ad ogni processo dalla sua creazione, e quindi calcolare la quantità di CPU a cui ciascuno ha diritto, dividendo il tempo trascorso dalla creazione per n.

4.2.6 Schedulazione a lotteria

Sebbene fare promesse agli utenti e mantenerle è una buona idea, è difficile implementarla. L’algoritmo di schedulazione a lotteria è molto più semplice della schedulazione garantita e fornisce risultati prevedibili e simili a quest’ultima.

L’idea è quella di fornire dei biglietti della lotteria ai processi. Nel momento in cui deve essere presa una decisione di schedulazione, viene estratto un biglietto della lotteria ed il processo che lo possiede ottiene la risorsa. Ai processi più importanti è possibile assegnare più biglietti ed è anche possibile che i processi scambino fra loro i biglietti per collaborare.

4.2.7 Schedulazione fair-share

Finora abbiamo supposto che ogni processo sia schedulato per se stesso, senza considerare a chi esso appartenga; come risultato, se

Page 76: Introduzione ai Sistemi Operativi - camillobella.it

76

l’utente A lancia nove processi e l’utente B solo uno, l’utente A avrà il 90% della CPU.

Per evitare questa situazione bisogna tenere in considerazione a chi i processi appartengano prima di schedularli: in questo modello ad ogni utente è assegnata una frazione della CPU e lo schedulatore sceglierà i processi rispettando tale assegnamento.

4.3 Algoritmi di scheduling nei sistemi real-time I sistemi real time sono normalmente classificati in hard real time,

cioè sistemi che hanno scadenze incondizionate da rispettare e soft real time, per cui una scadenza non è opportuna, ma comunque tollerata. In entrambi i casi il comportamento real time viene raggiunto dividendo il programma in processi, il cui comportamento è prevedibile e noto in anticipo. Tali processi hanno vita breve e possono essere eseguiti completamente in breve tempo. Quando viene rilevato un evento esterno è compito dello schedulatore schedulare i processi in modo da rispettare tutte le scadenze. Un sistema può dover reagire ad una serie di eventi e perfino non essere in grado di gestirli tutti (sistema non schedulabile).

Gli algoritmi di schedulazione possono essere statici o dinamici: i primi prendono le decisioni di schedulazione prima dell’esecuzione, con il vincolo di disporre di tutte le informazioni necessarie in anticipo, mentre i secondi a runtime.

Page 77: Introduzione ai Sistemi Operativi - camillobella.it

77

Capitolo VII

Problemi

Dare una definizione di schedulatore e di algoritmo di scheduling.

Quali sono i principali criteri di scheduling?

Quali sono le principali tipologie di scheduling?

Illustrare l’algoritmo di scheduling First-come First-served.

Dibattere riguardo a differenze ed analogie fra sistemi batch e sistemi interattivi.

La schedulazione con priorità può sostituire il round robin? In quali casi? Quali sono i possibili problemi a cui si va incontro?

Descrivere l’algoritmo di schedulazione a lotteria.

Quali sono le esigenze che un algoritmo di schedulazione deve soddisfare in un sistema real-time?

La schedulazione shortest job first è soggetta a starvation?

In quali casi è opportuno adottare la schedulazione a code multiple?

Page 78: Introduzione ai Sistemi Operativi - camillobella.it

78

Bibliografia

A. Tanenbaum[2002] I Moderni Sistemi Operativi (II edizione). Jackson, 2002.

Batini, Pernici, Santucci[2001] Sistemi Informativi. Volume V. Franco Angeli, 2001

Birrell, Nelson[1984] Implementing Remote Procedure Calls. ACM Transactions on Computer Systems, febbraio 1984.

B. Hansen[1970] The nucleus of a multiprogramming system. Communications of the ACM 13, 4 (April), 238–250. Article 2.

Dijkstra[1965] Cooperating Sequential Processes. Technological University, Eindhoven, The Netherlands, 1965.

Linux Manual[1999] Linux Programmer’s Manual. http://unixhelp.ed.ac.uk/CGI/man-cgi?ptrace+2, 1999.

W. Stallings[2002] Operating Systems, 4th edition. Prentice Hall, 2002.