ein Kapitel zurück                                           ein Kapitel weiter

Mit den Pipes die wir bisher verwendet haben, konnten wir nur mit den Prozessen kommunizieren, die miteinander Verwandt waren. Also nur zwischen geforkten Prozessen. Mit FIFO's (benannte Pipes) haben wir nun die Möglichkeit mit einem völlig fremden Prozeß zu kommunizieren (Daten austauschen).

Intern (wenn man ein bißchen den Kernel betrachtet) ist eine FIFO nichts anderes als eine tatsächliche Implementierung einer Pipe, wie wir sie im Kapitel zuvor kennengelernt haben. Der Systemaufruf mkfifo bedeutet also nichts anderes, das ein Pipe als Filesystem-Objekt repräsentiert wird.

Auf der Shell läßt sich ein FIFO folgendermaßen erstellen.....

mkfifo fifo1  

Nun befindet sich in Ihrem Verzeichnis ein FIFO. Sie können jetzt in die FIFO etwas schreiben..........

echo hallo welt > fifo1  

...und aus dieser FIFO wieder lesen......

cat fifo1  

Natürlich müssen sie auch die Zugriffrechte der FIFO vergeben, wer in diese FIFO schreiben darf und wer aus Ihr lesen soll. Man kann in einer FIFO nun mit mehreren Fenstern (Prozessen) etwas reinschreiben und anderesrum können wir mehrere zugleich auslesen lassen.

FIFO's sind auch eine Halbduplex IPC, was bedeutet das auch mit FIFO's, wie schon bei den Pipes, kein mehrfaches Auslesen möglich ist.

Die FIFOs oder das FIFO arbeiten ebenfalls wie die Pipes nach dem first in - first out Prinzip. Das heißt die Daten die zuerst geschrieben werden werden auch wieder als erstes Ausgegeben. Mit FIFOs werden häufig die typischen Client/Server-Anwendungen geschrieben...............




An diesem Beispiel kann einen typischen Anwendungsfall erkennen. 4 Clients, in diesem Fall Uwe, Otto, Franz und Egon wollen auf 2 Drucker zugreifen. Alle 4 Clients schicken nun eine Schreibanforderung an den Drucker. Diese Schreibaufforderung wird zuerst von unserem FIFO angenommen und der Reihe nach wie die Anforderungen eingetroffen sind sortiert. Der Server ließt nun aus dieser FIFO und Bearbeitet diese Anforderungen und leitet diese an den Entsprechenden Drucker weiter. Wenn jetzt aber z.B. Uwe und Egon ein 100 Seiten starkes Skript ausdrucken lassen sollte der Server natürlich an Otto und Franz eine Meldung zurückschicken das sich der Auftrag in der Warteschlange befindet und momentan keine Drucker mehr frei ist (ganz so wie bei den teuren 0190-Service Nummern ;)) Der Server schickt also ein Antwort an den Client. Entweder eine Bestätigung des Auftrags oder eine Warteanforderung............




Hier kann man erkennen das der Server in eine FIFO schreibt und die Clients diesmal aus der FIFO lesen. Dies soll erst mal veranschaulichen worum es bei FIFOs geht.

Nun aber zu den Grundlagen wie FIFOs überhaupt in C zu bewerkstelligen sind. Diese Art von FIFOs sind übrigens nicht gleichzusetzen mit den FIFOs die wir mit doppelt verketteter Listen erstellt habe. Das Prinzip (first in - first out) ist zwar dasselbe aber FIFOs sind keine normalen Dateien.

Hier der Syntax um eine FIFO anzulegen..........

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pfadname, mode_t mode);  

Bei Erfolg bekommen wir 0 zurück und bei Fehler -1. Als ersten Parameter geben wir den Pfad an wo wir diese FIFO anlegen wollen. Als 2.Parameter geben wir den Modus an.

Wollen wir uns das ganze mal in Praxis ansehen und eine FIFO erstellen.......

/*Download:fifo1.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { /*Wir erzeugen eine FIFO*/ if((mkfifo("fifo0001", O_RDWR)) == -1) { fprintf(stderr, "Konnte keine FIFO erzeugen.........\n"); exit(0); } return 0; }

Wenn sie jetzt ls -l eingeben dürften sie im Verzeichnis folgenden Eintrag finden...............

p---------    1    tot    tot    0  Jun  9  11:33    fifo0001|  

An dem p können sie erkennen das es bei dieser Datei um eine Pipe oder FIFO handelt. Es lässt sich auch am Dateinamen erkennen fifo0001|

Nun wollen wir doch mal stat zur Überprüfung der Dateiart auf die FIFO loslassen............

/*Download:fifo2.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { struct stat attribut; unlink("fifo0001"); /*Falls fifo0001 schon vorhanden ist*/ /*Wir erzeugen eine FIFO*/ if((mkfifo("fifo0001", O_RDWR)) == -1) { fprintf(stderr, "Konnte keine FIFO erzeugen.........\n"); exit(0); } if(stat("fifo0001", &attribut) == -1) { fprintf(stderr, "Fehler bei fstat............\n"); exit(0); } if(S_ISREG(attribut.st_mode)) printf("fifo0001 ist eine reguläre Datei\n"); else if(S_ISDIR(attribut.st_mode)) printf("fifo0001 ist ein Verzeichnis\n"); #ifdef S_ISSOCK else if(S_ISSOCK(attribut.st_mode)) printf("fifo0001 ist ein Socket\n"); #endif else if(S_ISFIFO(attribut.st_mode)) printf("fifo0001 ist eine FIFO oder Pipe\n"); else printf("Kann Dateiart nicht feststellen\n"); return 0; }

Spätestens ab jetzt dürfte Ihnen klar geworden sein das eine FIFO kein normale Datei ist sondern eben eine weitere Dateiart!

So nun wollen wir mal etwas in die FIFO schreiben und wieder auslesen..............

/*Download:fifo3.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd_fifo; /*handle for FIFO*/ char puffer[]="This text is for the fifo\n"; char buf[100]; unlink("/tmp/fifo0001.1"); /*We create a FIFO*/ if((mkfifo("/tmp/fifo0001.1", O_RDWR)) == -1) { fprintf(stderr, "Can't creat a fifo.........\n"); exit(0); } /*We open the fifo for read and write*/ if((fd_fifo=open("/tmp/fifo0001.1", O_RDWR)) == - 1) { fprintf(stderr, "Can't open the fifo.....\n"); exit(0); } write(fd_fifo,puffer,strlen(puffer)) ; if(read(fd_fifo, &buf, sizeof(buf)) == -1) fprintf(stderr, "Error! can't read from FIFO.......\n"); else printf("read now from FIFO : %s\n",buf); return 0; }

So nun erstellen wir eine FIFO mit mkfifo, öffnen diese mit open zum Lesen und Schreiben. Anschließend schreiben wir etwas in die FIFO (write) und lesen dies anschließend gleich wieder aus (read). Man kann an diesem Programm sehen das bei FIFOs wie eine normale Datei, die elementaren E/A-Funktionen oder aber auch die Standard E/A-Funktionen verwendet werden können.

Wie weiter oben schon erwähnt sollten sie bei einer FIFO die Zugriffsrechte festlegen beim Erstellen der FIFO.

Es kann sein das auf Ihrem System die Funktion mkfifo() nicht vorhanden ist. In diesem Fall müssen sie die Funktion............

int mknod(char *pfadname, int mode, int dev);  

...verwenden. Als Pfadname geben sie hier ebenfalls die reguläre Unix-Verzeichnisangabe und den Namen der FIFO an. Für mode müssen sie hier die Konstante S_IFIFO aus der Headerdatei <sys/stat.h> mit den Zugriffsrechten verwenden. Der Parameter dev wird nicht benötigt. Die Verwendung von mknod sieht dann wie folgt aus............

if(mknod("/tmp/fifo0001.1", S_IFIFO | S_IRUSR | S_IWUSR, 0) == - 1)
{ /*Kann keine fifo erzeugen...........Fehler*/  

Der Rückgabewert bei Erfolg ist 0 und bei Fehler, wie eben gesehen -1. Diese erzeugt FIFO kann anschließend genauso verwendet werden wie mit mkfifo() erzeugte FIFOs. Ich werde in weiteren Kapiteln die Funktion mkfifo() verwenden. Sie müssen sich die Beispiele dann selbst anpassen.

Bevor wir zu einem Client/Server-Beispiel müssen wir noch einige Punkte ansprechen worauf man bei Zugriff auf FIFOs achten muss....

Wenn sie beim öffnen der FIFO mit open nicht den Modus O_NONBLOCK verwenden, wird die Öffnung der FIFO sowohl zum Schreiben als auch zum Lesen blockiert. Beim Schreiben wird solange blockiert bis ein anderer Prozess die FIFO zum Lesen öffnet. Beim Lesen wiederum wird ebenfalls solange blockiert bis ein anderer Prozess in die FIFO schreibt.

Das Flag O_NONBLOCK kann nur bei Lesezugriffen verwendet werden. Wird mit O_NONBLOCK versucht FIFO mit Schreibzugriffen zu öffnen führt die zu einem Fehler beim öffnen der FIFO

Wenn man ein FIFO zum Schreiben mit close oder fclose schließt bedeutet dies für die FIFO zum Lesen ein EOF.

Und wie bei den Pipes gilt schon falls mehrere Prozesse auf in die selbe FIFO schreiben muss darauf geachtet werden das niemals mehr als PIPE_BUF Bytes auf einmal geschrieben werden damit die Daten nicht durcheinandergemischt werden. Dies können sie mit folgendem Beispiel ermitteln.......

/*Download:fifo4.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { unlink("fifo0001"); /*Wir erzeugen eine FIFO*/ if((mkfifo("fifo0001", O_RDWR)) == -1) { fprintf(stderr, "Konnte keine FIFO erzeugen.........\n"); exit(0); } printf("Es können maximal %ld Bytes auf einmal in die FIFO geschrieben werden\n", pathconf("fifo0001", _PC_PIPE_BUF)); printf("Außerdem können max %ld FIFOs geöffnet sein\n", sysconf(_SC_OPEN_MAX)); return 0; }

Beim Versuch in eine FIFO zu schreiben die momentan keinen Prozess zum Lesen geöffnet hat sollte das Signal SIGPIPE generiert werden.

Hier nun ein kleines Beispiel wo wir einen Signalhandler für SIGPIPE einrichten, eine FIFO erzeugen, mit dem Kindprozess in diese FIFO schreiben und mit dem Elternprozess dies wieder auslesen. Wenn sie so wollen ein einfaches Server/Client Beispiel (Eltern/Kind). Hier das Beispiel........

/*Download:fifo5.c*/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> static volatile sig_atomic_t sflag; static sigset_t signal_neu, signal_alt, signal_leer; static void sigfunc(int sig_nr) { fprintf(stderr, "SIGPIPE erhalten. Programmabbruch...\n"); exit(0); } void signal_pipe(void) { if(signal(SIGPIPE, sigfunc) == SIG_ERR) { fprintf(stderr, "Konnte Signal SIGPIPE nicht erzeugen..........\n"); exit(0); } /*Wir entfernen alle Signale aus der Signalmenge*/ sigemptyset(&signal_leer); sigemptyset(&signal_neu); sigaddset(&signal_neu, SIGPIPE); /*Jetzt setzen wir signal_neu und sichern die*/ /* noch aktuelle Signalmaske in signal_alt*/ if(sigprocmask(SIG_UNBLOCK, &signal_neu, &signal_alt) < 0) exit(0); } int main() { int r_fifo, w_fifo; /*handle for FIFO*/ char puffer[]="This text is for the fifo\n"; char buf[100]; pid_t pid; signal_pipe(); unlink("/tmp/fifo0001.1"); /*We create a FIFO*/ if((mkfifo("/tmp/fifo0001.1", O_RDWR)) == -1) { fprintf(stderr, "Can't creat a fifo.........\n"); exit(0); } pid=fork(); if(pid == -1) { perror("fork"); exit(0);} else if(pid > 0) /*Elternprozess ließt aus der FIFO*/ { if (( r_fifo=open("/tmp/fifo0001.1", O_RDONLY)) < 0) { perror("r_fifo open"); exit(0); } while(wait(NULL)!=pid); /*Wir warten auf das Ende vom Kindprozess*/ read(r_fifo, &buf, sizeof(buf)); /*Lesen aus der FIFO*/ printf("%s\n",buf); close(r_fifo); } else /*Kindprozess schreibt in die FIFO*/ { if((w_fifo=open("/tmp/fifo0001.1", O_WRONLY)) < 0) { perror("w_fifo open"); exit(0); } write(w_fifo, puffer, strlen(puffer)); /*Schreiben in die FIFO*/ close(w_fifo); /*EOF*/ exit(0); } return 0; }

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf