This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Viene gestito tramite una coda a cui più processi possono accedereI processi che intendono utilizzare lo scambio di messaggi devono utilizzare la stessa codaCiascun processo può utilizzare una coda sia per inviare messaggi che per riceverneA ciascun messaggio è possibile associare un tipo di messaggioUtilizzabile per indirizzare un messaggio ad uno dei processi che accedono in lettura ad una coda condivisa
Le librerie da importare per l’utilizzo dello scambio di messaggi sono
sys/ipc.hsys/msg.h
Per poter utilizzare i messaggi è prima necessario inizializzare una coda con la funzione msgget()Una volta inizializzata la coda (da parte di tutti i processi che devono comunicare) è possibile:
Inviare un messaggio con la funzione msgsnd()Ricevere un messaggio con la funzione msgrcv()
EffettoSe non è ancora stata creata una coda a partire dalla chiave specificata, viene creata la coda e ne viene restituito l’identificatoreAltrimenti viene restituito l’identificatore della coda creata in precedenza
Il primo parametro serve a fare in modo che tutti i processi che chiamano la msgget() con la stessa chiave (un valore numerico ottenibile con la funzione ftok()) facciano riferimento alla stessa coda; se viene specificato come IPC_PRIVATE la coda è utilizzabile solo dal processo che la creaIl secondo parametro è definito come l’or di seguenti parametri:
IPC_CREAT da specificare se la coda va creata qualora non esista giàIPC_EXCL da specificare insieme a IPC_CREAT se la funzione deve fallire qualora la coda esista giàI permessi di accesso (notazione ottale)
EffettoInvia il messaggio alla coda specificata e restituisce il numero di byte spediti
ParametriL’identificatore della coda a cui inviare il messaggioUn puntatore al messaggioLa dimensione del messaggio0 o IPC_NOWAIT a seconda se il processo vada sospeso o meno in caso di coda piena
0 per ricevere il primo messaggio della codaUn numero positivo per ricevere il primo messaggio del tipo specificatoUn numero negativo per ricevere il primo messaggio con tipo maggiore del modulo del valore specificato
Un flag:IPC_NOWAIT se in assenza di messaggi si vuole che la funzione ritorni subitoMSG_NOERROR se si vogliono troncare messaggi più lunghi della dimensione specificataMSG_EXCEPT se si specifica il tipo di messaggio da non ricevere
Una pipe è una coda circolare gestita dal sistema operativoTipicamente esistono uno o più processi che scrivono sulla pipe e un processo che legge dalla pipeUna volta inizializzata una pipe, è possibile utilizzarla per scrivere e leggere bytesE’ un canale di comunicazione unidirezionale
Le librerie da importare sono:sys/types.hunistd.hstdio.h
Si definisce un vettore di due interi che corrispondono all’ingresso e all’uscita della codaSi inizializza la pipe tramite la funzione pipe()La scrittura avviene tramite la funzione write()La lettura avviene tramite la funzione read()La chiusura avviene tramite la funzione close()
EffettoLegge dalla pipe e memorizza i dati letti in bufRestituisce il numero di bytes letti, oppure 0 se è stato raggiunto EOF oppure -1 in caso di errore
ParametriCome primo parametro si usa pipedes[0]Il secondo parametro è un puntatore all’area di memoria in cui scrivere quanto lettoIl numero di bytes che si intende leggere
Ogni processo ha uno spazio di indirizzamento privatoIl sistema operativo non consente di accedere allo spazio di indirizzamento di un altro processoEsistono delle funzioni che permettono di definire dello spazio in memoria come condiviso da più processi
La funzione che permette di condividere un’area di memoria è la mmap() di cui non vediamo tutti i dettagliLibreria da includere: sys/mman.hUna volta condiviso un’area di memoria, è possibile leggere e scrivere al suo interno come per una qualsiasi altra area di memoriaL’utilizzo della memoria condivisa implica la necessità di gestire in maniera corretta l’accesso concorrente ad essa (mutua esclusione)
I semafori sono una delle possibili soluzioni al problema dell’accesso a risorse condivise in mutua esclusioneUn semaforo è un contatore intero positivo a cui sono associate
Una coda dei processi sospesi sul semaforoTre operazioni
InizializzazionePost (nota anche come signal e come V)Wait (nota anche come P)
Esistono diverse implementazioni dei semaforiNell’implementazione standard di Unix i semafori vengono raggruppati in vettoriLe principali funzioni per la gestione di vettori di semafori sono:
EffettoEffettua le operazioni specificate sul vettore di semafori
Parametrisemid l’identificatore del vettore di semaforisops un vettore di strutture dati che definisce le operazioninsops il numero di operazioni specificate in sembuf
E’ la struttura dati che descrive un’operazione da svolgere su un semaforo tramite la semop()Se si vuole agire su un solo semaforo si crea un vettore composto da un solo elementoI campi della struttura sono:
sem_num: l’indice del semaforo su cui agire all’interno del vettoresem_op: il valore da sommare al semaforosem_flg: è possibile specificare come flag IPC_NOWAIT se non si desidera sospendere il processo sull’operazione sul semaforo
Sintassiint semctl (int semid, int semnum, int cmd, union semun arg)
ParametriL’identificatore del vettore di semaforiIl numero del semaforo su cui agireIl comando da eseguire (fra cui SETVAL per inizializzare e IPC_RMID per distruggere il vettore di semafori)Un parametro opzionale dipendente dal comando (per la SETVAL si può utilizzare un intero che corrisponde al valore iniziale del semaforo)
La funzione per operare sui semafori (semop()) è scomoda da utilizzareUn’unica funzione implementa sia la wait che la postSolitamente si definiscono due funzioni più intuitive che effettuano la post e la waitAnalogamente per l’inizializzazione dei semaforiSi veda in proposito l’esempio di codice sulla memoria condivisa
/* logical size of buffer */ #define SHARED_MEM_SIZE
(BUF_SIZE+2)*sizeof(int) /* size of shared memory */
#define run_length 10 /* number of iterations in test run */
#define buf_used 0 /* semaphore array index to check buffer elts used */
#define buf_space 1 /* semaphore array index to check buffer elts empty */
int sem_init(void) {
/* procedure to create and initialize semaphores and return semaphore id,assuming two semaphores defined in the given array of semaphores */ int semid; /* create new semaphore set of 2 semaphores */ if ((semid = semget (IPC_PRIVATE, 2, IPC_CREAT | 0600)) < 0){
perror ("error in creating semaphore");/* 0600 = read/alter by user */
Memoria condivisa - esempioMemoria condivisa - esempio
/* initialization of semaphores */ /* BUF_SIZE free spaces in empty buffer */ if (semctl (semid, buf_space, SETVAL, BUF_SIZE) < 0){
perror ("error in initializing first semaphore");
exit (1); }/* 0 items in empty buffer */ if (semctl (semid, buf_used, SETVAL, 0) < 0){
perror ("error in initializing second semaphore");
exit (1); }return semid;
}
void sem_wait(int semid, int index) {
/* procedure to perform a P or wait operation on a semaphore of given index */struct sembuf sops[1]; /* only one semaphore operation to be executed */ sops[0].sem_num = index; sops[0].sem_op = -1; sops[0].sem_flg = 0;
pid_t pid; /* variable to record process id of child *//* shared memory elements */ caddr_t shared_memory; /* shared memory base address */ int *in; /* pointer to logical 'in' address for writer */ int *out; /* pointer to logical 'out' address for reader */ int *buffer; /* logical base address for buffer */ /* semaphore elements */ int semid; /* identifier for a semaphore set */ /* local variables */ int i_child, j_child; /* index variables */ int value; /* value read by child */
Memoria condivisa - esempioMemoria condivisa - esempio
printf ("Reader's report: item %2d == %2d\n", i_child, value); sem_post(semid, buf_space); /* signal semaphore for space available */ } printf ("Reader done.\n");}else { /* processing for parent == writer */ printf ("The writer process begins.\n"); for (j_child = 0; j_child < run_length; j_child++) { sem_wait(semid, buf_space); /* wait semaphore for space available */ buffer[*in] = j_child*j_child; /* put data in buffer */ *in = (*in + 1) % BUF_SIZE;
printf ("Writer's report: item %2d put in buffer\n", j_child); sem_post(semid, buf_used); /* signal semaphore for something used */ } wait (pid); printf ("Writer done.\n"); /* Remove the semaphore from the system */ if (semctl (semid, 0, IPC_RMID) < 0) { perror ("error in removing semaphore from the system"); exit (1); } printf ("Semaphore cleanup complete.\n");}exit (0);
Una interessante libreria di semafori è quella dello standard PosixComoda, ma non sempre implementataLa libreria da importare è la semaphore.hIl tipo di un semaforo è sem_tLe principali funzioni per l’utilizzo dei semafori sono:
EffettoDecrementa il valore del semaforoSe il semaforo è a 0, il chiamante viene sospesoUtilizzando la sem_trywait() si ha lo stesso effetto senza sospensione del processoRestituisce 0 in caso di successo, -1 altrimenti
EffettoSe non ci sono processi sospesi sul semaforo, incrementa il valore del semaforoSe ci sono processi sospesi sul semaforo, ne viene risvegliato unoRestituisce 0 in caso di successo, -1 altrimenti
Per comunicare tramite l’interfaccia socket è necessario stabilire una connessioneUna connessione fra un processo P e un processo Q è rappresentata da quattro parametri:
Indirizzo IP di PNumero di porta di PIndirizzo IP di QNumero di porta di Q
Un processo può avere contemporaneamente attive più connessioni
Creazione della connessioneCreazione della connessione
Per creare una connessione esistono due modi diversi:
Il server utilizza la funzione accept (bloccante) che effettua un’apertura passiva (si dichiara disponibile ad aprire una connessione)Il client utilizza la funzione connect che effettua un’apertura attiva (stabilisce effettivamente la connessione con il server)
Perché una connect abbia successo, il server che si intende contattare deve aver effettuato l’apertura passiva
Il client esegue le seguenti operazioni:Definisce e inizializza una variabile di tipo struct sockaddr_in contenente le informazioni relative al server (indirizzo IP e porta)Crea un socket della connessioneEsegue la connect specificando il proprio socket
Il serverPredispone una variabile struct sockaddr_in client_addr per memorizzare indirizzo IP e porta del clientPredispone una variabile struct sockaddr_in server_addr per memorizzare il proprio indirizzo IP e port (normalmente si specifica solo la porta)Crea un socket per accettare connessioniEffettua, tramite la funzione bind un binding fra il socket e server_addrEffettua la accept per stabilire la connessione
Sintassiint connect(int sd, struct sockaddr_in *server_addr, int ep_len)
EffettoEffettua l’apertura attiva della connessione
ParametriIl socket da utilizzareI dati relativi all’indirizzo e porta del serverLa lunghezza del parametro precedente (tipicamente sizeof(server_addr))
Sintassiint bind(int sd, struct sockaddr_in *server_addr, int ep_len)
EffettoAssocia un numero di porta TCP ad un socket. Restituisce 0 in caso di successo, -1 altrimenti
ParametriIl socket da utilizzareI dati relativi all’indirizzo e porta del serverLa lunghezza del parametro precedente (tipicamente sizeof(server_addr))
Sintassiint accept(int sd, struct sockaddr_in *client_addr, int ep_len)
EffettoAccetta una richiesta di collegamento in qualità di server e restituisce un socket
ParametriIl socket da utilizzare per la ricezione della richiestaI dati relativi all’indirizzo e porta del clientLa lunghezza del parametro precedente (tipicamente sizeof(client_addr))
Per trasferire dati si utilizzano le funzioni send() e recv()
int send(ind sd, char *message, int len, int flags)
int recv(ind sd, char *message, int len, int flags)
ParametriIl socket da utilizzareUna stringa contentente il messaggioLa lunghezza della stringaDei flag per specificare funzioni speciali (di solito 0)Restituiscono il numero di caratteri trasferiti