:: Sistemi Operativi - Lezione del 19 Maggio 2008 ::
Torna alla pagina di Sistemi Operativi
Sistemi Distribuiti
Lezione 1 - Obiettivi & funzioni
Nei sistemi distribuiti, abbiamo diverse macchine connesse, con almeno 1 processore per sistema. La memoria è locale ad ogni sistema, e le periferiche possono essere:
Once upon a time, le reti erano composte da macchine e in generale hw tutto della stessa marca, a volte addirittura dello stesso modello.
Ai giorni nostri, invece, si tende a mettere insieme macchine eterogenee, perché le aziende si espandono e man mano comprano cose differenti. Per fortuna ci sono gli standard che ci permettono di far dialogare macchine e sistemi diversi.
Vediamo un po' di concetti:
Ecco invece i vantaggi di avere architetture distribuite:
Così come il SO dovrebbe astrarre dalla macchina sottostante, anche i SO per archi distr dovrebbere permettere di astrarre dalla rete sottostante, e mostrarne le grazie al processo come tutte allocate a lui.
Ma dobbiamo distinguere tra due gradi di virtualizzazione:
Network Operating System = la struttura della rete è visibile. Il processo non si occupa dei dettagli della comunicazione, ma comunque la rete non è trasparente.
Distributed Operating System = è tutto trasparente all'utente.
L'obiettivo di un NetSO è quello di permettere l'accesso alle risorse sulle varie macchine. Esse devono essere innanzitutto collegate, in qualche modo:
Come vedremo poi, RPC non è un metodo per la comunicazione tra processi.
Per trasferire i files tra una macchina e l'altra, posso usare il protocollo applicativo FTP oppure un fileserver remoto.
In questo modello, vogliamo che tutte le risorse siano accessibili come se fossero locali. A questo scopo, non basta montare il FS remoto: occorre che tutto, quindi compreso anche il FS locale, sia uguale per tutte le macchine della rete.
Questo deve potermi permettere di migrare i dati da una macchina all'altra, e di migrare processi da una macchina all'altra. Non si tratta di roba da poco, in effetti.
Posso ottenere questa virtualizzazione in diversi modi:
Comune ad entrambe le modalità sono
Il punto 2 mi dice, ad esempio, che una macchina little-endian dovrà lavorare in modo diverso sugli stessi dati, rispetto ad una macchina big-endian. Deve però pensarci il SO, e non l'utente, in modo trasparente (aggettivo quanto mai abusato...).
Ci sono due tipi di migrazione, per quanto riguarda la computazione: far migrare una procedura oppure un processo.
La migrazione di procedura consiste nell'RPC, o nell'RMI. Invio parametri ad una procedura, che viene eseguita su di un'altra macchina.
A che pro?
Per migrare il processo, devo essere in grado di congelare l'evo della comp di un processo, copiarlo su un'altra macchina e ripartire da là senza che il processo se ne accorga, oppure usare dei misteriosi - finora - agenti mobili, che sono progettati apposta per andare in giro per il sistema.
Anche qui, le motivazioni che inducono a migrare i processi sono le stesse della migrazione delle procedure, con quest'aggiunta:
Inoltre, il DistroSO deve avere anche un file server distribuito, al solito TRASPARENTE!!!
Possono accadere guasti o malfunzionamenti, ma lo show deve andare avanti come se nulla fosse. Ecco quindi che il sistema distribuito deve, in qualche modo:
Come si ottiene?
Potrei monitorare periodicamente le macchine, facendosi scambiare tra di loro dei protocolli di handshaking, così che si accorgano se un'altra sta soffrendo o no.
Posso implementare un timeout: se una macchina non risponde a certe sollecitazioni dopo un certo timeout, posso assumere che sia giù.
Posso, ancora, duplicare la computazione: ad esempio, faccio eseguire lo stesso compito a due macchine diverse, e confronto i risultati. Se qualcosa non coincide, è sintomo di qualche malfunzionamento. Infatti, è ben poco probabile che due macchine diverse presentino lo stesso problema, che conduca allo stesso tipo di guasto.
Gli errori non devono essere visibili, perché tutto deve procedere come se nulla fosse. Come faccio?
Posso replicare la computazione un numero dispari di volte. Basandomi sulla statistica presentata qui sopra, è improbabile che tutte le macchine presentino lo stesso problema. Inoltre, essendo in dispari, scelgo il risultato che la maggioranza mi dà, e sono praticamente certo che il calcolo è esatto.
Posso duplicare le risorse: raddoppio tutto, così ho abbastanza ridondanza da poter mascherare ogni cosa.
Se cade un collegamento, o va giù una macchina, devo semplicemente togliere dalle tabelle di routing quella destinazione e/o percorso. Poi ci penserà qualcuno a rimettere in sesto il tutto.
Dopo aver riparato il guasto, le tabelle di instradamento vanno ripristinate, magari con l'handshaking di cui parlavo prima si informano le altre macchine del ritorno in vita, e si reinvia la posta arretrata (fondamentale...).
Quando si progetta un SO distribuito, occorre tenere a mente le seguenti cose:
Quindi, occorre scrivere il tutto in MIXAL.
Lezione 1 (ancora?) - Gestione della comunicazione in rete
OCCHIO: tutto ciò che segue è roba damianesca. Inoltrarsi a proprio rischio e pericolo nella lettura. Eh, i bei tempi di quando i protocolli di rauting variavano di giorno in giorno...
Devo dare nomi univoci alle risorse (informative o fisiche), e ai processi, così che li so pescare in tutta la rete.
Devo quindi
In astratto, è così:
<nome host, identificatore>
Per quanto riguarda il nome host, posso usare gli indirizzi IP numerici, oppure i nomi logici ad essi associati.
La risoluzione dei nomi è quella branca della scienza che si occupa di collegare un nome ad un indirizzo. Le alternative, in questo campo, sono fondamentalmente 2:
I DNS partono dal concetto di dominio, che è una suddivisione arbitraria della rete. Il protocollo DNS è distribuito: ogni server conosce i dettagli del proprio dominio, e sa solo a grandi linee dove andare a chiedere per il resto.
Sono organizzati in modo gerarchico: ci sono server sempre più dettagliati a partire dai dominî più generali. Ad esempio, www.swappa.it si raggiunge prima attraverso il DNS che gestisce tutti i .it, e poi tramite il DNS che gestisce swappa.
Per ottimizzare le prestazioni, i server DNS mantengono una cache delle informazioni richieste di recente, così che quando arriva una richiesta ad una stessa risorsa non devono chiedere ancora in rete. Ovviamente questa cache va tenuta aggiornata, e quindi rinfrescata ogni tanto.
Devo trovare un percorso da A a B. Se esiste un solo percorso, non ho scelta: uso quello. Se ne ho diversi, invece, devo scegliere quello che rende di più.
Ci sono 3 tipi di instradamento: statico, virtuale e dinamico.
Il percorso A-B è definito a priori, e non lo cambio più.
Il percorso A-B è scelto per ogni sessione. Per tutta la sessione di comunicazione, si usa quel percorso. È possibile che alla sessione successiva il percorso sia diverso.
Il percorso A-B è scelto per ogni messaggio, e si adatta a qualsiasi tipo di danno e variazione di carico.
Nei primi due tipi di instradamento, statico e virtuale, i messaggi vengono inviati in un certo ordine, e ricevuti nello stesso ordine.
Con l'instradamento dinamico, invece, no, perché è possibile che un messaggio spedito dopo abbia scelto una strada che lo fa arrivare prima a destinazione, rispetto alle strade scelte dai messaggi precedenti.
Ecco quindi che occorre gestire la faccenda di ricostruire i messaggi nell'ordine in cui sono stati spediti originariamente, anche se arrivano in ordine sparso.
Serve per mettere in comunicazione 2 reti, anche se usano un protocollo diverso.
Per andare da host a gateway, in genere si usa l'instradamento statico. Infatti, il gateway difficilmente cambierà indirizzo in una rete. La dinamicità qui è inutile.
Al contrario, da gateway a host serve instradamento dinamico, perché se la rete è grossa è impensabile che il gateway conosca tutte le macchine della rete.
Il rùter, a differenza del rauter di damianesca memoria, gestisce l'instradamento, ed è quindi più complesso di un gateway.
Finora abbiamo parlato della strada che i messaggi devono prendere per arrivare a destinazione. Ma il messaggio può avere lunghezza variabile. E più è lungo, più è probabile che un'interferenza o un disturbo lo alterino.
La soluzione è dividerlo in pacchetti di dimensione fissa. Questi pacchetti in realtà hanno nomi diversi a seconda del protocollo che sto usando:
I pacchetti vengono inviati in ordine, e poi vanno riassemblati nel giusto ordine anche se arrivano sparsi. Devo poi saper gestire anche i reinvii, nel caso che un pacchetto arrivi errato o non arrivi del tutto. Come dicevamo prima, se il pacchetto è piccolo è meno probabile che sia soggetto a difetti di trasmissione.
L'obiettivo è far parlare 2 processi che stanno a distanza. Come diavolo faccio? Devo creare un canale tra di essi, e ho 3 strategie diverse:
I 2 processi definiscono un percorso fisico tra di essi, e questo percorso rimane bloccato per tutta la sessione, e nessun altro lo può usare.
Le caratteristiche sono:
E' quello che succede con la linea telefonica: si crea un circuito fisico tra la mia cornetta e quella dell'ascoltatore.
Il collegamento tra i 2 processi rimane in vigore per tutta la durata dell'invio del messaggio. Il canale viene quindi creato dinamicamente, e c'è però un po' più overhead di gestione, perché per ogni messaggio devo creare il canale.
Ogni pacchetto segue una sua connessione. Qui la faccenda si fa complicata, perché è possibile che i pacchetti, come dicevamo prima, arrivino in ordine sparso e sia necessario ricomporli. Però uso al meglio la banda.
Che conflitti?
I conflitti si hanno quando più macchine vogliono usare lo stesso canale di comunicazione.
E' in sostanza il cavo di rete.
Se ricordate dal Damiani, le collisioni sono autorilevate tramite il controllo: se quello che ricevo è uguale a quello che invio, allora tutto ok, se no c'è stato un errore. Le varie periferiche sanno gestire autonomamente questi casi, tramite il reinvio dopo un tempo più o meno casuale dei messaggi.
Per evitare troppe collisioni, devo porre un limite al numero di nodi.
C'è un token che gira per le macchine: quando una macchina riceve il token, manda i suoi messaggi, poi passa il token ad altri.
Occorre avere strategie per recuperare nel caso in cui i token si perdano o si rovini. In genere si usa un timeout: se entro tot tempo il token non riappare, viene dato per morto e si riparte.
Lezione 2 - Protocolli di comunicazione
I driver del SO devono, purtroppo per loro, occuparsi dell'implementazione dei protocolli di comunicazione. Ecco perché ci occorre sapere come sono fatti sti protocolli, per poterli implementare...
I problemi che devo affrontare sono questi:
E al solito, voglio un ambiente omogeneo e astratto rispetto a tutto sto casino. In altre parole, voglio realizzare una virtualizzazione della comunicazione: non devo vedere tutti i canali e i messaggi etc., voglio solo che i 2 processi vedano un canale virtuale, con i dettagli scabrosi nascosti.
Il modello astratto che si segue è quello ISO / OSI, composto da diversi strati:
ISO / OSI è una pila (scarica:)). I vari strati, a partire da quello più alto, aggiungono info per trasformare il messaggio in qualcosa di accettabile. Se lo vedo al contrario, invece, a partire dal basso i dati vengono incapsulati in strutture più grandi per renderli appetibili agli strati superiori.
Quindi, un messaggio parte dallo strato di applicazione, si inciccisce e divide etc. fino a diventare bit, e dall'altro lato fa la strada inversa, dimagrendo fino a tornare quello che era.
I modelli reali si discostano un po' da questo modello teorico, perché
Tanto per ricordare, il TCP realizza le connessioni, ed è affidabile (se c'è un errore, si cerca di rimediare). Invece UDP non realizza connessioni ed è inaffidabile.
I driver di rete sono quei software che nel mio SO realizzano questi protocolli di comunicazione in rete, siano essi ISO / OSI o altra roba.
Computazione distribuita
Lezione 1 - Distribuzione della computazione
Quello che voglio è sfruttare, finalmente, questo benedetto parallelismo tra macchine diverse. E poi, siccome ho i dati spantegati su varie macchine, sarebbe bello se il mio processo andasse in esecuzione proprio su quella macchina, così che i dati li ha subito disponibili.
In generale, dovrei poter spostare la computazione sulla macchina che ha le risorse adatte per quel tipo di processo. Risorse intese in astratto: CPU, memoria, dotazione HW e SW.
Per supportare la computazione distribuita, mi serve poter
i processi distribuiti.
Lezione 2 - Chiamata di procedura remota (RPC)
L'idea è di eseguire la procedura laddove essa può essere eseguita meglio. Il resto del processo rimane altrove.
In questo scenario, l'entità attiva è il processo chiamante, mentre quella passiva è la procedura chiamata, che "dorme" finché non viene risvegliata da qualcuno.
Il processo chiamante deve avere disponibile uno stub, cioè un abbozzo di come è fatta la procedura remota, di quali parametri vuole etc. etc. Ha quindi una rappresentazione della procedura.
Deve però poi essere il SO ad inoltrare la richiesta ad un demone che è in attesa sulla macchina remota. Il demone ascolta la richiesta del nostro processo, esegue quello che deve eseguire, e ritorna i risultati indietro per la rete.
Ogni procedura, per essere chiamata, deve avere il suddetto stub.
Il demone di cui sopra, quando riceve la richiesta di esecuzione di una procedura, deve innanzitutto controllare se questa procedura, effettivamente, c'è. Lo scambio di messaggio per la rete è gestito in automatico dal SO, il programmatore non deve preoccuparsene.
Da notare che la RPC non serve per trasmettere messaggi. Al contrario, sono i messaggi ad essere usati dalla RPC. La RPC non manda dati ad un processo remoto, al fine di mettersi in comunicazione con quel processo. No: manda solo i parametri che servono, e attende dei risultati. Per ottenere questo risultato, usa i messaggi, ma non viceversa.
Problema: le varie macchine possonon avere un modo diverso di rappresentare i dati. Pensiamo a little-endian, big-endian etc. Ecco allora che si è inventato lo standard XDR per trasmettere i dati in un modo univoco per la rete. Poi ci pensa la macchina locale a convertire da XDR nel formato locale.
Certo, se le due macchine in rete usano la stessa rappresentazione dei dati, convertira da e verso XDR è uno spreco di tempo. Però, non si sa mai chi ci sarà di là ad ascoltare, quindi meglio farlo sempre.
Un altro problema è: quante volte viene eseguita la mia procedura, remotamente? Ricordiamoci infatti che si passa attraverso una rete, che è inaffidabile, può non vedere pacchetti, può vederseli arrivare doppi ed altre stranezze.
Qui, dipende dalla politica di implementazione:
Un'applicazione delle RPC è il FS distribuito, in cui le chiamate per accedere ai files vengono inviate in modo trasparente (sigh) a un qualche demone RPC, che poi risponderà bla bla bla.
RMI = Remote Method Invocation, ed è una tecnologia Java. In sostanza, si estende il concetto di procedura remota alla tecnologia ad oggetti.
Un oggetto è una struttura contentente dati e metodi, ovvero procedure che lavorano su quei dati. Le RMI mi permettono di interagire con oggetti posti su macchine virtuali Java (JVM) remote.
Per JVM remota si intende una qualsiasi JVM che NON sia quella del mio processo. Posso eg avere 2 JVM sulla stessa macchina, ma l'una rispetto all'altra è sempre remota.
Il cliento ha lo stub, il server lo skeleton, e l'ambiente Java fa tutto lui in modo TRASPARENTE al programmatore.