ein Kapitel zurück                                           ein Kapitel weiter

Message Queues sind eine weiter Form von IPC und werden hauptsächlich verwendet um Nachrichten zwischen Prozessen auszutauschen. Message Queues ist ein alte Form der IPC und ist eigentlich nur noch in (vielen) alten UNIX-Programmen zu finden. Der Vorteil von Message Queues ist sicherlich der das sie Nachrichten an einen anderen Prozess schicken können die auch nur für diesen Prozess bestimmt sind.

Dies geschieht in folgenden Schritten.....

  • Wir erzeugen eine Message Queues und erhalten dabei eine Kennungs-ID (msgget)
  • Wir verschicken ein Nachricht mit der Kennungs-ID. (msgsnd) Wobei verschicken heißt das diese Nachricht in der Message Queues eingetragen wird. Message Queues werden in verketteten Listen verwaltet.
  • Ein Prozess empfängt die Nachricht wenn sie mit der Kennungs-ID übereinstimmt. (msgrcv)
  • Wir löschen die Nachricht. (msgctl)

Diese 4 Schritte jetzt mal im Schnelldurchlauf. Nun wollen wir uns die einzelnen 4 Funktionen anschauen mit denen wir dies Realisieren können.

a) Öffnen oder Erzeugen einer Message Queues

Um ein Message Queues zu öffnen oder erzeugen verwendet man die Funktion....

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

int msgget(key_t key, int flag);  

Wenn wir eine neue Message Queues einrichten müssen wir einen sogenannten Schlüssel angeben. Dieser Schlüssel hilft uns z.B. wenn ein anderer Prozess die Kennung - ID ,die die Funktion msgget bei Erfolg zurückliefert , nicht kennt trotzdem auf das Objekt zugreifen mit Hilfe des Schlüssels key. Also alle Prozesse die den Schlüssel key kennen können auf auf das Objekt zugreifen das wir mit msgget erzeugen. Folgende Möglichkeiten haben wir einen Schlüssel zu erzeugen............

  • Mit der Konstante IPC_PRIVATE. Das bedeutet das diesem Objekt kein Schlüssel zugeordnet ist.
  • Wir geben selbst einen Wert eines noch nicht existierenden Schlüssels an. Wichtig ist dabei das sie als flag IPC_CREAT und IPC_EXCL setzen. Denn falls das Objekt schon existiert gibt diese Funktion errno=EEXIST zurück.
  • Wir erzeugen einen Schlüssel mit der Funktion....

    key_t ftok(char *pfadname, char projektbezeichner);  

    Diese Funktion wandelt den Pfadnamen einer existierenden Datei zusammen mit dem Projektbezeichner in einem IPC-Schlüssel vom Typ key_t um. (Mehr dazu unter man ftok)

Wollen wir eine Verbindung mit einem bereits existierenden Objekt aufnehmen müssen wir den gleichen Schlüssel verwenden und dürfen als flag auf keinen Fall IPC_CREAT setzen.

Wenn z.B. mehrere verschieden Prozesse auf ein Objekt zugreifen müssen haben sie folgende Möglichkeiten den Schlüssel für diese Prozesse bekannt zu machen......

  • Sie Speichern diesen in einer Datei
  • Alle Prozesse benutzen die selbe Headerdatei in dem sich dieser Schlüssel befindet.......

    #ifndef _KEY_H_
    #define _KEY_H_
    
    #define UNIVERSAL_KEY 123456
    
    #endif  

b) Senden einer Message

Der nächste Schritt wäre dann das Senden einer Message an die Massage Queues. Dies machen wir mit der Funktion.........

int msgsnd(int kennungs_id, const void *puffer, size_t laenge, int flag);  

Die kennungs_id ist die ID die uns Funktion die msgget zurückgeliefert hat. Für den puffer können sie dann Ihre eigene Message definieren......

struct test_message{
    long mtype;
    char text[100];
}  

Wichtig ist aber das Ihre Message immer einen Massage-Typ (long) und einen Messagestring enthält. Ein weitere wichtige Angabe ist die laenge der Message. Als flag können sie hier IPC_NOWAIT setzen. Ist dieses Flag gesetzt blockiert die Funktion msgsnd nicht. Bei Fehler liefert msgsnd -1 zurück ansonsten 0.

c) Wir empfangen eine Message

Um eine Message aus der Message Queues zu empfangen steht uns die Funktion.....

int msgrcv(int kennungs_id, void *puffer, size_t laenge, long typ, int flag);  

....zur Verfügung. Die kennungs_id ist die Message Queues von der eine Message zu empfangen ist.

puffer ist die Adresse in der sich die Massage und der Message-typ befindet die wir empfangen. Als laenge geben sie an wie viele Bytes unsere Message lang ist. Wird diese laenge überschritten liefert unsere Funktion einen Fehler zurück.

Mit typ legen sie die Art fest wie sie die Message empfangen wollen.....

  • typ==0 - FIFO-Prinzip
  • typ > 0 - Message typ wird empfangen
  • typ < 0 - Massage mit dem kleinsten Wert typ wird empfangen

Auch beim Empfangen können sie das flag IPC_NOWAIT setzen. Damit wird die Funktion msgrsv nicht blockiert.

d) Löschen oder Ändern einer Message Queue

Um eine Message Queue zu löschen oder deren Status abzufragen bzw. zu ändern haben wir die Funktion.......

int msgctl(int kennungs_id, int kommando, struct msqid_ds *puffer);  

...zur Verfügung. Die Bedeutung der kennungs_id ist wie gehabt. Als Kommando legen sie fest was unsere Funktion msgctl machen soll.....

  • IPC_STAT - Status der Massage Queue wird an die Adresse von puffer geschrieben
  • IPC_SET - Ändern der Zugriffsrechte
  • IPC_RMD - Löschen der Message Queue

Wir befassen aber hier nur das Löschen einer Message Queue. Mehr Informationen zum Abfragen des Status und Änderung des Zugriffsrecht finden sie unter man msgctl und in der Headerdatei <sys/ipc.h> finden sie die Struktur sturct msqid_ds ausführlich dokumentiert.

Es ist übrigens wichtig das sie die Message Queues bei Beendigung löschen. Im Gegensatz zu den benannten Pipes in den Kapiteln zuvor bleibt die Message Queue solange erhalten bis sie das System herunterfahren oder mit dem Kommando...

ipcrm  

...von Hand löschen. Wen sie nicht sicher sind ob die Message Queue gelöscht wurde können sie das Kommando.....

ipcs  

...in der Kommandozeile eingeben. Dies gibt Ihnen Auskunft über zur Zeit laufende IPCs. Jetzt wollen wir alle Funktionen der Message Queues mal in der Praxis testen.

/*Download:mes1.c*/
#include <stdio.h> #include <sys/types.h> #include <sys/msg.h> #include <sys/ipc.h> /*Unser Nachricht mit dem Typ*/ struct amsgbuf { long mtype; char mtext[80]; } mq_test_buf; int main() { key_t key; int kennungs_id; int i; key = ftok(".", 'a'); /*'a' kann ein beliebiges Zeichen sein */ printf("key=%d\n", key); /*Wir erstellen ein neue Message Queue*/ kennungs_id = msgget(key, IPC_CREAT | 0666); printf("Message Queue id=%d\n", kennungs_id); if (kennungs_id == -1) { fprintf(stderr,"Fehler beim erstellen einer Message Queue!!\n"); exit(0); } /*Wir geben einen Message-Typ und den Message-Text ein*/ mq_test_buf.mtype = 123; sprintf(mq_test_buf.mtext,"Test Message"); /*Wir schicken die Message an die Message Queue*/ printf("Sende Message...\n"); if (msgsnd(kennungs_id, (struct msgbuf *)&mq_test_buf, sizeof("Test Message") + 1, 0) == -1) { // room for 32 chars in mq_test_buf perror("msgsnd"); printf("Fehler beim senden an die Message Queue\n"); exit(1); } system("ipcs");/*Wir wollen ein paar Infos unserer Message Queue lesen*/ sleep(4); /*Wir vertrödeln ein bißchen Zeit*/ mq_test_buf.mtext[0] = 0; /*Wir löschen den Inhalt von der gesendeten Nachricht*/ /*Wir versuchen eine Nachricht aus der Message Queue zu lesen mit dem Message-Typ 123*/ i = msgrcv(kennungs_id, (struct msgbuf *)&mq_test_buf, 80, 123, IPC_NOWAIT); if (i == -1) printf("Kein Message-Typ mit 123 emfangen\n"); else printf("Habe Message-Typ 123 emfangen mit folgendem Inhalt : %s\n", mq_test_buf.mtext); /*Wir löschen die Message Queue*/ msgctl(kennungs_id, IPC_RMID, 0); }

Da sie jetzt jede Funktion in Ihrer Anwendung kennen wollen wir Message Queues auf 2 Prozessen aufteilen. In einem soll etwas von der Standardeingabe eingegeben und an die Message Queue geschickt werden. In dem anderen Prozess soll die Message aus Massage Queue empfangen werden.

Zuerst verwenden wir eine Gemeinsame Headerdatei in der unser Schlüssel (key_t) definiert wird.........

/*keyid.h*/

#ifndef KEYID
#define KEYID

#define KEY 111111
#define KEY2 111110

#endif  

Jetzt schreiben wir das Programm das aus der Standardeingabe ließt und das als Message an die Message Queue schickt.....

/*Download:mes2.c*/
#include <stdio.h> #include <sys/types.h> #include <sys/msg.h> #include <sys/ipc.h> #include <unistd.h> #include "keyid.h" /*Unser Nachricht mit dem Typ*/ struct amsgbuf { long mtype; char mtext[80]; } mq_test_buf; int main() { int kennungs_id; int i,n; printf("key=%d\n", KEY); /*Wir öffnen die Message Queue*/ kennungs_id = msgget(KEY, 0666); printf("Message Queue id=%d\n", kennungs_id); if (kennungs_id == -1) { fprintf(stderr,"Fehler beim erstellen einer Message Queue!!\n"); exit(0); } printf("Bitte geben sie Ihre Nachricht ein............\n "); /*Wir geben einen Message-Typ und den Message-Text ein*/ mq_test_buf.mtype = 123; while((n=read(STDIN_FILENO, &mq_test_buf.mtext, 80)) > 0) { /*Wir schicken die Message an die Message Queue*/ printf("Sende Message...(%d Bytes)\n",n+1); if (msgsnd(kennungs_id, (struct msgbuf *)&mq_test_buf, n + 1, 0) == -1) { perror("msgsnd"); printf("Fehler beim senden an die Message Queue\n"); exit(1); } printf("Bitte geben sie Ihre Nachricht ein............\n "); mq_test_buf.mtext[0] = 0; } return 0; }

Und nun noch das Programm das aus der Message Queue die Message empfängt......

/*Download:mes3.c*/
#include <stdio.h> #include <sys/types.h> #include <sys/msg.h> #include <sys/ipc.h> #include <unistd.h> #include "keyid.h" /*Unser Nachricht mit dem Typ*/ struct amsgbuf { long mtype; char mtext[80]; } mq_test_buf; int main() { int kennungs_id; int i,n; printf("key=%d\n", KEY); /*Wir erstellen ein neue Message Queue*/ kennungs_id = msgget(KEY, IPC_CREAT | 0666); printf("Message Queue id=%d\n", kennungs_id); if (kennungs_id == -1) { fprintf(stderr,"Fehler beim erstellen einer Message Queue!!\n"); exit(0); } /*Wir versuchen eine Nachricht aus der Message Queue zu lesen mit dem Message-Typ 123*/ while(msgrcv(kennungs_id, (struct msgbuf *)&mq_test_buf, 80, 123, 0)) { printf("Habe Message-Typ 123 emfangen mit folgendem Inhalt : %s\n", mq_test_buf.mtext); mq_test_buf.mtext[0]=0; } /*Wir löschen die Message Queue*/ msgctl(kennungs_id, IPC_RMID, 0); }

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf