Wie erwähnt, lösen FIFOs das Kommunikationsproblem zwischen Server- und
Client-Prozessen, die nicht direkt miteinander verwandt sind. Wir implementieren
zwei einfache solche Beispiele.
int main()
{
int fd,err;
char c;
static char fifoname[]="/tmp/echoserv";
err=mkfifo(fifoname,0x666);
fd=open(fifoname,O_RDONLY);
if ( err || fd<0 ) { perror(fifoname); exit(0); }
while (read(fd,&c,1)==1) putchar(c);
close(fd);
unlink(fifoname);
}
Mehrere Clients können gleichzeitig an den Server senden. Dazu setzen wir
einfach in mehreren Fenstern das Kommando
cat > /tmp/echoserv
ab. Die danach eingegebenen Zeichen werden (durch die Terminal-Einstellung
zeilenweise) an den Server geschickt, der sie (ggf. gemischt) ausgibt. Das
cat können wir mit einem EOF (CTRL-D) abbrechen,
wodurch die FIFO von diesem Prozeß aus geschlossen wird. Wenn der letzte
Client sich verabschiedet, liefert das read im Server eine Null, und
der Server bricht ab und löscht die FIFO.
Wenn der Server den Clients Daten liefert, kann dieser Verkehr natürlich
nicht über dieselbe FIFO abgewickelt werden wie die Kommandos. In diesem
Fall legt jeder Client eine weitere FIFO an, in die der Server die Antwort
schreibt.
| |
Üblicherweise enthält der Name dieser FIFO die Prozeßnummer des
Clients, damit keine Überschneidungen entstehen können. Diese PID
muß natürlich auch im Kommando mitgeschickt werden. Der Client
muß nach Erhalt der Antwort die FIFO selbst löschen.
Beispiel:
Unser Server ermittelt den kleinsten Teiler (>1) einer natürlichen Zahl,
die ihm von einem Client geschickt wird. Zur Identifikation von Primzahlen
liefert er 0 (eine 1 bei 0 und 1).
Die Kommandos, die ihm geschickt werden, haben das Format
"pid:num", wobei pid die Prozeß-Nummer des
Clients und num die zu untersuchende Zahl ist.
Den Namen der Kommando-FIFO und den Namensanfang der Antwort-FIFOs definieren
wir in einer Header-Datei fifoname.h, die Client und Server einbinden:
#define REQFIFO "/tmp/divireq"
#define ANSFIFO "/tmp/divians"
Wir zeigen zunächst das Client-Programm. Ihm können in der Aufrufzeile
beliebig viele Zahlen übergeben werden, die es an den Server schickt.
Es gibt eine Interpretation der erhaltenen Antwort aus. Wir wollen hier
keine Binärdaten, sondern ASCII-Daten schicken. Daher werden zur einfacheren
Formatierung mit fdopen C-Ströme um die File-Deskriptoren
gelegt.
#include "fifoname.h"
int main(int argc, char *argv[])
{
int fd_cmd,i;
FILE *cmdfifo;
if ((fd_cmd=open(REQFIFO,O_WRONLY))<0)
{ fputs("server not running\n",stderr); exit(0); }
cmdfifo=fdopen(fd_cmd,"w");
for ( i=1 ; i<argc ; ++i )
{
char ansname[80],buffer[80];
int err,div,fd_ans;
long num;
pid_t self=getpid();
FILE *ansfifo;
sprintf(ansname,ANSFIFO".%d",self);
if (mkfifo(ansname,0666) || (fd_ans=open(ansname,O_RDWR))<0 )
{ perror("can't create answer fifo"); exit(0); }
ansfifo=fdopen(fd_ans,"rw");
num=abs(atol(argv[i]));
fprintf(cmdfifo,"%d:%ld\n",self,num);
fflush(cmdfifo);
if (fscanf(ansfifo,"%d",&div)!=1)
perror("no answer");
else
{
if (div) printf("%d is divisible by %d\n",num,div);
else printf("%d is prime\n",num);
}
fclose(ansfifo);
unlink(ansname);
}
fclose(cmdfifo);
}
Der Server legt zunächst die Kommando-FIFO an und öffnet sie zum Lesen
und zum Schreiben. Dadurch gibt es immer einen schreibenden Prozeß, so
daß die FIFO beim Lesen nie ein EOF meldet.
Der Server läuft in einer Endlosschleife und kann nur durch ein Signal
abgebrochen werden. Damit auch bei einem CTRL-C die Kommando-FIFO
weggeräumt wird, müssen wir einen Signal-Handler schreiben.
#include "fifoname.h"
static FILE *cmdfifo;
static void sigint_handler(int sig)
{
fclose(cmdfifo);
unlink(REQFIFO);
exit(0);
}
static unsigned long divisor(unsigned long l)
{
unsigned long d,s;
if (l<4) return (l<=1);
if ((l&1)==0) return 2;
s=(unsigned long)sqrt((double)l);
for (d=3;d<=s;d+=2) if (l%d==0) return d;
return 0;
}
int main()
{
int fd;
signal(SIGINT,sigint_handler);
if ( mkfifo(REQFIFO,0666) || (fd=open(REQFIFO,O_RDWR))<0 )
{ perror("can't create " REQFIFO); sigint_handler(SIGINT); }
cmdfifo=fdopen(fd,"rw");
for (;;)
{
char buffer[256], *P=buffer;
fgets(buffer,256,cmdfifo);
P=strchr(buffer,':');
if (P==0)
fprintf(stderr,"illegal request: %s\n",buffer);
else
{
int fd2;
unsigned long num;
*P=0;
num=atoi(P+1);
sprintf(buffer,ANSFIFO".%d",atoi(buffer));
if ((fd2=open(buffer,O_WRONLY))<0)
fprintf(stderr,"can't open %s\n",buffer);
else
{
FILE *ansfifo=fdopen(fd2,"w");
fprintf(ansfifo,"%d\n",divisor(num));
fclose(ansfifo);
}
}
}
}