Axel Rogat
Betriebssysteme und betriebssystemnahes Programmieren
 
9.2: IPC-Mechanismen Kapitel 9 9.4: Semaphore  
 
  9.3 Message Queues  
 

Es gibt eine Möglichkeit zur indirekten IPC über den Austausch von Nachrichten per Mailbox, die sogenannte Message Queue, die von beliebig vielen Prozessen aus erreichbar ist. Alles, was mit den Message-Operationen möglich ist, läßt sich auch über FIFOs realisieren. Programme werden aber oft übersichtlicher als eine gleichwertige FIFO-Version.

Strukturen, Konstanten und Aufrufe sind in sys/ipc.h und sys/msg.h definiert. Die Nachrichten haben auf Benutzer-Ebene folgenden Aufbau:

struct msgbuf { long mtype; // Nachrichten-Typ, >0 char mtext[1]; // eigentlich variabel langes Array }; Wie lang die Nachricht tatsächlich ist, muß beim Senden explizit angegeben werden. Am besten definiert man eine eigene Struktur, die den zu verschickenden Daten angepaßt ist, und ermittelt mit sizeof ihre Länge.

Es gibt folgenden Satz von Systemaufrufen zu Messages:

UNIX
int msgget(key_t key, int flg);
  get message queue, erfragt die ID einer bestehenden Message-Queue oder legt eine neue an (siehe 9.2)
int msgctl(int qid, int cmd, struct msqid_ds *buf);
  message queue control, dient zum direkten Auslesen oder Einschreiben von Teilen der Message-Queue-Struktur.
Am wichtigsten ist das Kommando cmd=IPC_RMID zum Löschen der Queue.
int msgsnd(int qid, struct msgbuf *p, int sz, int flg);
  message send, sendet eine Nachricht an die durch qid bezeichnete Queue. mp zeigt auf die Daten, sz gibt die tatsächliche Länge an, flg steuert das Verhalten übergroßer Nachrichten. Wenn die Queue voll ist, blockiert der Aufruf, bis sie verkleinert, gelöscht oder der Prozeß unterbrochen wird.
int msgrcv(int qid, struct msgbuf *mp, int sz, long typ, int flg);
  message receive, holt eine Nachricht aus der Queue qid ab (sie wird aus der Queue entfernt).
Es wird folgende Message abgeholt:
  • typ=0: die vorderste (also älteste) Message
  • typ>0: die vorderste Message dieses Typs - es sei denn, MSG_EXCEPT ist in flg gesetzt, dann die vorderste nicht dieses Typs!
  • Wenn IPC_NOWAIT in flg gesetzt ist, wird nicht auf eine passende Nachricht gewartet, sondern mit -1 abgebrochen.

Beispiel: Wir formulieren unser Client-Server-Beispiel aus dem FIFO-Abschnitt für Message Queues um. Wenn wir schon eine Message-Struktur verschicken, arbeiten wir diesmal direkt mit binären Daten.

Wir brauchen nur eine Queue überhaupt. Die Messages enthalten ein Feld mit der PID der Clients, und die Clients sind so nett und holen sich nur Nachrichten ab, die für sie bestimmt sind. Zusätzlich benötigen wir aber ein Kommunikations-File, in dem der Server den Schlüssel der Queue ablegt, die er angelegt hat.

Unsere genaue Message-Struktur und den Namen des Files definieren wir in einer gemeinsamen Header-Datei divq.h:

#define QIDFILE "/tmp/divqid" struct mymsgbuf { long mtype; pid_t pid; unsigned long data; }; Der Client holt sich als erstes die Nummer der Queue aus dem Kommunikationsfile. Danach braucht er nur noch je einen Send- und Receive-Aufruf:
#include "divq.h" int main(int argc, char *argv[]) { int fd,i,qid; struct mymsgbuf msg; pid_t self=getpid(); if ((fd=open(QIDFILE,O_RDONLY))<0) { fputs("server not running\n",stderr); exit(0); } read(fd,&qid,sizeof(qid)); close(fd); for ( i=1 ; i<argc ; ++i ) { long num=abs(atol(argv[i])); msg.mtype=1; msg.pid=self; msg.data=abs(num); if ( msgsnd(qid,(struct msgbuf *)&msg,sizeof(msg),0) || msgrcv(qid,(struct msgbuf *)&msg,sizeof(msg),self,0)==0) { perror("queue error"); exit(0); } if (msg.data) printf("%d is divisible by %d\n",num,msg.data); else printf("%d is prime\n",num); } } Der Server vereinfacht sich ebenfalls ein wenig (die Funktion divisor ist ausgelassen):
#include "divq.h" static int qid; static void sigint_handler(int sig) { if (qid>0) msgctl(qid,IPC_RMID,0); unlink(QIDFILE); exit(0); } int main() { int fd; struct mymsgbuf msg; signal(SIGINT,sigint_handler); if ((qid=msgget(IPC_PRIVATE,0666))<=0 || (fd=open(QIDFILE,O_WRONLY|O_CREAT,0644))<0) { perror("can't open queue\n"); sigint_handler(SIGINT); } write(fd,&qid,sizeof(qid)); close(fd); for (;;) { msgrcv(qid,(struct msgbuf *)&msg,sizeof(msg),1,0); msg.mtype=msg.pid; msg.data=divisor(msg.data); msgsnd(qid,(struct msgbuf *)&msg,sizeof(msg),0); } } Während unser Server läuft, erscheint er auch in der Liste von "ipcs -q", z.B.:
------ Message Queues -------- msqid owner perms used-bytes messages 128 root 700 0 0 2178 axel 666 12 0 Um die Anzeige "12 Bytes" zu erhalten, wurde der Server kurzzeitig mit CTRL-Z gestoppt.

 
9.2: IPC-Mechanismen Startseite 9.4: Semaphore