Ουρές Μηνυμάτων <sys/msg.h>


Η βασική ιδέα της ουράς μηνυμάτων είναι απλή.

Δύο (ή περισσότερες) διεργασίες μπορούν να ανταλλάξουν πληροφορίες μέσω της προπέλασης σε μια κοινή ουρά μηνυμάτων. Η διεργασία αποστολής γράφει ένα μήνυμα στην ουρά μέσω μιας ειδικής συνάρτησης (κλήσης συστήματος). Το μήνυμα αυτό μπορεί να το διαβάσει η διεργασία παραλαβής, πάλι μέσω ειδικής συνάρτησης (Σχήμα 24.1). Κάθε μήνυμα έχει μια ταυτότητα type έτσι ώστε ο  παραλήπτης να μπορεί να επιλέξει μήνυμα. Οι διεργασίες πρέπει να μοιράζονται ένα κοινό κλειδί key ώστε να έχουν πρόσβαση στην ουρά μηνυμάτων (υπάρχουν και μερικοί άλλοι περιορισμοί, όπως θα δούμε).

 

Σχήμα. 24.1 Βασική Ανταλλαγή Μηνυμάτων 

Η ανταλλαγή μηνυμάτων επιτρέπει την αποστολή και παραλαβή μηνυμάτων με αυθαίρετη σειρά. Σε αντίθεση με τις σωληνώσεις (και τις ροές, δηλαδή αρχεία κειμένου ή αντίστοιχες συσκευές) κάθε μήνυμα έχει συγκεκριμένο μήκος και συγκεκριμένο type. Έτσι το σύστημα μπορεί να κατευθύνει τη κυκλοφορία μεταξύ των διεργασιών χρησιμοποιώντας το pid της διεργασίας σαν type

Πριν από την ανταλλαγή μηνυμάτων, η ουρά αρχικοποιείται (μέσω της συνάρτησης msgget). Οι λειτουργίες αποστολής και παραλαβής υλοποιούνται με τις συναρτήσεις msgsnd() και msgrcv(), αντίστοιχα.

Κατά την αποστολή ενός μηνύματος, τα δεδομένα γράφονται στην ουρά. Οι συναρτήσεις msgsnd() και msgrcv() μπορούν να έχουν ανασταλτικό χαρακτήρα ή όχι (σύγχρονη ή ασύγχρονη επικοινωνία). Στην περίπτωση του ανασταλτικού χαρακτήρα (σύγχρονη επικοινωνία) η διεργασία αποστολής δεν μπορεί να συνεχίσει  (ασύγχρονη επικοινωνία) ο αποστολέας δεν περιμένει επιβεβαίωση από τον παραλήπτη. Μια σύχρονη αποστολή σήματος προκαλεί αναστολή της διεργασίας αποστολής μέχρι να συμβεί ένα από τα παρακάτω:

Αρχικοποίηση Ουράς Μηνυμάτων

Η συνάρτηση msgget() αρχικοποιεί μια νέα ουρά μηνυμάτων:

int msgget(key_t key, int msgflg)

Επίσης επιστρέφει τον αριθμό της ουράς (msqid) που αντιστοιχεί στο όρισμα key. Η τιμή του ορίσματος msgflg πρέπει  να είναι ένας οκταδικός αριθμός που ορίζει τις άδειες πρόσβασης και ελέγχου της ουράς.

Ο κώδικας που ακολουθεί επιδεικνύει τη χρήση της συνάρτησης msgget().

#include <sys/ipc.h>; 
#include <sys/msg.h>;

...

key_t key; /* key to be passed to msgget() */
int msgflg /* msgflg to be passed to msgget() */
int msqid; /* return value from msgget() */

...
key = ...
msgflg = ...

if ((msqid = msgget(key, msgflg)) == -1)
{
perror("msgget: msgget failed");
exit(1);
} else
(void) fprintf(stderr, &ldquo;msgget succeeded");
...

Συναρτήσεις, Ορίσματα και Σημαίες IPC <sys/ipc.h>

Η συντομογραφία IPC σημαίνει Inter-Process Communication (Δια-Διεργασιακή Επικοινωνία) και αναφέρεται σε αρκετές μεθόδους επικοινωνίας μεταξύ διεργασιών (σήματα, σωληνώσεις, μηνύματα κλπ). Στις πιο σύνθετες μορφές δια-διεργασιακής επικοινωνίας απαιτείται ταυτοποίηση της μορφής επικοινωνίας. Η ταυτοποίηση επιτυγχάνεται μέσω του ορίσματος key του τύπου key_t key . (Ο τύπος key_t είναι στην ουσία int και ορίζεται στο <sys/types.h>)

Το key έχει μια αυθαίρετη τιμή που συνήθως δημιουργείται κατά την εκτέλεση. Μια μέθοδος δημιουργίας είναι η χρήση της συνάρτησης  ftok(), που μετατρέπει ένα όνομα διαδρομής (όνομα αρχείου) σε μια τιμή κλειδιού μοναδική μέσα στο σύστημα. Γενικά οι συναρτήσεις που αρχικοποιούν στιγμιότυπα σύνθετωνIPC μεθόδων επιστρέφουν  έναν ακέραιο αριθμό (ID του στιγμιοτύπου της IPC μεθόδου). Κατόπιν, οι συναρτήσεις που χρησιμοποιούν το συγκεκριμένο στιγμιότυπο της IPC μεθόδου επικαλούνται αυτό το ID. 

Αν το όρισμα msgflg οριστεί ως IPC_PRIVATE, η κλήση αρχικοποιεί ένα νέο στιγμιότυπο της IPC μεθόδου που είναι ιδιωτικό για τη καλούσα διεργασία. Όταν το όρισμα msgflg περιλαμβάνει τη σημαία IPC_CREAT, η συνάρτηση προσπαθεί να δημιουργήσει ένα στιγιότυπο της μεθόδου, αν δεν υπάρχει ήδη. Όταν το όρισμα περιλαμβάνει και τις δύο σημαίες,  IPC_CREAT και IPC_EXCL, τότε η συνάρτηση αποτυγχάνει στη περίπτωση που ήδη υπάρχει στιγμιότυπο της μεθόδου. Αυτή η τεχνική ίσως είναι χρήσιμη αν πολλές διεργασίες προσπαθούν ταυτόχρονα να αρχικοποιήσουν ένα στιγμιότυπο της μεθόδου. Σε αυτή τη περίπτωση πετυχαίνει μόνο η πρώτη προσπάθεια. 

Σε περίπτωση που δεν δίνεται κανένα από τα παραπάνω ορίσματα, τότε η συνάρτηση επιστρέφει στη καλούσα συνάρτηση το ID του στιγμιοτύπου της μεθόδου. Αν η σημαία IPC_CREAT δεν υπάρχει στη κλήση, αλλά το στιγμιότυπο της μεθόδου δεν έχει προηγούμενα αρχικοποιηθεί, τότε η κλήση της συνάρτησης αποτυχγάνει. Οι σημαίες συνδυάζονται με το λογικό (bitwise) OR. Στην ίδια σύνθεση περιλαμβάνεται και ο ορισμός των δικαιωμάτων πρόσβασης στην μέθοδο, με τη γνωστή μορφή των οκταδικών αριθμών αδειοδότησης αρχείων. Για παράδειγμα, η παρακάτω κλήση αρχικοποιεί μια ουρά μηνυμάτων αν αυτή δεν υπάρχει ήδη.

msqid = msgget(ftok("/tmp", key), (IPC_CREAT | IPC_EXCL | 0400));

Το πρώτο όρισμα υπολογίζει ένα key βασιζόμενο σε ένα όνομα διαδρομής ("/tmp"). Το δέυτερο όρισμα σημιουργεί μια μάσκα από το συνδυασμό των επιμέρους σημαιών και των δικαιωμάτων πρόσβασης.

Έλεγχος Ουράς Μηνυμάτων

Η συνάρτηση msgctl() επιτρέπει στον ιδιοκτήτη ή το δημιουργό μιας ουράς μηνυμάτων (ή σε όποια διεργασία έχει τα κατάλληλα δικαιώματα) να τροποποιεί τα δικαιώματα πρόσβασης και άλλα χαρακτηριστικά της. 

Η συνάρτηση msgctl() ορίζεται ως εξής:

int msgctl(int msqid, int cmd, struct msqid_ds *buf )

Το όρισμα msqid πρέπει να είναι το ID μιας υπάρχουσας ουράς. Το όρισμα cmd είναι ένα από τα:

IPC_STAT
-- Επιστρέφει πληροφορίες για τη κατάσταση της ουράς στη δομή που δείχνει ο  buf. Η καλούσα διεργασία πρέπει να έχει δικαίωμα ανάγνωσης.
IPC_SET
-- Ρυθμίζει το ΙD του ιδιοκτήτη και της ομάδας, τα δικαιώματα και το μέγεθος της ουρα (σε bytes). Η καλούσα διεργασία πρέπει να έχει δικαίωμα ιδιοκτήτη ή διαχειριστή.
IPC_RMID
-- Διαγραφή της ουράς που ορίζεται από το όρισμα msqid. Η καλούσα διεργασία πρέπει να έχει δικαίωμα ιδιοκτήτη ή διαχειριστή.

Το παράδειγμα που ακολουθεί εξηγεί τη χρήση της συνάρτησης msgctl():

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
...
if (msgctl(msqid, IPC_STAT, &buf) == -1) {
perror("msgctl: msgctl failed");
exit(1);
}
...
if (msgctl(msqid, IPC_SET, &buf) == -1) {
perror("msgctl: msgctl failed");
exit(1);
}
...

Αποστολή και Παραλαβή Μηνυμάτων

Οι συναρτήσεις msgsnd() και msgrcv() στέλνουν και λαμβάνουν μηνύματα, αντίστοιχα:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtype, int msgflg);

Το όρισμα msqid πρέπει να είναι το ID μιας υπάρχουσας ουράς. Το όρισμα msgp είναι δείκτης σε μιά δομή που περιέχει το μήνυμα, όπως στο παράδειγμα που ακολουθεί:

 struct mymsg {
long int msgtype; /* message type */
char mtext[1]; /* message text of length 1 */
}

Το όρισμα msgsz ορίζει το μέγεθος του μηνύματος σε bytes.

Το όρισμα msgtype του παραλήπτη είναι ίδιο με το μέλος msgtype της δομής mymsg του αποστολέα.

Το όρισμα msgflg ορίζει τις λειτουργίες που πρέπει να κάνει η συνάρτηση άν ισχύει μια από τις παρακάτω συνθήκες:

Οι λειτουργίες είναι οι παρακάτω:

O κώδικας που ακολουθεί εξηγεί τη χρήση των συναρτήσεων msgsnd() και msgrcv():

#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/msg.h>

...

int msgflg; /* message flags for the operation */
struct msgbuf *msgp; /* pointer to the message buffer */
int msgsz; /* message size */
long int msgtyp; /* desired message type */
int msqid /* message queue ID to be used */

...

msgp = (struct msgbuf *)malloc((unsigned int )(sizeof(struct msgbuf) - sizeof(msgp->mtext) + maxmsgsz));

if (msgp == NULL) {
(void) fprintf(stderr, "msgop: %s %d byte messages.\n", "could not allocate message buffer for", maxmsgsz);
exit(1);

...

msgsz = ...
msgflg = ...

if (msgsnd(msqid, msgp, msgsz, msgflg) == -1)
perror("msgop: msgsnd failed");
...

msgsz = ...
msgtyp = first_on_queue;
msgflg = ...

if (rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) == -1)
perror("msgop: msgrcv failed");
...

Μηνύματα POSIX <mqueue.h>

Η παραπάνω περιγραφή αναφέρεται στις πρότυπες βιβλιοθήκες της C με συμβατότητα Unix System V. Το πρότυπο POSIX ορίζει αντίστοιχες ουρές μηνυμάτων. Οι σχετικές συναρτήσεις υπάρχουν στη βιβλιοθήκη mqueue.h.  Για περισσότερες λεπτομέρεις δείτε στο 

man 7 mq_overview

Παρακάτω παρουσιάζονται σύντομα, σε αντιστοιχία με τη προηγούμενη συζήτηση.

    mq_open() -- Συνδέεται, ή δημιουργεί, μια ονοματισμένη ουρά μηνυμάτων.

mqd_t mq_open(const char *name, int oflag);

όπου mqd_t ο προκαθορισμένος τύπος περιγραφέα ουράς, name δείκτης στο όνομα της ουράς, oflag η μάσκα που αντιστοιχεί στο msgflag. Αν έχει τιμή O_CREAT τότε έχουμε δημιουργία ουράς. Ο συνδυασμός τιμών στη μάσκα ορίζει και τα δικαιώματα πρόσβασης. Σε περίπτωση επιτυχίας επιστρέφεται ένας περιγραφέας ουράς.

    mq_close() -- Τερματίζει τη σύνδεση με μια ανοικτή ουρά.

mqd_t mq_close(mqd_t mqdes);

όπου mqdes ο περιγραφέας της ανοικτής ουράς.

    mq_unlink() -- Τερματίζει τη σύνδεση σε μια ανοικτή ουρά μηνυμάτων και διαγράφει την ουρά μόλις και η τελευταία διεργασία τερματίσει τη σύνδεσή της  (η καλούσα διεργασία πρέπει να έχει δικαιώματα ιδιοκτήτη η διαχειριστή).

mqd_t mq_unlink(const char *name);

όπου name δείκτης στο όνομα της ουράς. 

    mq_send() -- Αποστολή (εγγγραφή) μηνύματος σε ουρά. 

mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);

όπου msg_ptr δείκτης στο κείμενο του μηνύματος, msg_len το μήκος  του μηνύματος σε site_t (πρέπει να είναι μικρότερο από το προκαθορισμένο mq_msgsize) και msg_prio η προτεραιότητα του μηνύματος. Η εκτέλεση της συνάρτησης ακολουθεί τις συμβάσεις που συζητήθηκαν παραπάνω (ανασταλτική ή μη- λειτουργία, πιθανά σφάλματα κλπ.). Επίσης υπάρχει και σχετική συνάρτηση για χρονικά περιορισμένη εκτέλεση της συνάρτησης.

    mq_receive() -- Παραλαβή (διαγραφή) του παλαιότερου, με υψηλότερη προτεραιότητα μηνύματος από την ουρά.

mqd_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);

με συμπεριφορά και επιλογές αντίστοιχα της συνάρτησης αποστολής.

    mq_notify() -- Ενημερώνει μια διεργασία οτι ένα μήνυμα είναι διαθέσιμο στην ουρά.

mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification);

όπου η δομή sigevent καθορίζει τον τύπο της ενημέρωσης.

    mq_setattr() -- Θέτει τις ιδιότητες της ουράς.

mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); 

    mq_getattr() -- Επιστρέφει ιδιότητες της ουράς.

mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);

με τη δομή mq_attr να έχει τη μορφή
struct mq_attr {
long mq_flags; /* Flags: 0 or O_NONBLOCK */
long mq_maxmsg; /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue */
};

Παράδειγμα -- μεταβίβαση μηνυμάτων μεταξύ διεργασιών

Τα δύο προγράμματα που ακολουθούν πρέπει να εκτελεστούν ταυτόχρονα (σε διαφοερτικά παράθυρα τερματικών) ώστε να επιτευχθεί η ανταλλαγή μηνυμάτων.

message_send.c
-- Δημιουργεί μια ουρά μηνυμάτων και στέλνει ένα μήνυμα στην ουρά.
message_rec.c
-- Παραλαμβάνει ένα μήνυμα από την ουρά.

message_send.c -- δημιουργία ουράς και αποστολή μηνύματος

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

#define MSGSZ 128


/*
* Declare the message structure.
*/

typedef struct msgbuf {
long mtype;
char mtext[MSGSZ];
} message_buf;

main()
{
int msqid;
int msgflg = IPC_CREAT | 0666;
key_t key;
message_buf sbuf;
size_t buf_length;

/*
* Get the message queue id for the
* "name" 1234, which was created by
* the server.
*/
key = 1234;

(void) fprintf(stderr, "\nmsgget: Calling msgget(%#lx,\%#o)\n",key, msgflg);

if ((msqid = msgget(key, msgflg )) < 0) {
perror("msgget");
exit(1);
}
else
(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);


/*
* We'll send message type 1
*/

sbuf.mtype = 1;
(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);
(void) strcpy(sbuf.mtext, "Did you get this?");
(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);
buf_length = strlen(sbuf.mtext) ;

/*
* Send a message.
*/
if (msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT) < 0) {
printf ("%d, %d, %s, %d\n", msqid, sbuf.mtype, sbuf.mtext, buf_length);
perror("msgsnd");
exit(1);
}

else
printf("Message: \"%s\" Sent\n", sbuf.mtext);

exit(0);
}

Ορισμένα σημαντικά σημεία:

message_rec.c -- παραλαβή μηνύματος

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

#define MSGSZ 128


/*
* Declare the message structure.
*/

typedef struct msgbuf {
long mtype;
char mtext[MSGSZ];
} message_buf;


main()
{
int msqid;
key_t key;
message_buf rbuf;

/*
* Get the message queue id for the
* "name" 1234, which was created by
* the server.
*/
key = 1234;

if ((msqid = msgget(key, 0666)) < 0) {
perror("msgget");
exit(1);
}


/*
* Receive an answer of message type 1.
*/
if (msgrcv(msqid, &rbuf, MSGSZ, 1, 0) < 0) {
perror("msgrcv");
exit(1);
}

/*
* Print the answer.
*/
printf("%s\n", rbuf.mtext);
exit(0);
}

Ορισμένα σημαντικά σημεία:

Ακόμη μερικά παραδείγματα

Τα παρακω προγράμματα βοηθούν στη διερεύνηση ζητημάτων σχετικών με τη μεταβίβαση μηνυμάτων. Η ουρά μηνυμάτων πρέπει να αρχικοποιηθεί με το πρόγραμμα msgget.c.  Ο έλεγχος της ουράς και η μεταβίβαση μηνυμάτων επιτυγχάνεται με τα προγράμματα msgctl.c και msgop.c .

msgget.c-- επίδειξη της συνάρτησης msget()

/*
* msgget.c: Illustrate the msgget() function.
* This is a simple exerciser of the msgget() function. It prompts
* for the arguments, makes the call, and reports the results.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

extern void exit();
extern void perror();

main()
{
key_t key; /* key to be passed to msgget() */
int msgflg, /* msgflg to be passed to msgget() */
msqid; /* return value from msgget() */

(void) fprintf(stderr, "All numeric input is expected to follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);
(void) fprintf(stderr, "Enter key: ");
(void) scanf("%li", &key);
(void) fprintf(stderr, "\nExpected flags for msgflg argument are:\n");
(void) fprintf(stderr, "\tIPC_EXCL =\t%#8.8o\n", IPC_EXCL);
(void) fprintf(stderr, "\tIPC_CREAT =\t%#8.8o\n", IPC_CREAT);
(void) fprintf(stderr, "\towner read =\t%#8.8o\n", 0400);
(void) fprintf(stderr, "\towner write =\t%#8.8o\n", 0200);
(void) fprintf(stderr, "\tgroup read =\t%#8.8o\n", 040);
(void) fprintf(stderr, "\tgroup write =\t%#8.8o\n", 020);
(void) fprintf(stderr, "\tother read =\t%#8.8o\n", 04);
(void) fprintf(stderr, "\tother write =\t%#8.8o\n", 02);
(void) fprintf(stderr, "Enter msgflg value: ");
(void) scanf("%i", &msgflg);

(void) fprintf(stderr, "\nmsgget: Calling msgget(%#lx,%#o)\n", key, msgflg);
if ((msqid = msgget(key, msgflg)) == -1)
{
perror("msgget: msgget failed");
exit(1);
} else {
(void) fprintf(stderr, "msgget: msgget succeeded: msqid = %d\n", msqid);
exit(0);
}
}

msgctl.c-- επίδειξη της συνάρτησης msgctl()

/*
* msgctl.c: Illustrate the msgctl() function.
*
* This is a simple exerciser of the msgctl() function. It allows
* you to perform one control operation on one message queue. It
* gives up immediately if any control operation fails, so be careful
* not to set permissions to preclude read permission; you won't be
* able to reset the permissions with this code if you do.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <time.h>

static void do_msgctl();
extern void exit();
extern void perror();
static char warning_message[] = "If you remove read permission for \
yourself, this program will fail frequently!";

main()
{
struct msqid_ds buf; /* queue descriptor buffer for IPC_STAT and IP_SET commands */
int cmd, /* command to be given to msgctl() */
msqid; /* queue ID to be given to msgctl() */

(void fprintf(stderr, "All numeric input is expected to follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");

/* Get the msqid and cmd arguments for the msgctl() call. */
(void) fprintf(stderr, "Please enter arguments for msgctls() as requested.");
(void) fprintf(stderr, "\nEnter the msqid: ");
(void) scanf("%i", &msqid);
(void) fprintf(stderr, "\tIPC_RMID = %d\n", IPC_RMID);
(void) fprintf(stderr, "\tIPC_SET = %d\n", IPC_SET);
(void) fprintf(stderr, "\tIPC_STAT = %d\n", IPC_STAT);
(void) fprintf(stderr, "\nEnter the value for the command: ");
(void) scanf("%i", &cmd);

switch (cmd) {
case IPC_SET:
/* Modify settings in the message queue control structure. */
(void) fprintf(stderr, "Before IPC_SET, get current values:");
/* fall through to IPC_STAT processing */
case IPC_STAT:
/* Get a copy of the current message queue control
* structure and show it to the user. */
do_msgctl(msqid, IPC_STAT, &buf);
(void) fprintf(stderr, "msg_perm.uid = %d\n", buf.msg_perm.uid);
(void) fprintf(stderr, "msg_perm.gid = %d\n", buf.msg_perm.gid);
(void) fprintf(stderr, "msg_perm.cuid = %d\n", buf.msg_perm.cuid);
(void) fprintf(stderr, "msg_perm.cgid = %d\n", buf.msg_perm.cgid);
(void) fprintf(stderr, "msg_perm.mode = %#o, ", buf.msg_perm.mode);
(void) fprintf(stderr, "access permissions = %#o\n", buf.msg_perm.mode & 0777);
(void) fprintf(stderr, "msg_cbytes = %d\n", buf.msg_cbytes);
(void) fprintf(stderr, "msg_qbytes = %d\n", buf.msg_qbytes);
(void) fprintf(stderr, "msg_qnum = %d\n", buf.msg_qnum);
(void) fprintf(stderr, "msg_lspid = %d\n", buf.msg_lspid);
(void) fprintf(stderr, "msg_lrpid = %d\n", buf.msg_lrpid);
(void) fprintf(stderr, "msg_stime = %s", buf.msg_stime ? ctime(&buf.msg_stime) : "Not Set\n");
(void) fprintf(stderr, "msg_rtime = %s", buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not Set\n");
(void) fprintf(stderr, "msg_ctime = %s", ctime(&buf.msg_ctime));
if (cmd == IPC_STAT)
break;
/* Now continue with IPC_SET. */
(void) fprintf(stderr, "Enter msg_perm.uid: ");
(void) scanf ("%hi", &buf.msg_perm.uid);
(void) fprintf(stderr, "Enter msg_perm.gid: ");
(void) scanf("%hi", &buf.msg_perm.gid);
(void) fprintf(stderr, "%s\n", warning_message);
(void) fprintf(stderr, "Enter msg_perm.mode: ");
(void) scanf("%hi", &buf.msg_perm.mode);
(void) fprintf(stderr, "Enter msg_qbytes: ");
(void) scanf("%hi", &buf.msg_qbytes);
do_msgctl(msqid, IPC_SET, &buf);
break;
case IPC_RMID:
default:
/* Remove the message queue or try an unknown command. */
do_msgctl(msqid, cmd, (struct msqid_ds *)NULL);
break;
}
exit(0);
}

/*
* Print indication of arguments being passed to msgctl(), call
* msgctl(), and report the results. If msgctl() fails, do not
* return; this example doesn't deal with errors, it just reports
* them.
*/
static void
do_msgctl(msqid, cmd, buf)
struct msqid_ds *buf; /* pointer to queue descriptor buffer */
int cmd, /* command code */
msqid; /* queue ID */
{
register int rtrn; /* hold area for return value from msgctl() */

(void) fprintf(stderr, "\nmsgctl: Calling msgctl(%d, %d, %s)\n",
msqid, cmd, buf ? "&buf" : "(struct msqid_ds *)NULL");
rtrn = msgctl(msqid, cmd, buf);
if (rtrn == -1) {
perror("msgctl: msgctl failed");
exit(1);
} else {
(void) fprintf(stderr, "msgctl: msgctl returned %d\n", rtrn);
}
}

msgop.c -- επίδειξη των συναρτήσεων msgsnd() και msgrcv()

/*
* msgop.c: Illustrate the msgsnd() and msgrcv() functions.
*
* This is a simple exerciser of the message send and receive
* routines. It allows the user to attempt to send and receive as many
* messages as wanted to or from one message queue.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

static int ask();
extern void exit();
extern char *malloc();
extern void perror();

char first_on_queue[] = "-> first message on queue",
full_buf[] = "Message buffer overflow. Extra message tetx is discarded.";

main()
{
register int c; /* message text input */
int choice; /* user's selected operation code */
register int i; /* loop control for mtext */
int msgflg; /* message flags for the operation */
struct msgbuf *msgp; /* pointer to the message buffer */
int msgsz; /* message size */
long msgtyp; /* desired message type */
int msqid, /* message queue ID to be used */
maxmsgsz, /* size of allocated message buffer */
rtrn; /* return value from msgrcv or msgsnd */

(void) fprintf(stderr, "All numeric input is expected to follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
/* Get the message queue ID and set up the message buffer. */

(void) fprintf(stderr, "Enter msqid: ");
(void) scanf("%i", &msqid);

/*
* Note that <sys/msg.h> includes a definition of struct msgbuf
* with the mtext field defined as:
* char mtext[1];
* therefore, this definition is only a template, not a structure
* definition that you can use directly, unless you want only to
* send and receive messages of 0 or 1 byte. To handle this,
* malloc an area big enough to contain the template - the size
* of the mtext template field + the size of the mtext field
* wanted. Then you can use the pointer returned by malloc as a
* struct msgbuf with an mtext field of the size you want. Note
* also that sizeof msgp->mtext is valid even though msgp isn't
* pointing to anything yet. Sizeof doesn't dereference msgp, but
* uses its type to figure out what you are asking about.
*/
(void) fprintf(stderr, "Enter the message buffer size you want:");
(void) scanf("%i", &maxmsgsz);
if (maxmsgsz < 0) {
(void) fprintf(stderr, "msgop: %s\n", "The message buffer size must be >= 0.");
exit(1);
}
msgp = (struct msgbuf *)malloc((unsigned)(sizeof(struct msgbuf) - sizeof msgp->mtext + maxmsgsz));
if (msgp == NULL) {
(void) fprintf(stderr, "msgop: %s %d byte messages.\n", "could not allocate message buffer for", maxmsgsz);
exit(1);
}
/* Loop through message operations until the user is ready to
quit. */
while (choice = ask()) {
switch (choice) {
case 1: /* msgsnd() requested: Get the arguments, make the call, and report the results. */
(void) fprintf(stderr, "Valid msgsnd message %s\n", "types are positive integers.");
(void) fprintf(stderr, "Enter msgp->mtype: ");
(void) scanf("%li", &msgp->mtype);
if (maxmsgsz) {
/* Since you've been using scanf, you need the loop
below to throw away the rest of the input on the
line after the entered mtype before you start
reading the mtext. */
while ((c = getchar()) != '\n' && c != EOF);
(void) fprintf(stderr, "Enter a %s:\n", "one line message");
for (i = 0; ((c = getchar()) != '\n'); i++) {
if (i >= maxmsgsz) {
(void) fprintf(stderr, "\n%s\n", full_buf);
while ((c = getchar()) != '\n');
break;
}
msgp->mtext[i] = c;
}
msgsz = i;
} else
msgsz = 0;
(void) fprintf(stderr,"\nMeaningful msgsnd flag is:\n");
(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#8.8o\n",
IPC_NOWAIT);
(void) fprintf(stderr, "Enter msgflg: ");
(void) scanf("%i", &msgflg);
(void) fprintf(stderr, "%s(%d, msgp, %d, %#o)\n", "msgop: Calling msgsnd", msqid, msgsz, msgflg);
(void) fprintf(stderr, "msgp->mtype = %ld\n", msgp->mtype);
(void) fprintf(stderr, "msgp->mtext = \"");
for (i = 0; i < msgsz; i++)
(void) fputc(msgp->mtext[i], stderr);
(void) fprintf(stderr, "\"\n");
rtrn = msgsnd(msqid, msgp, msgsz, msgflg);
if (rtrn == -1)
perror("msgop: msgsnd failed");
else
(void) fprintf(stderr, "msgop: msgsnd returned %d\n", rtrn);
break;
case 2: /* msgrcv() requested: Get the arguments, make the call, and report the results. */
for (msgsz = -1; msgsz < 0 || msgsz > maxmsgsz;
(void) scanf("%i", &msgsz))
(void) fprintf(stderr, "%s (0 <= msgsz <= %d): ", "Enter msgsz", maxmsgsz);
(void) fprintf(stderr, "msgtyp meanings:\n");
(void) fprintf(stderr, "\t 0 %s\n", first_on_queue);
(void) fprintf(stderr, "\t>0 %s of given type\n", first_on_queue);
(void) fprintf(stderr, "\t<0 %s with type <= |msgtyp|\n", first_on_queue);
(void) fprintf(stderr, "Enter msgtyp: ");
(void) scanf("%li", &msgtyp);
(void) fprintf(stderr, "Meaningful msgrcv flags are:\n");
(void) fprintf(stderr, "\tMSG_NOERROR =\t%#8.8o\n", MSG_NOERROR);
(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#8.8o\n", IPC_NOWAIT);
(void) fprintf(stderr, "Enter msgflg: ");
(void) scanf("%i", &msgflg);
(void) fprintf(stderr, "%s(%d, msgp, %d, %ld, %#o);\n", "msgop: Calling msgrcv", msqid, msgsz, msgtyp, msgflg);
rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
if (rtrn == -1)
perror("msgop: msgrcv failed");
else {
(void) fprintf(stderr, "msgop: %s %d\n",
"msgrcv returned", rtrn);
(void) fprintf(stderr, "msgp->mtype = %ld\n",
msgp->mtype);
(void) fprintf(stderr, "msgp->mtext is: \"");
for (i = 0; i < rtrn; i++)
(void) fputc(msgp->mtext[i], stderr);
(void) fprintf(stderr, "\"\n");
}
break;
default:
(void) fprintf(stderr, "msgop: operation unknown\n");
break;
}
}
exit(0);
}

/*
* Ask the user what to do next. Return the user's choice code.
* Don't return until the user selects a valid choice.
*/
static
ask()
{
int response; /* User's response. */

do {
(void) fprintf(stderr, "Your options are:\n");
(void) fprintf(stderr, "\tExit =\t0 or Control-D\n");
(void) fprintf(stderr, "\tmsgsnd =\t1\n");
(void) fprintf(stderr, "\tmsgrcv =\t2\n");
(void) fprintf(stderr, "Enter your choice: ");

/* Preset response so "^D" will be interpreted as exit. */
response = 0;
(void) scanf("%i", &response);
} while (response < 0 || response > 2);

return(response);
}

Ασκήσεις

Άσκηση 12755

Γράψτε δύο προγράμματα που θα ανταλλάσουν μεταξ'υ τους τα παρακάτω μηνύματα

Άσκηση 12756

Μεταγλωττίστε τα προγράμματα msgget.c, msgctl.c και msgop.c. Στη συνέχεια

Άσκηση 12757

Γράψτε ένα πρόγραμμα διακομιστή και δύο προγράμματα πελάτη έτσι ώστε ο διακομιστής να επικοινωνεί με κάθε πελάτη  "ιδιωτικά" μέσω μιας μόνο ουράς.

Άσκηση 12758

Υλοποιήστε τη σύγχρονη ή ανασταλτική μέθοδο ανταλλαγής μυνημάτων με τη βοήθεια σημάτων.


Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
25/3/2008