Corso di Sistemi Operativi



Documenti analoghi
IPC System V. Code di messaggi

Inter-Process Communication

Laboratorio di Sistemi Operativi

I/O su Socket TCP: read()

Inter Process Communication. Laboratorio Software C. Brandolese

Sistemi Operativi (modulo di Informatica II)

Sistemi Operativi. Lez. 13: primitive per la concorrenza monitor e messaggi

Corso di Laboratorio di Sistemi Operativi

Sistemi Operativi. 3 LEZIONE PROCESSI CORSO DI LAUREA TRIENNALE IN INFORMATICA. Sistemi Operativi 2007/08

Lab. di Sistemi Operativi - Esercitazione n 9- -Thread-

CREAZIONE PROCESSI IN UNIX 20

Sommario. G. Piscitelli

ESERCIZI DI PROGRAMMAZIONE C IN AMBIENTE UNIX

Lab. di Sistemi Operativi - Esercitazione n 7- -Gestione dei processi Unix-

Esempio produttori consumatori. Primitive asincrone

Esercitazione [5] Input/Output su Socket

Sincronizzazione e comunicazione tra processi in Unix. usati per trasferire ad un processo l indicazione che un determinato evento si è verificato.

Esercitazione [8] Pipe e FIFO

Interprocess Communications - II. Franco Maria Nardini

Allocazione dinamica della memoria - riepilogo

Cenni di programmazione distribuita in C++ Mauro Piccolo

Comunicazione. La comunicazione point to point e' la funzionalita' di comunicazione fondamentale disponibile in MPI

Pronto Esecuzione Attesa Terminazione

CAPITOLO 27 SCAMBIO DI MESSAGGI

Gestione dei File in C

Fondamenti di Informatica T-1, 2009/2010 Modulo 2 Prova d Esame 5 di Giovedì 15 Luglio 2010 tempo a disposizione 2h30'

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

CAPITOLO 7 - SCAMBIO DI MESSAGGI

Computazione multi-processo. Condivisione, Comunicazione e Sincronizzazione dei Processi. Segnali. Processi e Threads Pt. 2

AXO. Operativo. Architetture dei Calcolatori e Sistema. programmazione di sistema

Esercizio sulla gestione di file in Unix

I Socket. Laboratorio Software M. Grotto R. Farina

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

Strutture. Strutture e Unioni. Definizione di strutture (2) Definizione di strutture (1)

System call fcntl e record locking

Esercizio 2. Client e server comunicano attraverso socket TCP

Introduzione al Linguaggio C

Corso di Sistemi Operativi Ingegneria Elettronica e Informatica prof. Rocco Aversa. Raccolta prove scritte. Prova scritta

Le operazioni di allocazione e deallocazione sono a carico del sistema.

Gestione dei processi

SISTEMI OPERATIVI 14 settembre 2015 corso A nuovo ordinamento e parte di teoria del vecchio ordinamento indirizzo SR

Inter-Process Communication

Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica. Programmazione I - corso B a.a prof.

Con il termine Sistema operativo si fa riferimento all insieme dei moduli software di un sistema di elaborazione dati dedicati alla sua gestione.

Introduzione alla programmazione in C

Sistemi Operativi (modulo di Informatica II) I processi

I file di dati. Unità didattica D1 1

Laboratorio di Sistemi Operativi

ISTITUTO TECNICO INDUSTRIALE STATALE LA GESTIONE DEI FILE DI TESTO IN C++

Monitor. Introduzione. Struttura di un TDA Monitor

MODELLO CLIENT/SERVER. Gianluca Daino Dipartimento di Ingegneria dell Informazione Università degli Studi di Siena

Realizzazione di Politiche di Gestione delle Risorse: i Semafori Privati

SISTEMI OPERATIVI. Sincronizzazione dei processi. Domande di verifica. Luca Orrù Centro Multimediale Montiferru 30/05/2007

Funzioni. Il modello console. Interfaccia in modalità console

Chiamate di sistema per la Gestione dei processi in POSIX. E.Mumolo, DEEI

Per scrivere una procedura che non deve restituire nessun valore e deve solo contenere le informazioni per le modalità delle porte e controlli

Sistemi Operativi. Lezione 7 Comunicazione tra processi

Inizializzazione, Assegnamento e Distruzione di Classi

Introduzione alle applicazioni di rete

Calcolatori Elettronici Parte X: l'assemblatore as88

Processi UNIX. I Processi nel SO UNIX. Gerarchie di processi UNIX. Modello di processo in UNIX

I processi. Concetto di processo Scheduling dei processi Operazioni sui processi Processi cooperanti Comunicazione fra processi

Funzioni matlab per la gestione dei file. Informatica B Prof. Morzenti

costruttori e distruttori

I puntatori e l allocazione dinamica di memoria

J+... J+3 J+2 J+1 K+1 K+2 K+3 K+...

dall argomento argomento della malloc()

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

Appunti tratti dal videocorso on-line di Algoritmi e Programmazione Avanzata By ALeXio

Sistemi Operativi. Interfaccia del File System FILE SYSTEM : INTERFACCIA. Concetto di File. Metodi di Accesso. Struttura delle Directory

SOMMARIO Coda (queue): QUEUE. QUEUE : specifica QUEUE

Il costrutto monitor [Hoare 74]

MANUALE D'USO DEL PROGRAMMA IMMOBIPHONE

Definizione di processo 1

CONTROLLO DI GESTIONE DELLO STUDIO

Il descrittore di processo (PCB)

Matlab: Gestione avanzata dei file

Il Sistema Operativo

Esercitazione finale per il corso di Sistemi Operativi (A.A. 2004/2005)

Funzioni in C. Violetta Lonati

Transcript:

Corso di Sistemi Operativi Comunicazione tra processi (IPC) a.a. 2012/2013 Francesco Fontanella

Comunicazione tra processi I processi in esecuzione concorrente possono essere indipendenti o cooperanti. Sono indipendenti se non processi o subirne l'influenza. possono influire su altri Sono cooperanti se influenzano o sono influenzati da altri processi. a.a. 2012/2013 3

Comunicazione interprocesso Al fine di consentire lo scambio di dati e informazioni tra processi, il SO deve implementare un meccanismo di comunicazione tra processi (IPC, InterProcess Communication). Esistono due modelli fondamentali di IPC: Memoria condivisa Scambio di messaggi. Entrambi i modelli sono diffusi e spesso sono presenti entrambi. a.a. 2012/2013 4

Comunicazione interprocesso messaggi Memoria condivisa a.a. 2012/2013 5

Memoria condivisa La condivisione di memoria richiede che: Un processo allochi una zona di memoria condivisa (solitamente nel suo spazio di indirizzamento) Gli altri processi aggiungano tale zona al loro spazio di indirizzamento Ovviamente il SO, solo in questo caso, consente che i processi comunicanti possano accedere alla memoria del processo che ha allocato lo spazio di memoria condivisa. La comunicazione avviene poi tramite scritture e letture dall'area messa in condivisione. La sincronizzazione è a carico dei processi a.a. 2012/2013 6

Il problema del Produttore/consumatore esiste un processo produttore (Producer) che genera valori (record, caratteri, oggetti, etc.) e vuole trasferirli a un processo consumatore (Consumer) che prende i valori generati e li consuma la comunicazione avviene attraverso una singola variabile condivisa Bisogna garantire che : Il produttore scriva nuovamente l'area di memoria condivisa prima che il consumatore abbia effettivamente utilizzato il valore precedente Il consumatore non legga due volte lo stesso valore, ma attenda la generazione di un nuovo valore. a.a. 2012/2013 7

Produttore/consumatore: soluzione I due processi possono essere messi in comunicazione per mezzo di un buffer di memoria condivisa. Il buffer sarà scritto dal produttore e letto dal consumatore. È possibile pensare a due tipi di buffer: Illimitato: la dimensione del buffer non è limitata, e il produttore non deve preoccuparsi che il consumatore abbia letto i dati inseriti in precedenza. Limitato: il buffer ha dimensioni fissate. Il produttore deve attendere la lettura del buffer da parte del consumatore. a.a. 2012/2013 8

Implementazione del buffer limitato È possibile implementare un buffer limitato per mezzo di una array circolare out in in out a.a. 2012/2013 9

produttore/consumatore: esempio #define BUFFER_SIZE 10 #define IN 0 #define OUT 1 typedef struct {... } item; item buffer[buffer_size]; int inout[2]; inout[in] = inout[out] = 0; a.a. 2012/2013 10

Il produttore void producer(item buffer[], int &in, int out) item tmp; } while (true) { /* si produce un item*/.. while (( (in + 1) % BUFFER_SIZE ) == out) ; /* si aspetta che ci sia posto nel buffer*/ buffer[in] = item; in = (in + 1) % BUFFER SIZE; } a.a. 2012/2013 11

Il consumatore void consumer(item buffer[], int in, int &out) { item tmp while (true) { while (in == out) ; // la coda è vuota: attesa // si estrae l'item tmp = buffer[out]; out = (out + 1) % BUFFER SIZE; } } /* si consuma l'item*/.. a.a. 2012/2013 12

Memoria condivisa secondo POSIX seg_id = shmget(ipc_private, size, S_IRUSR S_IWUSR) Il primo parametro è un identificatore di segmento. Il valore IPC_PRIVATE specifica che bisogna creare un nuovo segmento. size: dimensioni del segmento. Il terzo parametro specifica la modalità di accesso. S_IRUSR S_IWUSR indica sia lettura che scrittura. Shmget restituisce l'identificatore dell'area allocata. a.a. 2012/2013 13

Memoria condivisa secondo POSIX I processi che vogliono accedere all'area creata dalla chiamata shmget devono usare la funzione: sh_mem = (char *) shmat (id, NULL, 0) Id èi'identificatore del segmento; Il secondo parametro specifica dove annettere il segmento, se vale NULL decide il SO. Il terzo parametro specifica la modalità di accesso: 0 sola lettura > 0 lettura e scrittura a.a. 2012/2013 14

Memoria condivisa secondo POSIX Quando non è più necessario accedere ad una zona di memoria condivisa si può usare la funzione: shmdt(sh_mem) Dove sh_mem è il puntatore alla zona condivisa a.a. 2012/2013 15

Memoria condivisa secondo POSIX: esempio int main() { pid_t pid; int seg_id; char *sh_mem; seg_id = shmget(ipc_private, size, S_IRUSR S_IWUSR); sh_mem = (char *) shmat(seg_id, NULL, 1); pid = fork(); if (pid < 0) { // errore! fprintf(stderr, "Fork Failed"); exit( 1); } a.a. 2012/2013 16

Memoria condivisa secondo POSIX: esempio } if (pid == 0) // processo figlio printf( processo figlio stringa condivisa: %s, sh_mem); else { // processo padre sprintf (sh_mem, hello!"); exit(0); } a.a. 2012/2013 17

Scambio di messaggi Consente ai processi di comunicare e sincronizzare le loro azioni. Un messaggio è un insieme di informazioni formattate da un processo mittente e interpretate da un processo destinatario Un meccanismo di "scambio di messaggi" copia le informazioni di un messaggio da uno spazio di indirizzamento di un processo allo spazio di indirizzamento di un altro processo Questo meccanismo permette di mettere in comunicazione due processi senza che essi debbano condividere lo spazio di indirizzamento a.a. 2012/2013 18

Scambi di messaggi: send e receive send(message) È utilizzata dal processo mittente per "spedire" un messaggio ad un processo destinatario. Deve specificare il processo destinatario. Le dimensioni possono essere fisse o variabili receive(message): utilizzata dal processo destinatario per "ricevere" un messaggio da un processo mittente. Non è necessario specificare il mittente a.a. 2012/2013 19

Scambio di messaggi Se due processi P e Q vogliono comunicare devono: Stabilire un canale di comunicazione tra loro Scambiarsi messagi per mezzo di send e receive mittente Dati originali Sistema Operativo messaggio Dati copiati destinatario a.a. 2012/2013 20

Comunicazione diretta I processi devono conoscere il PID del processo con cui vogliono comunicare: send (P, message) invia messaggio al processo P receive(q, message) riceve un messaggio dal processo Q Proprietà del canale di comunicazione Si stabilisce in maniera automatica; È associato esattamente a due processi; Il canale è di solito bidirezionale a.a. 2012/2013 21

Comunicazione indiretta I messaggi si inviano/prelevano a/da porte (dette anche mailboxes): Ogni porta è individuata in maniera univoca, tipicamente un intero. Due processi possono comunicare se condividono una porta. Proprietà del canale di comunicazione: Canale stabilito dalla condivisione della porta Un canale può essere associato a più processi Ogni coppia di processi può condividere più canali (porte) I Canali possono essere uni/bi-direzionali a.a. 2012/2013 22

Comunicazione indiretta Il SO consente di: creare una porta Mandare/ricevere messaggi utilizzando la porta Distruggere la porta Primitive send(a, message) invia un messaggio alla porta A receive(a, message) riceve un messaggio dalla porta A a.a. 2012/2013 23

Scambio di messaggi: sincronizzazione Lo scambio di messaggi può essere sincrono (bloccante) o asincrono (non bloccante). Invio sincrono: il mittente attende la ricezione da parte del destinatario; Invio asincrono: il mittente riprende la sua normale esecuzione; ricezione sincrona: il destinatario si blocca in attesa del messaggio; ricezione asincrono: il destinatario riceve messaggi validi oppure nulli a.a. 2012/2013 24

Problema del produttore/consumatore Il problema del produttore/consumatore può essere risolto per mezzo di send/receive sincroni Il produttore si limita ad inviare messaggi e ad attenderne la ricezione da parte del consumatore Il consumatore aspetta invece i messaggi del produttore a.a. 2012/2013 25

Produttore/consumatore #define BUFFER_SIZE 10 #define IN 0 #define OUT 1 typedef struct {... } item; item buffer[buffer_size]; int inout[2]; a.a. 2012/2013 26

Scambio di messaggi vs memoria condivisa: produttore void producer(pid_t c_id) } item tmp; while (true) { /* si produce un item*/.. send(&tmp, sizeof(item), c_id); } Messaggi void producer(item buffer[], int &in, int out) } Memoria condivisa item tmp; while (true) { /* si produce un item*/.. while (((in + 1) % BUFFER_SIZE) == out) ; buffer[in] = item; in = (in + 1) % BUFFER SIZE; } a.a. 2012/2013 27

Scambio di messaggi vs memoria condivisa: produttore messaggi Memoria condivisa void consumer(pid_t p_id) { item tmp; } while (true) { receive(&tmp, sizeof(item), p_id); } /* si consuma l'item*/.. void consumer(item buffer[], int in, int &out) { item tmp; } while (true) { while (in == out) ; } // si estrae l'item tmp = buffer[out]; out = (out + 1) % BUFFER SIZE; /* si consuma l'item*/.. a.a. 2012/2013 28

produttore/consumatore: memoria condivisa int main(int argc, char* argv[]) { /* process id */ pid_t pid; /* memory segment id */ int buffer_id, inout_id;; item* shared_buffer; /* buffer pointer */ int* shared_inout; /* inout pointer */ if ((buffer_id = shmget(ipc_private, sizeof(item)*buffer_size, PERMS)) == 1) { fprintf(stderr,"errore!: impossibile allocare memoria condivisa\n"); exit( 1); } if ((inout_id = shmget(ipc_private, sizeof(int)*2, PERMS)) == 1) { fprintf(stderr,"errore!: impossibile allocare memoria condivisa\n"); exit( 1); } a.a. 2012/2013 29

produttore/consumatore: memoria condivisa if ((shared_buffer = (item *) shmat(buffer_id, 0, 0)) == (item *) 1) { fprintf(stderr,"unable to attach to segment %d\n",buffer_id); exit( 1); } if ((shared_buffer = (int *) shmat(inout_id, 0, 0)) == (int *) 1) { fprintf(stderr,"unable to attach to segment %d\n",buffer_id); exit( 1); } pid = fork(); if (pid < 0) { // errore! fprintf(stderr, "Fork Failed"); exit( 1); } if (pid == 0) // processo figlio: consumatore consumer(shared_buffer, shared_inout[in], sharred_inout[out]) else // processo padre: produttore producer(shared_buffer, shared_inout[in], shared_inout[out]) } exit(0); a.a. 2012/2013 30

produttore/consumatore: scambio messaggi int main(int argc, char* argv[]) { /* process id */ pid_t pid1, pid2; pid1 = getpid(); pid2 = fork(); if (pid2 < 0) { // errore! fprintf(stderr, "Fork Failed"); exit( 1); } if (pid2 == 0) // processo figlio: consumatore consumer(pid1); else // processo padre: produttore producer(pid2); } exit(0); a.a. 2012/2013 31

Socket Una socket è definita come l'estremità di un canale di comunicazione. Una coppia di processi su due macchine distinte (collegate in rete) può comunicare per mezzo di una coppia di socket. Una socket è individuata da: Indirizzo IP Numero di porta Esempio: 143.225.2.121:1625 Le porte nell'intervallo 0-1024 sono assegnate a servizi standard. a.a. 2012/2013 40

Socket: Esempio a.a. 2012/2013 41

Le Pipe È un canale di comunicazione tra processi Questioni relative alla pipe: Comunicazione birezionale o unidirezionale? L'eventuale comunicazione bidirezionale deve essere half o full duplex? Deve esistere una relazione padre-figlio? I processi devono essere sulla stessa macchina? a.a. 2012/2013 42

Pipe convenzionali È un canale di comunicazione tra processi Usano il paradigma produttore-consumatore. Sono unidirezionali. Il produttore scrive sull'estremità detta write-end. Il consumatore invece legge dalla read-end. Il sistema provvede a copiare i dati scritti dal produttore sulla write-end Nei sistemi UNIX, la pipe è considerata una tipo speciale di file. a.a. 2012/2013 43

La funzione pipe #include <unistd.h> int pipe(int *fd); Restituisce: 0 se OK, -1 in caso di errore Restituisce 2 descrittori di file (parametro fd) a.a. 2012/2013 44

La funzione pipe in filedes[0] c'è un descrittore di file di un "file" aperto in lettura in filedes[1] c'è un descrittore di file di un "file" aperto in scrittura I dati fluiscono dal file in cui si scrive a quello in cui si legge grazie alla pipe l'output di filedes[1] è l'input di filedes[0] a.a. 2012/2013 45

La funzione pipe Un tipico utilizzo della pipe è questo: int fd[2];... pipe(fd); pid=fork();... a.a. 2012/2013 46

Pipe+fork: effetto parent child fork fd[0] fd[1] fd[0] fd[1] PIPE a.a. 2012/2013 47

Scelta della direzione, Esempio 1 Dopo la fork, per esempio, si può creare un canale dal padre verso il figlio: if (pid > 0) // padre close(fd[0]); else close(fd[1]); // figlio a.a. 2012/2013 48

Scelta della direzione, Esempio 1 parent child fork fd[0] fd[1] fd[0] fd[1] PIPE a.a. 2012/2013 49

Scelta della direzione, Esempio 1 parent child fork fd[0] fd[1] fd[0] fd[1] PIPE a.a. 2012/2013 50

Scelta della direzione, Esempio 2 Dopo la fork, per esempio, si può creare un canale dal figlio verso il padre: if (pid > 0) // padre close(fd[1]); else close(fd[0]); // figlio a.a. 2012/2013 51

Scelta della direzione, Esempio 2 parent child fork fd[0] fd[1] fd[0] fd[1] PIPE a.a. 2012/2013 52

Pipe: uso Crata la pipe e scelto il verso di comunicazione è possibile usare le normali funzioni di I/O di lettura/scrittura (eg. read, write) Nella pipe i dati vengono letti nello stesso ordine in cui sono scritti Le pipe hanno una capacità limitata (la costante PIPE_BUF ne specifica la dimensione massima) a.a. 2012/2013 53

La funzione write ssize_t write(int fd, const void *buf, size_t count); Quando la pipe si riempie la write si blocca fino a che la read non ha rimosso un numero sufficiente di dati (non vengono effettuate scritture parziali). Se il descrittore del file che legge dalla pipe è chiuso, una write genererà un errore (segnale SIGPIPE) a.a. 2012/2013 54

La funzione read ssize_t read(int fd, void *buf, size_t count); Legge i dati dalla pipe nell ordine in cui sono scritti. Non è possibile rileggere o rimandare indietro i dati letti. Se la pipe è vuota la read si blocca fino a che non vi siano dei dati disponibili (syscall bloccante). Se il descrittore del file in scrittura è chiuso, la read restituirà EOF dopo aver completato la lettura dei dati. a.a. 2012/2013 55

La funzione close La funzione close sul descrittore del file in scrittura agisce come end-of-file per la read. La chiusura del descrittore del file in lettura causa un errore nella write. a.a. 2012/2013 56

Pipe convenzionali: esempio int main(void) { char write_msg[buffer_size] = "Greetings"; char read_msg[buffer_size]; pid_t pid; int fd[2]; /** create the pipe */ if (pipe(fd) == 1) { fprintf(stderr,"pipe failed"); return 1; } /** now fork a child process */ pid = fork(); if (pid < 0) { fprintf(stderr, "Fork failed"); return 1; } a.a. 2012/2013 57

Pipe convenzionali: esempio if (pid > 0) { /* parent process */ /* close the unused end of the pipe */ close(fd[read_end]); /* write to the pipe */ write(fd[write_end], write_msg, strlen(write_msg)+1); /* close the write end of the pipe */ close(fd[write_end]); } else { /* child process */ /* close the unused end of the pipe */ close(fd[write_end]); /* read from the pipe */ read(fd[read_end], read_msg, BUFFER_SIZE); printf("child read %s\n",read_msg); /* close the write end of the pipe */ close(fd[read_end]); } } a.a. 2012/2013 58

FIFO (Named pipe) La relazione padre figlio non è necessaria. Continuano ad esistere anche dopo che i processi comunicanti le hanno create. Possono essere utilizzate da diversi processi una volta che sono state create. In UNIX sono create con la funzione mkfifo() Sono gestite con le chiamate open(), read(), write() e close() a.a. 2012/2013 59

#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); Restituisce: 0 se OK -1 in caso di errore a.a. 2012/2013 60

FIFO: apertura Creare una FIFO è come creare un file (FIFO è un tipo possibile di file) Una volta creata, una FIFO può essere aperta con open oppure fopen Somiglia ad un file (si utilizzano le stesse funzioni di I/O, risiede sul filesystem) ha le caratteristiche di una pipe: I dati scritti vengono letti in ordine first-in-first-out Non è possibile rileggere i dati già letti né posizionarsi all interno con lseek a.a. 2012/2013 61

FIFO: sincronizzazione Una open in lettura si blocca fino a che un processo non effettua una open in scrittura (e viceversa). Se viene utilizzato il flag O_NONBLOCK, una open in lettura ritorna immediatamente se non c è un processo che ha effettuato una open in scrittura. una open in scrittura restituisce un errore se non c è un processo che ha effettuato una open in lettura. Se si effettua una write su una FIFO che non e piu aperta in lettura da alcun processo, viene generato il segnale SIGPIPE Quando l ultimo degli scrittori chiude una FIFO, viene generato un EOF per il processo lettore a.a. 2012/2013 62

FIFO: esempio Diversi client possono inviare richieste ad un server SERVER read FIFO write write Client..... Client a.a. 2012/2013 63

FIFO: client #include<stdio.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> int main(){ int fds, pid; char c[5],f[9]; if((fds=open("fifo",o_wronly))<0){ //Apre la FIFO printf("error: impossibile aprire la fifo in scrittura\n"); exit(0); } pid=getpid(); } write(fds,&pid,sizeof(pid)); //Invia il messaggio al server close(fds); //Chiude la well known FIFO while(1) //Attende di essere ucciso ; a.a. 2012/2013 64

FIFO: server #include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<signal.h> int main(){ int fd; int pidc; //CREA la FIFO if(mkfifo("fifo",s_irwxu S_IRGRP S_IROTH)<0){ printf("\nimpossibile creare la FIFO\n"); exit(0); } SEGUE... a.a. 2012/2013 65

FIFO: server while(1){ if((fds=open("fifo",o_rdonly)) < 0){ //Apre la FIFO printf("\nerror: impossibile aprire la FIFO\n"); exit(0); } If (read(fds,&pidc,sizeof(pidc))<0){ //Legge dalla FIFO printf("error: il messaggio non è valido\n"); return; } printf("il server ha letto dalla FIFO %d\n",pidc); kill(pidc,sigkill); //Uccide il processo client } close(fds); //Chiude la well-known FIFO } return; a.a. 2012/2013 66

Code di Messaggi (IPC SysV) Aprire/creare una coda: id msgget(key, flags) Inviare messaggio: ok msgsnd(id, msg, nbytes, flags) Ricevere messaggio: nbytes msgrcv(id, msg, nbytes, type, flags) Operazioni di controllo: ok msgctl(id, cmd, buf) a.a. 2012/2013 67

Creare una Coda: Condivisione della chiave padre/figlio id = msgget(ipc_private, IPC_CREAT IPC_EXCL 0666); if (id > 0) fork();... Uso della funzione ftok #define CHIAVE /home/user // Server: key = ftok(chiave, 1); id = msgget(key, IPC_CREAT IPC_EXCL 0666); // Client: key = ftok(chiave, 1); id = msgget(key, 0); Definizione (condivisa) di una costante #include MyQueue.h // Server: id = msgget(key, IPC_CREAT IPC_EXCL 0666); // Client: int id = msgget(key, 0); MyQueue.h.. #define KEY 18071968.. a.a. 2012/2013 68

Creare/Aprire una coda int msgget(key_t key, int flags); Apre la coda con chiave key; Viene creata una nuova coda se: la chiave e' IPC_PRIVATE, oppure flag contiene IPC_CREAT e la chiave e' nuova Restituisce l'identificatore della coda, oppure -1 in caso di errore. a.a. 2012/2013 69

Creare una coda: opzioni Diverse opzioni del campo flags possono essere combinate con l'operatore (or bit-a-bit): IPC_CREAT: se non esiste, creala IPC_EXCL: se esiste, fallisci ES: creare una nuova coda (padre/figlio) msgget(ipc_private, IPC_CREAT IPC_EXCL 0666) Permessi per gli utenti a.a. 2012/2013 70

Inviare messaggi È necessario dichiarare una apposita struct: Obbligatorio, Non fa parte del contenuto Definiti dall'utente struct my_msg { long type; Type1 user_field1;... Type1 user_field_n; } Dimensioni del messaggio: sizeof(my_msg) sizeof(long) a.a. 2012/2013 71

Inviare messaggi int msgsnd(int id, const void *ptr, size_t nbytes, int flags); Invia il messaggio contenuto all'indirizzo ptr, di dimensione nbytes (solo dei campi definiti dall'utente), sulla coda id, con opzioni flags Restituisce 0 (success) oppure -1 (failure) Se la coda è piena: Senza opzioni: si Blocca in attesa spazio Opzione IPC_NOWAIT: la chiamata ritorna immediatamente) a.a. 2012/2013 72

Osservazioni A differenza delle pipe, le code di messaggi ricordano dove finisce un messaggio e ne comincia un altro (ricordano i record boundaries) I messaggi in coda possono essere eterogenei (dimensioni diverse) Ogni messaggio ha un'etichetta chiamata tipo (campo type della struct) un processo puo' richiedere messaggi di un dato tipo a.a. 2012/2013 73

Ricevere messaggi int msgrcv(int id, const void *ptr, size_t nbytes, long type, int flags) Riceve un messaggio dalla coda id, di tipo type, e lo memorizza all'indirizzo ptr, se la sua dimensione e' al piu' nbytes bytes Restituisce la dimensione effettiva del messaggio ricevuto (-1 in caso di errore) Se la coda è vuota: Senza opzioni: Blocca il chiamante Opzione IPC_NOWAIT: la chiamata ritorna immediatamente a.a. 2012/2013 74

Ricevere messaggi: il campo type Significati del campo type: type == 0, riceve il primo messaggio presente nella coda type > 0, riceve il primo messaggio di tipo type type < 0, riceve il primo messaggio di tipo minimo tra quelli di tipo minore o uguale di -type a.a. 2012/2013 75

Ricevere messaggi Cosa succede se il messaggio è troppo grande? Se non viene specificata nessuna opzione (campo flags), allora msgrcv() fallisce e il messaggio resta nella coda Se invece viene data l'opzione MSG_NOERROR allora il messaggio viene troncato e ricevuto (la parte troncata viene persa) a.a. 2012/2013 76

Ricevere messaggi: Osservazioni Più processi possono scrivere e leggere sulla stessa coda Per non confondere i flussi, i processi possono usare il campo tipo del messaggio ES: un server e molti client i client mandano messaggi di tipo 1 il server risponde usando il pid del client come tipo a.a. 2012/2013 77

Code di messaggi: un server e molti client SERVER Tipo: pid Tipo: 1 CODA messaggi Tipo: pid_p1 Tipo: 1 Tipo: pid_pn Tipo: 1 P1 PN a.a. 2012/2013 78

Modificare una coda int msgctl(int id, int cmd, struct msqid_ds* buf); id: chiave della coda da modificare cmd: azione da eseguire buf: puntatore ad una struct di tipo msqid_ds Per cancellare la coda: msgctl(id, IPC_RMID, NULL) Ha effetto immediato i messaggi eventualmente presenti vengono persi. Tutti i processi in attesa vengono risvegliati a.a. 2012/2013 79

Le struct msqid_ds e ipc_perm struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) */ time_t msg_rtime; /* Time of last msgrcv(2) */ time_t msg_ctime; /* Time of last change */ unsigned long msg_cbytes; /* Current number of bytes in queue */ msgqnum_t msg_qnum; /* Current number of messages in queue */ msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ pid_t msg_lrpid; /* PID of last msgrcv(2) */ }; struct ipc_perm { key_t key; /* Key supplied to msgget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short seq; /* Sequence number */ }; a.a. 2012/2013 80

Msgctl: Alcuni comandi IPC_STAT: mette in buf la struttura msqid_ds della coda IPC_SET: setta i 4 campi (msg_perm.uid,msg_perm.gid, msg_perm.mode, msg_qbytes) della struttura puntata da buf nei corrispondenti campi della struttura della coda IPC_RMID : rimuovi la coda e ciò che contiene a.a. 2012/2013 81

Visualizzare e rimuovere le risorse $> ipcs T ID KEY MODE OWNER Message Queues: q 10 0x003e8 rw r r st103010 q 20 0x003e8 rw rw r st102210 Shared Memory: m 25 0x003e8 rw rw rw st103010 Semaphores: s 280 0x003e8 rw rw rw Le risorse non vengono deallocate quando il processo che le ha create termina. Occorre farlo esplicitamente: $> ipcrm q 10 s 280 m 25 st245020 a.a. 2012/2013 82

Code messaggi: esempio Si realizzino due distinti programmi: receiver e sender Receiver crea una coda di messaggi (nuova) e legge messaggi dalla coda fino a quando non riceve un messaggio vuoto Sender riceve dalla linea di comando due parametri: L'id di una coda di messaggi Un numero N Sender invia sulla coda specificata dall'id N messaggi e poi invia un messaggio vuoto. I programmi sono reperibili qui: http://webuser.unicas.it/fontanella/dida/so/1213/slides/exas/msgqueue.zip a.a. 2012/2013 83