• Non ci sono risultati.

Inter-Process Communication

N/A
N/A
Protected

Academic year: 2021

Condividi "Inter-Process Communication"

Copied!
8
0
0

Testo completo

(1)

Inter-Process Communication

C. Baroglio a.a. 2002-2003

1 Introduzione

In Unix i processi possono essere sincronizzati utilizzando strutture dati speciali, appartenti al pacchetto IPC (inter-process communication). Queste strutture consentono inoltre lo scambio di dati fra processi. IPC contiene tre tipi di strutture: semafori, code di messaggi e memoria condivisa. Lo schema di utilizzo delle tre ` e identico:

1. uno dei processi alloca la struttura di interesse;

2. tutti i processi che devono essere sincronizzati tramite la struttura, uti- lizzano una chiave comune, decisa a tempo di programmazione, per fare riferimento alla struttura allocata. Tale chiave ` e usata anche dal processo allocatore;

3. le strutture di IPC possono essere utilizzate solo per mezzo di system call predefinite;

4. una volta terminato il compito previsto, le strutture di IPC devono essere esplicitamente disallocate da uno dei processi in questione (non necessari- amente il processo che ha effettuato l’allocazione).

Per ciascuno dei tre tipi di strutture IPC sono definite una system call -get e una system call -ctl: la prima consente alternativamente di allocare un semaforo, una coda o un’area di memoria condivisa, oppure di ottenere l’identificatore di accesso a una struttura allocata da un altro processo. La seconda system call consente di eseguire operazioni diverse, specificate da nomi in codice, fra i quali la disallocazione della struttura condivisa (operazione: IPC RMID). Infine per ciascuna struttura sono previste system call specifiche che consentono, nel caso dei semafori di eseguire le operazioni di sincronizzazione up e down, nel caso delle code di messaggi di effettuare send e receive e, per la memoria condivisa, di agganciare l’area in questione allo spazio degli indirizzi di un processo.

Queste note contengono una descrizione ad alto livello delle system call viste a lezione ed integrano i lucidi. Per i dettagli si consulti il manuale on-line.

(2)

2 Chiavi ed identificatori interni

Una struttura di IPC ` e in un certo senso esterna a qualsiasi processo, anche al processo allocatore, ed ` e gestita dal sistema operativo. Il numero di strut- ture allocabili in un sistema ` e finito e ciascuna struttura ` e rappresentata da una struttura dati contenente tutte le informazioni che la riguardano. Queste strutture dati variano a seconda del tipo di oggetto IPC che consideriamo.

Per allocare una struttura di IPC vengono utilizzate le system call -get:

semget (semafori), msgget (code di messaggi) e shmget (aree di memoria condi- visa). In tutti e tre i casi il primo argomento ` e un numero intero detto chiave e il risultato restituito dalla system call ` e un altro numero intero detto identi- ficatore. In un certo senso anche la chiave ` e un identificatore della struttura in questione, il suo uso ` e per` o assai diverso da quello del valore restituito da una -get.

Siano prog1.c e prog2.c i codici sorgenti di due programmi che generano due processi P1 e P2 da sincronizzare. Il primo richiede l’allocazione di un semaforo, che sar` a utilizzato anche dal processo generato dal secondo programma. P1 eseguir` a una semget, richiedendo al sistema operativo di allocare un semaforo.

Ottiene come risultato un identificatore che gli consente di accedere alla specifica struttura semaforo allocata. P2 dovr` a accedere, affinch´ e le cose funzionino come desideriamo, alla stessa struttura semaforo allocata da P1. P2 per` o non pu` o sapere quale struttura ` e stata allocata dal sistema operativo e quindi quale identificatore ` e stato restituito a P1, a meno che P1 e P2 non abbiano definito un nome convenzionale con il quale identificano la struttura di IPC in questione;

tale nome convenzionale ` e la chiave. Per fare un’analogia, facciamo finta che invece di semafori si parli di numeri di telefono: un numero di telefono identifica (in senso lato) un utente. Tale utente avr` a per` o anche un nome: per telefonare a una persona posso chiedere a un servizio automatico di darmi il numero associato al nome che mi interessa. Ebbene, per tornare ai semafori, il nome ` e l’analogo della chiave, il numero di telefono lo ` e dell’identificatore restituito da semget e infine il servizio automatico ` e il sistema operativo. Non posso telefonare a una persona se conosco solo il suo nome (chiave), devo avere il suo numero (identificatore).

3 Allocazione e accesso

Ciascuna system call -get ha diversi usi e comportamenti che dipendono dai valori del primo e dell’ultimo suo argomento. Il primo argomento, lo abbiamo visto, ` e la chiave. L’ultimo ` e detto normalmente “flag” e specifica sia i diritti di accesso da associare alla struttura condivisa sia alcune opzioni relative alla creazione. Vediamo i casi principali.

Supponiamo che un processo voglia allocare una nuova struttura dati con-

divisa. Pu` o procedere in due modi. Il pi` u semplice ` e utilizzare la specifica

convenzionale IPC PRIVATE come chiave. IPC PRIVATE forza l’allocazione

di un nuovo oggetto IPC, se vi ` e ancora spazio disponibile, il cui identifica-

(3)

tore viene restituito al processo chiamante. Questa soluzione pu` o apparire in contrasto con quanto spiegato sopra, in quanto IPC PRIVATE non ` e una vera chiave (` e solo l’indicazione di creare forzatamente un nuovo oggetto), non pu` o quindi essere usata per accedere all’identificatore di uno specifico semaforo, a una specifica coda o a una specifica area di memoria condivisa. Questa soluzione

` e per` o molto comoda quando un processo alloca delle strutture i cui identifi- catori verranno passati in eredit` a ai processi figli. Supponiamo che il codice contenga l’istruzione:

semid = semget(IPC PRIVATE, . . . , . . . )

Il valore restituito da semget viene salvato in una variabile di tipo intero.

Quando il processo eseguir` a una fork, il figlio erediter` a una copia della vari- abile semid e del suo valore. Erediter` a quindi l’identificatore del semaforo allo- cato, restituito dal sistema operativo e potr` a usarlo per eseguire operazioni sul semaforo, senza dover eseguire una seconda semget.

La soluzione precedente funziona se i processi da sincronizzare sono in re- lazione padre–figli. Non sempre questo ` e il caso. Pi` u in generale i processi da sincronizzare saranno avviati da comandi differenti all’interno di shell differenti.

Occorre quindi specificare chiavi effettive affinch´ e possano usare le stesse strut- ture IPC. Supponiamo di voler usare la chiave 1234 e che un processo esegua semget(1234, . . . , . . . ). Ci sono due possibilit` a: o il semaforo richiesto non ` e an- cora stato allocato oppure ` e gi` a stato allocato da qualche processo (in generale un processo diverso dal nostro). Nel secondo caso la semget restituisce semplice- mente l’identificatore del semaforo allocato. Se invece non esiste alcun semaforo con quella chiave, il sistema operativo pu` o ancora eseguire due operazioni di- verse: l’allocazione oppure la restituzione di un errore. Il comportamento tenuto dipende dal valore dei flag. Entrambe le operazioni sono lecite: a seconda dello schema di sincronizzazione che si intende implementare pu` o darsi che il nostro processo debba effettivamente interpretare il ruolo dell’allocatore della struttura oppure pu` o darsi che questo ruolo competa ad un altro processo. Nel secondo caso, se il nostro processo scopre che la struttura non ` e ancora stata allocata, deve fermarsi perch´ e probabilmente qualcosa ` e andato storto.

Supponiamo che non esista alcuna struttura IPC del tipo richiesto, con la chiave specificata, e che il nostro processo debba fungere da allocatore. In questo caso occorre specificare come flag della system call -get un or bit a bit, i cui operandi sono la parola chiave IPC CREAT (senza ‘e’ finale) e i diritti di accesso che si intende assegnare all’oggetto allocato; per esempio IPC CREAT

| 0600. IPC CREAT `e una costante predefinita nel sistema, 0600 i diritti di accesso di cui parleremo pi` u avanti. Per esempio:

semid = semget(1234, . . . , IPC CREAT | 0600)

Si osservi che, nel caso in cui dovesse esistere una struttura di IPC con chiave identica a quella specificata, la -get non eseguirebbe alcuna allocazione e si limiterebbe a restituire l’identificatore trovato.

Se invece vogliamo che il processo non esegua alcuna allocazione e si limiti

ad utilizzare una struttura IPC gi` a allocata, restituendo errore nel caso che non

(4)

la trovi, ` e importante non specificare IPC CREAT nei flag. Spesso si utilizza in questo caso come flag il valore 0. Per esempio:

semid = semget(1234, . . . , 0)

4 Parametri specifici delle varie system call -get

Nelle sezioni precedenti sono state illustrate le caratteristiche comuni delle varie system call -get. Ciascuna di esse ha per` o anche alcuni parametri specifici, che catturano caratteristica della struttura di IPC considerata.

La system call semget, che consente di allocare semafori, ha tre parametri.

Oltre ai due gi` a descritti occorre specificare anche il numero di semafori da allocare. La questione ` e che in Unix i semafori vengono allocati a pool: ogni struttura IPC mantenuta dal sistema operativo contiene infatti un puntatore a un vettore di semafori in senso tradizionale, ai quali ` e possibile accedere sin- golarmente. Supponiamo, per esempio, che un processo padre debba allocare i semafori privati dei propri 10 figli. In Unix pu` o eseguire un’unica operazione di allocazione, nella quale richiede di riservare un vettore di 10 semafori. Cias- cun figlio potr` a accedere al proprio semaforo privato specificandone l’indice nell’array.

La system call msgget, che consente di allocare una coda di messaggi ha solo due parametri: la chiave e i flag, dei quali abbiamo gi` a parlato.

Infine, la system call shmget, per le aree di memoria condivisa, ha tre parametri: i due visti pi` u un numero che corrisponde alla dimensione in byte dell’area da riservare.

5 System call -ctl

Le system call di questo tipo consentono di effettuare operazioni dette “di con- trollo”. Ciascuna di esse consente di effettuare una certa variet` a di operazioni predefinite. I loro parametri sono: le informazioni necessarie ad identificare la struttura su cui agire, una keyword che codifica l’operazione da svolgere, un eventuale parametro dell’operazione.

1. semctl(semid, semnum, op, param): alcune delle operazioni che possono essere svolte sui semafori tramite semctl agiscono sull’intero pool di se- mafori allocato, altre su di un elemento del vettore di semafori. Il primo parametro ` e l’identificatore del pool di semafori restituito da semget; il secondo ` e l’eventuale indice di una specifica componente del vettore. Il terzo ` e l’operazione da eseguire. Le pi` u frequenti sono: SETVAL (assegna un valore alla componente di indice semnum del pool semid), GETVAL (restituisce il valore della componente di indice semnum del pool semid), IPC RMID (rimuove l’intero pool di semafori).

2. msgctl(msqid, op, param): msqid ` e l’identificatore interno della coda di

messaggi su cui si vuole operare, op ` e l’operazione da svolgere, ve ne sono

(5)

diverse, fra di esse la pi` u utilizzata ` e IPC RMID, che consente di rimuove la coda. Il terzo argomento ` e un parametro utile ad alcune delle operazioni possibili.

3. shmctl(shmid, op, param): analoga alla precedente, per le aree di memoria condivisa.

Il tipo del terzo parametro varia a seconda della system call -ctl considerata.

Nel caso dei semafori si tratta di una union semun, nel caso delle code si tratta di un puntatore a struct msqid ds (un tipo di dato predefinito), infine per le aree condivise si tratta di un puntatore a struct shmid ds.

La union semun non ` e un tipo predefinito quindi nel definire variabili di questo tipo occorre specificare l’intera struttura, ad esempio: union semun { int val; struct semid ds *buf; ushort t *array; } arg .

6 System call semop

semop ` e una system call specifica per i semafori. Consente di effettuare una serie di operazioni sospensive (up e down) su di un pool di semafori. Ha tre argomenti: l’identificatore interno del pool di semafori su cui agire, un vettore di operazioni da eseguire, il numero di tali operazioni. Supponiamo di avere un pool di 2 semafori e di volere che il nostro processo esegua una up sul primo e una down sul secondo. Potr` a eseguire le due operazioni con una sola chiamata a semop, nel seguente modo:

struct sembuf cmd[2];

semid = semget(KEY, ...);

. . .

. . . /* inizializza cmd[0] */

. . . /* inizializza cmd[1] */

semop(semid, cmd, 2);

L’esecuzione di una sola operazione (up o down) ` e quindi un caso particolare.

Un’operazione ` e descritta da una variabile di tipo struct sembuf (tipo pre- definito nel sistema), avente tre campi: short sem num; short sem op; short sem flg. Nell’esempio precedente ` e stato definito un array di due elementi di questo tipo, esso codificher` a quindi due operazioni.

L’operazione descritta da una variabile di tipo struct sembuf viene applicata a una specifica componente del pool di semafori in questione. Tale componente

` e identificata dal primo campo (sem num). Il secondo campo (sem op) codifica l’operazione da compiere. Il terzo, che ignoreremo, specifica ulteriori comporta- menti da tenere in taluni casi particolari; per noi varr` a sempre zero.

Il campo sem op codifica quindi il fatto che la system call debba compiere una up oppure una down. Questo campo contiene un numero intero (short);

vediamo il comportamento assunto dal sistema operativo a seconda dei possibili

(6)

valori di questo campo. Chiamando semval il valore corrente del semaforo che ci interessa, ci sono tre possibilit` a:

1. sem op = = 0: particolarit` a di Unix, attesa del valore zero.

il processo viene sospeso se il semaforo ha valore maggiore di zero. verr` a riattivato solo quando il semaforo assume valore zero. Non ` e un’operazione classica dei semafori.

2. sem op > 0: up.

semval viene incrementato di sem op unit` a.

3. sem op < 0: down.

• |sem op| ≤ semval: semval viene decrementato di sem op unit` a, il processo continua ad eseguire.

• |sem op| > semval: sospensione del processo. Il valore del semaforo rimane immutato.

7 System call msgsnd e msgrcv

Vediamo ora le system call specifiche per le code di messaggi, che consentono di effettuare le operazioni di invio (msgsnd) e recezione (msgrcv). Si tratta di system call molto a basso livello che possono essere utilizzate per implementare diverse politiche di sincronizzazione, con e senza attesa.

L’invio di un messaggio ` e effettuato tramite msgsnd(int msqid, const void

*msgp, size t msgsz, int msgflg). Il primo parametro, msqid, ` e l’identificatore interno della coda alla quale si intende inviare il messaggio. L’ultimo parametro, msgflg, specifica il comportamento da tenere nel caso in cui la coda sia piena. Si tratta di una situazione particolare, che difficilmente si verificher` a in laboratorio per via delle ridotte dimensioni del progetto. In generale quando la coda ` e piena

1

e risulta quindi impossibile inserire il messaggio da inviare, il processo pu` o alternativamente attendere che qualche altro processo consumi un messaggio in coda, liberando spazio, oppure terminare immediatamente la msgsnd senza attendere (opzione IPC NOWAIT).

I due parametri msgp e msgsz riguardano il messaggio da spedire. Un messaggio ` e genericamente contenuto in una struttura dati definita dall’utente a seconda delle proprie esigenze. Supponiamo che si chiami, per esempio, mio tipo mess. Tale struttura deve per` o forzatamente contenere due campi, in quest’ordine: il primo deve essere un numero intero (long, strettamente mag- giore di zero) indicante il tipo del messaggio, il secondo ` e il campo dati vero e proprio e pu` o essere di qualsiasi tipo. Fra i due non deve essere inserito al- cun altro campo. La struttura mio tipo mess potrebbe, per esempio essere cos`ı definita:

1pu`o esserlo in due modi distinti: o perch´e contiene il numero massimo di messaggi possibili o perch´e occupa le quantit`a massima di spazio per essa riservato

(7)

struct mio tipo mess { . . .

long tipo;

struct mio dato dati;

. . . }

Come vengono utilizzate queste informazioni? Il tipo del messaggio verr` a dis- cusso quando si parler` a di msgrcv. Cominciamo da msgp e msgsz. L’operazione di inserzione di un messaggio in coda effettuata dalla msgsnd consiste in un’operazione di copiatura. Il sistema operativo copia un numero di byte pari al valore di ms- gsz, consecutivi all’indirizzo specificato da msgp all’interno della coda; msgp deve essere l’indirizzo del campo tipo della struttura dati contenente il messag- gio da inviare e dovrebbe essere convertito in un (void *).

La system call msgrcv viene utilizzata da un processo che desidera ricevere un messaggio di un tipo specificato e pu` o essere bloccante o non bloccante.

E bloccante quando, qualora la coda non contenga messaggi del tipo di inter- ` esse, il processo rimanga sospeso in attesa che giunga un tale messaggio, non bloccante altrimenti (in questo caso occorre usare il flag IPC NOWAIT). ssize t msgrcv(int msqid, void *msgp, size t msgsz, long int msgtyp, int msgflg) richiede come primo argomento l’identificatore della coda in questione; il secondo argo- mento ` e l’indirizzo di una variabile all’interno della quale verr` a memorizzato il messaggio ricevuto (void * ` e un puntatore generico, occorrer` a fare un opportuno cast al tipo di dato del messaggio letto); il terzo argomento ` e il numero di byte dei quali ` e costituito il messaggio; msgtyp ` e il tipo di messaggio atteso; del flag abbiamo gi` a parlato.

Il tipo del messaggio atteso deve essere un numero positivo; nel caso partico- lare in cui si utilizza il valore zero, verr` a estratto dalla coda il primo messaggio in essa contenuto, indipendentemente dal suo tipo.

8 System call shmat e shmdt

Infine, per quel che riguarda la memoria condivisa, sono disponibili due system call che consentono a un processo di agganciare un’area di memoria condivisa, eventualmente allocata da un processo diverso, al proprio spazio degli indirizzi e di sganciarla nel momento in cui non serve pi` u.

void *shmat(shmid, addr, flag) vuole come primo argomento l’identificatore

dell’area in questione (restituito da shmget). Come secondo indirizzo utilizzer-

emo sempre il valore zero. In questo modo lasceremo al sistema operativo la

scelta dell’indirizzo attraverso il quale agganciare l’area condivisa al segmento

dati. Il terzo argomento ` e costituito da eventuali flag di controllo. La system

call restituisce un indirizzo generico, del quale occorrer` a effettuare il cast ad

un’opportuno tipo di dato. Se per esempio, in fase di allocazione era stato

richiesto di riservare una quantit` a di spazio sufficiente a contenere un dato di

tipo mio tipo dato, occorrer` a convertire il puntatore generico restituito da shmat

in un mio tipo dato *.

(8)

shmdt ha un solo argomento, un indirizzo restituito da una shmat. Dopo

aver eseguito una shmdt sull’indirizzo di un’area di memoria condivisa, non sar` a

pi` u possibile per il processo accedere all’area in questione, anche se questa non

verr` a disallocata.

Riferimenti

Documenti correlati

● A parità di media, verrà considerata la media dei voti della pagella finale della classe 2^ media delle materie di indirizzo: matematica, scienze, discipline motorie. ● In caso

Redazioni di analisi politiche e report, creazione della strategia comunicativa; Rassegna stampa specializzata, redazione comunicati stampa; Fornitura di informazioni di base

avente sede amministrativa presso l’Università degli Studi di Bari Aldo Moro, chiede di essere ammesso/a all’esame finale per il conseguimento del titolo con certificazione di

diminuiscono per tutte le classi di comuni con popolazione residente &lt; 10.000 abitanti anche rispetto alle sole Entrate attività edilizia destinate a spese per

SaNIS - Scuola Di Nutrizione ed Integrazione per lo Sport [ 20/04/2015 ] Indirizzo: Palermo. Corso “Nutrizione e supplementazione nello sport per

favorendo sia esperienze presso aziende del settore tessile, sia l’instaurazione di relazioni con diversi enti e associazioni presenti nel tessuto sociale del territorio. I

14 se cittadino non comunitario, di essere consapevole che al momento della stipulazione del contratto di lavoro subordinato a tempo indeterminato dovrà essere in possesso di uno dei

- Buone competenze nella gestione del gruppo degli adulti acquisite durante l'esperienza da formatrice ed in altri contesti lavorativi;. - Distinte competenze nella gestione del