IPC System V Code di messaggi
Panoramica coda di messaggi una lista concatenata di messaggi, FIFO semaforo un contatore condiviso, atomicamente modificabile memoria condivisa uno spazio di memoria accessibile da piu' processi Marco Faella 2
Panoramica Sono strutture IPC che risiedono nel kernel Sono permanenti: non vengono cancellate quando i processi terminano Sono utili i comandi ipcs e ipcrm Marco Faella 3
Identificatori Alla sua creazione, ogni struttura IPC riceve un identificatore numerico Per usare una struttura IPC, bisogna specificare il suo identificatore Come fanno processi diversi ad usare la stessa struttura IPC? Marco Faella 4
Chiavi ed identificatori I processi si trasmettono l'identificatore facile se sono parenti piu' difficile se sono indipendenti Oppure, i processi fanno generare l'identificatore a partire da una chiave condivisa la funzione ftok() (filename to key) genera una chiave a partire dal nome di un file esistente Marco Faella 5
Code di messaggi 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) Chiudere/altro ok msgctl(id, cmd, buf) Marco Faella 6
Modo 1: Creare una coda int id = msgget(ipc_private, IPC_CREAT IPC_EXCL 0666); if (id < 0) perror( msgget ), exit(1); fork();... Modo 2: #define CHIAVE /home/mfaella // Server: key_t key = ftok(chiave, 1); int id = msgget(key, IPC_CREAT IPC_EXCL 0666); // Client: key_t key = ftok(chiave, 1); int id = msgget(key, 0); Marco Faella 7
Creare o 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 Marco Faella 8
Creare o aprire una coda int msgget(key_t key, int flag); Opzioni (flags), si combinano con (or bit-a-bit): IPC_CREAT: se non esiste, creala IPC_EXCL: se esiste, fallisci 0666: permessi di lettura e scrittura per tutti gli utenti Quindi, per creare una coda tra padre e figlio: msgget(ipc_private, IPC_CREAT IPC_EXCL 0666) Marco Faella 9
Coda di messaggi tra padre e figlio int id; if ( (id = msgget(ipc_private, IPC_CREAT IPC_EXCL 0666)) < 0) perror( msgget ), exit(1); if ( (pid=fork()) < 0 ) perror( fork ), exit(1); else if (pid>0) { // padre msgsnd(...);... msgctl(id, IPC_RMID, NULL); } else { // figlio msgrcv(...); } Marco Faella 10
Inviare messaggi Dichiarare una struttura apposita struct my_msg { long type; int info1; int info2; double info3; } Il primo campo (tipo del messaggio) e' obbligatorio e non fa parte del contenuto del messaggio I successivi sono liberi La dimensione del contenuto del messaggio e' sizeof(struct my_msg) sizeof(long) Marco Faella 11
Inviare messaggi int msgsnd(int id, const void *ptr, size_t nbytes, int flags); Invia il messaggio contenuto all'indirizzo ptr, di dimensione nbytes, sulla coda id, con opzioni flags Restituisce 0 oppure -1 Uso tipico: msgsnd(id, &msg, msg_size, 0); Se la coda e' piena, il processo si blocca finche' non c'e' spazio Marco Faella 12
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 un processo puo' richiedere messaggi di un dato tipo Marco Faella 13
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 (oppure -1 in caso di errore) Blocca il processo se la coda e' vuota Marco Faella 14
Ricevere messaggi int msgrcv(int id, const void *ptr, size_t nbytes, long type, int flags); Significato di type type == 0, riceve il primo messaggio type > 0, riceve il primo messaggio di tipo type type < 0, riceve un messaggio di tipo minimo tra quelli di tipo minore o uguale di type Marco Faella 15
Ricevere messaggi int msgrcv(int id, const void *ptr, size_t nbytes, long type, int flags); Che succede se il messaggio e' troppo grande? Nessuna opzione: msgrcv fallisce, il messaggio resta nella coda Opzione MSG_NOERROR: il messaggio viene troncato e ricevuto Marco Faella 16
Osservazioni Piu' processi possono scrivere e leggere sulla stessa coda Per non confondere i flussi, i processi possono usare il tipo dei messaggi Ad esempio, consideriamo un server e tanti client: i client mandano messaggi di tipo 1 il server risponde usando il pid del client come tipo Marco Faella 17
Cancellare una coda int msgctl(int id, int cmd, struct msqid_ds* buf); Per cancellare la coda: msgctl(id, IPC_RMID, NULL) Effetto immediato Eventuali messaggi contenuti sono persi Marco Faella 18
Interludio: scanf char s[100]; scanf( %20s, s); legge una stringa da terminale. Si ferma quando trova un carattere di spaziatura, oppure quando ha letto 20 caratteri scanf( %20[^\n], s); legge una stringa da terminale. Si ferma quando trova un carattere di fine riga, oppure quando ha letto 20 caratteri il carattere di fine riga non va in s, ma rimane nel buffer di input Marco Faella 19
Interludio: scanf scanf( %20[^\n]%*c, s); legge una stringa da terminale. Si ferma quando trova un carattere di fine riga, oppure quando ha letto 20 caratteri inoltre, legge e scarta il carattere successivo quindi, se la lettura era stata fermata da un carattere di fine riga, questo carattere viene rimosso dal buffer di input Marco Faella 20
Esercizio 1 Scrivere un programma C che genera un figlio e comunica con lui usando una coda di messaggi il figlio legge da terminale una stringa e la invia al padre quando il padre riceve una stringa, invia al figlio un messaggio contenente il numero di a nella stringa quando il figlio riceve la risposta dal padre, la mostra sul terminale Assicurarsi che alla fine la coda sia cancellata Marco Faella 21
Esercizio 2 Scrivere due programmi separati, che si comportano da client e server e comunicano usando una coda di messaggi Il server riceve dai client messaggi contenenti numeri interi Quando il server riceve un numero x da un client con pid y, aggiunge al file log.txt la linea y: x Poi, invia una risposta al client y contenente il numero di righe attualmente presenti nel file log.txt Il client invia al server un numero casuale e aspetta di ricevere la risposta dal server. Quando riceve la risposta, la stampa su STDOUT. Il procedimento si ripete a intervalli di 1 secondo. Testare il programma in presenza di numerosi client Marco Faella 22