Podstawy
Nazwany potok pracuje podobnie jak normalny ale znacząco różni się.
Nazwane potoki istnieją w systemie plików jako plik urządzenia.
Procesy różnych rodziców mogą dzielić dane poprzez nazwany potok.
Po skończeniu wszystkich operacji nazwany potok pozastaje w systemie plików w celu póżniejszego użycia.
Istnieje kilka sposobów aby utworzyć nazwany potok. Dwie pierwsze mogą zostać wykonane bezpośrednio z powłoki.
mknod MOJEFIFO p
mkfifo a=rw MOJEFIFO
Powyższe dwie komendy wykonują to samo zadanie, z jednym wyjątkiem. Komenda mkfifo umożliwia zmianę atrybutów utworzonego FIFO. Natomiast po użyciu mknod będziemy musieli w tym celu uruchomić chmod.
FIFO może być szybko zidentyfikowane poprzez literkę 'p' w listingu:
$ ls -l MYFIFO
prw-r--r-- 1 root root 0 Dec 14 22:15 MYFIFO|
Zauważ również znak potoku przy nazwie.
Aby utworzyć FIFO za pomocą C musimy posłużyć się wywołaniem systemowym mknod():
FUNKCJA Z BIBLIOTEKI: mknod();
PROTOTYP: int mknod( char *ścieżka, mode_t tryb, dev_t dev);
ZWRACA: 0 - sukces,
-1 - błąd: errno = EFAULT ( nieprawidłowa ścieżka )
EACCES ( niedozwolona operacja )
ENAMETOOLONG ( ścieżka za długa )
ENOENT ( nieprawidłowa ścieżka )
ENOTDIR ( nieprawidłowa ścieżka )
( zerknij do podręcznika mknod po więcej )
UWAGI: Tworzy węzeł w systemie plików ( plik, plik urządzenia, FIFO )
Szczegółowe omówienie mknod() pozostawię podręcznikowi, natomiast my zajmiemy się prostym przykładem tworzenia FIFO w C:
mknod("/tmp/MOJEFIFO", S_IFIFO|0666, 0);
W tym wypadku zostanie utworzony jako FIFO plik '/tmp/MOJEFIFO'. Żądanymi prawami dostępu są '0666', jednakże zależą one również od ustawienia umask w następujący sposób:
końcowe_prawa = żądane_prawa & ~oryginalny_umask
Trikiem na obejście tego jest użycie umask() w celu tymczasowego ustawienia wartości umask:
umask(0);
mknod("/tmp/MOJEFIFO", S_IFIFO|0666, 0);
Trzeci argument mknod() jest ignorowany, chyba że tworzymy plik urządzenia. W takim wypadku służy on do ustawienia głównego i podrzędnego numeru pliku urządzenia.
Operacje We/Wy na FIFO są właściwie takie same jak dla normalnych potoków, z jednym wyjątkiem - musimy użyć wywołania lub funkcji 'open' w celu fizycznego otwarcia kanału do potoku. Używając potoków nie musieliśmy tego robić, gdyż potok rezydował w jądrze a nie w systemie plików. W naszych przykładach będziemy traktować potoki tak jak strumienie - otwieramy je za pomocą fopen(), a zamykamy za pomocą fclose().
Rozważ ten prosty proces serwera:
/*****************************************************************************
Zaczerpnięto z "Linux Programmer's Guide - Rozdział 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODUŁ: fifoserver.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#define FIFO_FILE "MOJEFIFO"
int main(void)
{
FILE *fp;
char readbuf[80];
/* Tworzymy FIFO jeżeli nie istnieje */
umask(0);
mknod(FIFO_FILE, S_IFIFO|0666, 0);
while(1)
{
fp = fopen(FIFO_FILE, "r");
fgets(readbuf, 80, fp);
printf("Otrzymany łańcuch: %s\n", readbuf);
fclose(fp);
}
return(0);
}
Jakoże FIFO domyślnie blokuje program, uruchom go w tle:
$ fifoserver&
Blokowanie akcji przez FIFO omówimy za chwilę. Na razie, rozważ następujący przykład prostego klienta:
/*****************************************************************************
Zaczerpnięto z "Linux Programmer's Guide - Rozdział 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODUŁ: fifoclient.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define FIFO_FILE "MOJEFIFO"
int main(int argc, char *argv[])
{
FILE *fp;
if ( argc != 2 ) {
printf("UŻYCIE: fifoclient [łańcuch]\n");
exit(1);
}
if((fp = fopen(FIFO_FILE, "w")) == NULL) {
perror("fopen");
exit(1);
}
fputs(argv[1], fp);
fclose(fp);
return(0);
}
Normalnie, FIFO blokuje wykonywanie programu. Dla przykładu jeżeli otworzymy FIFO do czytania wykonywanie programu zostanie zatrzymane do czasu gdy inny proces otworzy FIFO do pisania. Oczywiście działa to również w dugą stronę. Jeżeli nie podoba nam się takie działanie musimy przekazać znacznik O_NONBLOCK wywołaniu open().
W przypadku naszego prostego serwera, który czeka w tle na klienta moglibyśmy uruchomić go w pierwszym planie, przełączyć się na inną konsolę, odpalić klienta, i przełączając się patrzeć co się dzieje.
Należy pamiętać, iż każdy potok musi posiadać proces czytający i zapisujący. Jeżeli jakiś proces próbuje przesłać dane przez potoku nie posiadający końcówki czytającej jądro prześle sygnał SIGPIPE. Jest to konieczne gdy więcej niż dwa procesy korzystają z potoku.