Swappa : Uni / Sistemi Operativi - Comunicazione tra processi
Creative Commons License

Torna alla pagina di Sistemi Operativi


 :: Appunti 2.0 ::

Comunicazione tra processi

Processi cooperanti

Un processo che non condivide dati con altri processi è detto indipendente, e la sua evoluzione non può influenzare o essere influenzata da alcuno. Da notare che ciò non implica che non abbiano risorse condivise (sarebbe impensabile, dal momento che almeno il processore deve essere accessibile a tutti), l'importante è che nessuno acceda alle informazioni dell'altro. I processi cooperanti sono invece l'esatto contrario e, pur svolgendo ognuno un compito ben definito, le loro computazioni concorrono all'adempimento di uno scopo applicativo congiunto. Stessa distinzione può essere operata sui thread.

Alcuni dei vantaggi della cooperazione sono:

Perché tutto ciò sia possibile occorre che tali processi possano scambiarsi informazioni e coordinarsi, o meglio, che possano comunicare e sincronizzarsi. I processi indipendenti si limitano invece alla sola sincronizzazione, per regolamentare l'accesso alle risorse condivise.

Comunicazione

Il processo di comunicazione comporta la descrizione di politiche e meccanismi che permettano ai processi di scambiarsi informazioni per operare in modo cooperativo. Anzitutto vanno definite le entità coinvolte, quindi il processo mittente (P) produttore dell'informazione, il processo ricevente (Q) utilizzatore della stessa, e il canale di comunicazione (mono o bidirezionale) attraverso cui farla fluire. Per decidere poi quale tipo di comunicazione è meglio adottare in una specifica situazione, bisognerà tener conto di alcune caratteristiche. Di seguito riportiamo le più importanti, alcune delle quali dovrebbero sempre essere garantite:

Prima di elencarne le principali implementazioni, distinguiamo le due modalità di realizzazione:

Implementazioni

Con memoria condivisa

E' una modalità di comunicazione diretta e può essere realizzata attraverso l'uso di variabili globali o buffer.

Nella condivisione di variabili globali ho due processi con una porzione del loro spazio di indirizzamento (le variabili globali, appunto) che si sovrappone. Pur essendo un sistema molto rapido, avere porzioni di memoria in comune implica dei problemi, come la possibilità di compiere operazioni inconsistenti. Se ad esempio un processo richiede la lettura di un'informazione in corso di modifica dall'altro processo, leggerebbe qualcosa di obsoleto e dunque errato. La situazione è però facilmente risolvibile attuando meccanismi di sincronizzazione per l'accesso in mutua esclusione all'area dati comune per le operazioni non compatibili (come ad esempio lettura e scrittura contemporanea). Inoltre sono in qualche modo tutelato dal fatto che essendo i due processi cooperanti non hanno alcun interesse a danneggiare l'altro, dal momento che indirettamente danneggerebbero sé stessi. Ho due meccanismi per realizzarla.
Il primo è fare in modo che l' area comune sia copiata dal sistema operativo, che crea l'illusione della condivisione spostando l'informazione in due tempi: 1. la copia dall'area condivisa del processo mittente al proprio spazio di indirizzamento (in una porzione di memoria grande abbastanza) 2. da qui la copia nell'area condivisa del ricevente. Ricordiamo che il sistema operativo può penetrare ovunque, quindi non ha alcun problema ad accedere agli spazi di indirizzamento riservati dei processi. Il limite di questo meccanismo è proprio che l'operazione di copiatura si fa in due passaggi, il che su grandi porzioni di memoria rallenterebbe sensibilmente il sistema; soprattutto se si considera il fatto che anche se un processo modifica solo una minima parte dell'area condivisa, il sistema operativo non ne conosce l'entità e quindi trasferisce tutto anche se non sarebbe necessario.
Un secondo meccanismo è la realizzazione con area comune fisicamente condivisa, in cui il sistema operativo garantisce che l'area comune appartenga contemporaneamente agli spazi di indirizzamento di entrambi i processi. Lo spazio rimane comunque logicamente separato e garantito protetto dal sistema operativo, anche se alcune porzioni sono residenti fisicamente negli stessi indirizzi. E' piuttosto semplice da realizzare e supera tutti i limiti della soluzione precedente, bisogna solo fare in modo che non avvengano operazioni inconsistenti (basta utilizzare politiche di sincronizzazione).

Nella condivisione di buffer ciò che cambia rispetto a prima è l'utilizzo di un buffer. La comunicazione rimane diretta, ma il processo mittente scriverà stavolta le sue informazioni all'interno del buffer dal quale poi il processo ricevente andrà a recuperarle. Anche in questo caso con la sincronizzazione è possibile imporre gli accessi in mutua esclusione così da garantire la consistenza.
I meccanismi con cui realizzarli sono del tutto simili a quelli precedenti, ovvero buffer con copiatura gestita dal sistema operativo e buffer in memoria fisicamente condivisa.
Notare infine come i buffer siano significativamente più piccoli delle porzioni di memoria centrale condivisa, quindi le operazioni di copiatura sono molto più veloci.

Con scambio di messaggi

La comunicazione con scambio di messaggi è diretta e prevede che l'informazione viaggi incapsulata all'interno di messaggi. Oltre ad essa, i messaggi contengono l'identità del processo mittente e ricevente, ed eventuali altre informazioni relative alla gestione dello scambio.
I messaggi vengono memorizzati in buffer che il sistema operativo può assegnare esplicitamente ad ogni coppia di processi, o predisporne un certo numero di uso generale che mette a disposizione di chiunque ne abbia bisogno. Tale quantità può essere:

Le funzioni messe a disposizione per la gestione dei messaggi sono quelle per l' invio e la ricezione, eventualmente condizionali:

La comunicazione tramite buffer in generale è asincrona, ovvero il mittente può spedire il messaggio in qualsiasi momento della computazione senza preoccuparsi se c'è qualche ricevente in grado di raccoglierlo. In una comunicazione sincrona lo scambio delle informazioni avviene invece solo quando entrambi gli interlocutori sono pronti. Si può ottenere utilizzando un buffer di dimensione nulla, così che tutte le operazioni di scrittura diventino bloccanti, obbligando di fatto i processi a scrivere e leggere nello stesso momento. E' molto interessante poi osservare che tale politica può essere facilmente adottata per modalità di comunicazione diretta o indiretta. Nel primo caso si parla di comunicazione simmetrica e prevede ovviamente che mittente e destinatario siano sempre univocamente identificati. Nel secondo caso otteniamo invece una comunicazione asimmetrica, in cui l'accesso ad un buffer in scrittura non è limitato ad un processo solo, ma ad un gruppo di processi che possono a loro volta decidere di far leggere il loro messaggio a tutti quelli che ascoltano o solo ad uno di essi.

Dal momento che la sincronizzazione per l'accesso ai messaggi viene gestita implicitamente dal sistema operativo, i buffer vengono implementati direttamente all'interno del suo spazio di indirizzamento. In questo modo viene risolto anche il problema dell'identificazione dei processi che inviano informazione, dato che è sufficiente vedere qual è il processo attivo (se ha effettuato una richiesta è evidentemente nello stato di running). I messaggi vengono normalmente smaltiti in modalità FIFO, ma potrebbero essere previste politiche di altro tipo (ad esempio con priorità, deadline, ecc).

Comunicazione con mailbox

La comunicazione con mailbox è un sistema indiretto, in cui non c'è conoscenza esplicita tra i processi che comunicano. Se nella comunicazione asimmetrica vista in precedenza non era necessaria la conoscenza esplicita dei processi, ma era sufficiente l'identificativo dei gruppi, questo sistema è invece completamente anonimo. Il messaggio viene depositato in una mailbox (detta anche porta), una struttura dati presente nel sistema operativo caratterizzata da un nome cui si farà riferimento per accedere ai messaggi in essa contenuta. Questi contengono, oltre all'informazione da trasmettere, anche l'identità del processo mittente, il nome della mailbox destinataria ed eventuali altre informazioni di supporto alla gestione dei messaggi nella mailbox (ad esempio se vanno tolti messaggi con deadline scaduta o come ordinare i messaggi). Va ricordato che non stiamo assegnando i messaggi ad una coppia di processi, ma li stiamo inserendo in una struttura accessibile (teoricamente) da tutti; eventuali restrizioni potranno essere applicate con delle politiche di accesso stabilite dal sistema operativo.
In generale la mailbox non è proprietà del processo, ma del sistema operativo (ricordiamo che è nel suo spazio di indirizzamento). Esistono però delle procedure per attribuirle ad un processo proprietario, al quale il sistema può dare il diritto esclusivo di ricezione. In questo caso, quando il processo proprietario termina, la mailbox viene deallocata con esso.

Similmente al buffer, la mailbox può avere una capienza limitata illimitata o nulla, indipendentemente dal numero di processi che la utilizzano e con le stesse conseguenze viste prima. Anche le funzioni sono simili:

e in più abbiamo

Come per i buffer, le politiche di sincronizzazione delle mailbox dipendono dalla loro capacità. Se è illimitata ho una comunicazione asincrona, ovvero il mittente depone il suo messaggio indipendentemente dallo stato di computazione del processo ricevente. Se invece è nulla quella che ottengo è un comunicazione sincrona. Ho infatti un processo mittente che non trova spazio per deporre il suo messaggio, ed è dunque obbligato a mettersi in attesa. Quando un processo Q invocherà una receive(), lo troverà ancora in quello stato, e a questo punto i due processi cominceranno a passarsi l'informazione direttamente in modo sincrono. Questo meccanismo prende il nome di rendez-vou, ed implica che P sappia a che punto della computazione si trovi il processo ricevente quando richiede la comunicazione. Se infine la capacità è limitata, la comunicazione verrà bufferizzata, ovvero passerà un po' di tempo prima che il messaggio inviato venga ricevuto.
Per quanto riguarda l'ordinamento delle code dei messaggi all'interno delle mailbox, può essere applicato un qualsiasi algoritmo di schedulazione.

La comunicazione con mailbox è particolarmente adatta per i seguenti scenari:

Comunicazione con file

Distinguiamo due implementazioni, quella con file condivisi e quella mediante pipe.

La comunicazione mediante file condivisi rappresenta una diretta estensione della comunicazione con le mailbox: se quella faceva uso di strutture dati realizzate in memoria centrale condivisa, i file sono invece memorizzati in memoria di massa. La sua gestione sarà al solito demandata al sistema operativo, che garantirà che le operazioni di accesso siano fatte in modo corretto e solo dai processi autorizzati.

La comunicazione mediante pipe impiega invece l'utilizzo dei pipe come strutture di appoggio, una struttura dati di tipo FIFO residente in memoria centrale e che condivide coi file molte delle loro funzioni.

Utilizzo per entrambi le stesse funzioni viste per le mailbox, delle quali ereditano anche gli stessi problemi (e soluzioni) di sincronizzazione e ordinamento.

Comunicazione con socket

La comunicazione con socket è dal punto di vista concettuale una generalizzazione in rete delle pipe, anche se dal punto di vista realizzativo è ovviamente qualcosa di molto più complesso. In sostanza ho il sistema operativo che virtualizza la comunicazione tra macchine diverse utilizzando una pipe spezzata in due porzioni, ognuna residente sulla memoria centrale delle macchine coinvolte.
L'architettura che sta dietro a tale modalità di comunicazione è di tipo client-server, ovvero ho un client che inoltra una richiesta ad una porta specifica su cui un server è in ascolto per la loro evasione. L'identificazione del server avviene tramite la coppia di identificatori indirizzo:porta.

I messaggi possono avere dimensione fissa o variabile a seconda delle applicazioni che le utilizzano, e sono ordinati in modalità FIFO (così come i processi in attesa). I socket si possono creare o distruggere, e si può leggerci o scriverci sopra tramite due canali mono-direzionali. Ciò significa che nel momento in cui una connessione via socket viene accettata, è possibile sia per il server che per il client comunicare su due canali separati. Per evitare che i client trovino la porta di ascolto del server chiusa mentre sta servendo un altro processo, il client chiamante fa in modo di segnalare al server una nuova porta su cui spostare la comunicazione per la gestione della risposta.
Infine, la connessione può essere con gestione del sistema operativo o senza, in cui ogni messaggio viene trasmesso in modo individuale. Questa seconda modalità è ovviamente la più semplice da realizzare, dal momento che non viene fornita alcuna informazione su come il sistema dovrà trattare i messaggi. C'è poi un'ultima tecnica di connessione, che è il multicast.


Torna alla pagina di Sistemi Operativi

(Printable View of http://www.swappa.it/wiki/Uni/SO-Comunicazione)