Ουρές Μηνυμάτων (Message Queues in System V) Προγραμματισμός II 1 lalis@inf.uth.gr
Ουρά μηνυμάτων Αποθήκη μηνυμάτων στην μνήμη του λειτουργικού Περιορισμένος αριθμός / όγκος μηνυμάτων Κάθε μήνυμα είναι μια σειρά από bytes κατ εξαίρεση, κάποια από αυτά τα bytes ερμηνεύονται και από το λειτουργικό σύστημα βλέπε παρακάτω Κάθε μήνυμα είναι ξεχωριστό από τα υπόλοιπα Πολλές διεργασίες μπορεί να χρησιμοποιούν την ίδια ουρά μηνυμάτων ταυτόχρονα με εγγυημένο διαχωρισμό ανάμεσα στα διαφορετικά μηνύματα βεβαίως, χωρίς συγχρονισμό, τα μηνύματα θα μπουν / απομακρυνθούν στην / από την ουρά με τυχαία σειρά Προγραμματισμός II 2 lalis@inf.uth.gr
P1 P2 P3 P4 msg4 msg3 msg2 msg1 Προγραμματισμός ΙΙ 3 lalis@inf.uth.gr
Δημιουργία/ανεύρεση ουράς μηνυμάτων int msgget(key_t key, int flags); Δημιουργεί/βρίσκει την ουρά μηνυμάτων με κλειδί key, και επιστρέφει το αναγνωριστικό της Οι «κλασικές» βασικές επιλογές flags IPC_CREAT: δημιουργεί την ουρά μηνυμάτων IPC_EXCL: αποτυχία αν η ουρά ήδη υπάρχει άδειες, χρησιμοποιώντας τις ίδιες τιμές όπως στην open Αν το key είναι IPC_PRIVATE δημιουργείται μια ανώνυμη/ιδιωτική ουρά μηνυμάτων δεν μπορεί να βρεθεί μέσω msgget Προγραμματισμός II 4 lalis@inf.uth.gr
Έλεγχος ουράς μηνυμάτων int msgctl(int msqid, int cmd, struct msqid_ds *data); H cmd προσδιορίζει την ζητούμενη λειτουργία IPC_RMID: καταστροφή ουράς μηνυμάτων IPC_STAT: ανάκτηση πληροφοριών για την ουρά IPC_SET: καθορισμός ιδιοτήτων της ουράς Η αποθήκευση πληροφοριών και ο καθορισμός των επιθυμητών ιδιοτήτων γίνεται μέσω ειδικής δομής Η χωρητικότητα της ουράς μπορεί να ελεγχθεί ή/και να επαναπροσδιοριστεί μέσω της msgctl Αν η ουρά καταστραφεί, οι επόμενες κλήσεις πάνω στο αναγνωριστικό της ουράς αποτυγχάνουν, με την errno να λαμβάνει την τιμή EIDRM Προγραμματισμός II 5 lalis@inf.uth.gr
Δομή μηνυμάτων struct msg { long mtype; // convention! // data/payload }; Η δομή είναι ενδεικτική (δεν ορίζεται πουθενά) Τα πρώτα sizeof(long) bytes ερμηνεύονται από το λειτουργικό ως ο τύπος του μηνύματος Ο τύπος ενός μηνύματος πρέπει να είναι θετικός Μπορεί να γίνει παραλαβή μηνυμάτων ανάλογα με τον τύπο τους βλέπε παρακάτω Προγραμματισμός II 6 lalis@inf.uth.gr
Αποστολή μηνύματος int msgsnd(int msqid, void *msgp, size_t msize, int flags); Τοποθετεί το μήνυμα msgp στην ουρά msqid Το μέγεθος του μηνύματος επιπλέον των bytes για τον τύπο του μηνύματος, δίνεται μέσω msize Μπλοκάρει αν δεν υπάρχει διαθέσιμος χώρος Αποφυγή μπλοκαρίσματος: IPC_NOWAIT στα flags αν δεν υπάρχει χώρος, η κλήση επιστρέφει άμεσα με τιμή αποτυχίας και την errno ίση με EAGAIN Προγραμματισμός II 7 lalis@inf.uth.gr
Παραλαβή μηνύματος ssize_t msgrcv(int msqid, void *msgp, size_t msize,long mtype,int flags); Απομακρύνει ένα μήνυμα από την ουρά msqid, αποθηκεύει το περιεχόμενο (ως msize bytes) στο msgp, και επιστέφει το πόσα bytes αποθηκεύτηκαν αποτυγχάνει αν το μήνυμα δεν χωράει (errno ίσο E2BIG) εκτός αν στα flags συμπεριληφθεί MSG_NOERROR οπότε αντιγράφονται msize bytes και τα υπόλοιπα χάνονται Το μήνυμα επιλέγεται με βάση το mtype 0: το πρώτο μήνυμα στην ουρά >0: το πρώτο μήνυμα με αυτό το τύπο (ή το πρώτο μήνυμα με διαφορετικό τύπο, αν η flags περιέχει το MSG_EXCEPT) <0: το πρώτο μήνυμα με τον μικρότερο τύπο < mtype Μπλοκάρει αν δεν υπάρχει κατάλληλο μήνυμα εκτός αν στα flags συμπεριληφθεί IPC_NOWAIT, οπότε η κλήση αποτυγχάνει (errno ίσο με ENOMSG) Προγραμματισμός II 8 lalis@inf.uth.gr
(1) P1 P2 msg queue PING... (2) P1 msg queue P2 PONG... Προγραμματισμός ΙΙ 9 lalis@inf.uth.gr
#define PINGMSG 1 #define PONGMSG 2 struct msg { long type; char data[maxdata]; }; int main (int argc, char *argv[] ) { int mqid; struct msg m; } mqid = msgget(ipc_private,s_irwxu); if (!fork()) { msgrcv(mqid,&m,maxdata,pingmsg,0); printf("child got msg: %d:%s\n",m.type,m.data); m.type = PONGMSG; strcpy(m.data,"pong from child"); msgsnd(mqid,&m,strlen(m.data)+1,0); return(0); } m.type = PINGMSG; strcpy(m.data,"ping from parent"); msgsnd(mqid,&m,strlen(m.data)+1,0); msgrcv(mqid,&m,maxdata,pongmsg,0); printf("parent got msg: %d:%s\n",m.type,m.data); msgctl(mqid,ipc_rmid,null); return(0); Προγραμματισμός ΙΙΙ 10 lalis@inf.uth.gr
client queue (1) client server server queue ADD_REQ... (2) client client queue ADD_RES... server queue server Προγραμματισμός ΙΙ 11 lalis@inf.uth.gr
#define REQMSG 1 #define RSPMSG 2 #define REQ_SIZE (sizeof(struct req)-sizeof(long)) #define RSP_SIZE (sizeof(struct rsp)-sizeof(long)) struct req { long type; // REQMSG int mqid; // message queue id to use for reply int a,b; // arguments }; struct rsp { long type; // RSPMSG int res; // result }; Προγραμματισμός ΙΙΙ 12 lalis@inf.uth.gr
int main (int argc, char *argv[] ) { int key,mqid; struct req req; struct rsp rsp; key = ftok(argv[1],0); mqid = msgget(key,ipc_creat S_IRWXU); do { msgrcv(mqid,&req,req_size,0,0); if (req.type!= REQMSG) { continue; } // ignore rsp.type = RSPMSG; rsp.res = req.a + req.b; msgsnd(req.mqid,&rsp,rsp_size,0); } while (rsp.res!= 0); msgctl(mqid,ipc_rmid,null); return(0); } int main (int argc, char *argv[] ) { int key, mqid1,mqid2; struct req req; struct rsp rsp; } key = ftok(argv[1],0); mqid1 = msgget(key,0); mqid2 = msgget(ipc_private,s_irwxu); req.type = REQMSG; req.mqid = mqid2; req.a = atoi(argv[2]); req.b = atoi(argv[3]); msgsnd(mqid1,&req,req_size,0); msgrcv(mqid2,&rsp,rsp_size,rspmsg,0); msgctl(mqid2,ipc_rmid,null); return(0); server client Προγραμματισμός ΙΙΙ 13 lalis@inf.uth.gr