Komunikacja między procesami - potoki nazwane
Rury były przeznaczone do komunikacji między powiązanymi procesami. Czy możemy użyć potoków do niepowiązanej komunikacji procesowej, powiedzmy, chcemy wykonać program klienta z jednego terminala, a program serwera z innego terminala? Odpowiedź brzmi: Nie. Zatem jak możemy osiągnąć komunikację z niepowiązanymi procesami, prostą odpowiedzią są potoki nazwane. Mimo że działa to w przypadku powiązanych procesów, nie ma sensu używać nazwanych potoków do komunikacji z powiązanymi procesami.
Użyliśmy jednej rury do komunikacji jednokierunkowej i dwóch rur do komunikacji dwukierunkowej. Czy ten sam warunek dotyczy nazwanych rur. Odpowiedź brzmi: nie, możemy użyć pojedynczego nazwanego potoku, który może być używany do komunikacji dwukierunkowej (komunikacja między serwerem a klientem, a także klient i serwer w tym samym czasie), ponieważ Named Pipe obsługuje komunikację dwukierunkową.
Inna nazwa potoku nazwanego to FIFO (First-In-First-Out). Przyjrzyjmy się wywołaniu systemowemu (mknod ()), aby utworzyć nazwany potok, który jest rodzajem specjalnego pliku.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
To wywołanie systemowe utworzy specjalny plik lub węzeł systemu plików, taki jak zwykły plik, plik urządzenia lub FIFO. Argumentami wywołania systemowego są ścieżka, tryb i dev. Nazwa ścieżki wraz z atrybutami informacji o trybie i urządzeniu. Ścieżka jest względna, jeśli katalog nie zostanie określony, zostanie utworzony w bieżącym katalogu. Określony tryb to tryb pliku, który określa typ pliku, taki jak typ pliku i tryb pliku, jak wspomniano w poniższych tabelach. Pole dev służy do określenia informacji o urządzeniu, takich jak główne i poboczne numery urządzeń.
Typ pliku | Opis | Typ pliku | Opis |
---|---|---|---|
S_IFBLK | blok specjalny | S_IFREG | Zwykły plik |
S_IFCHR | znak specjalny | S_IFDIR | Informator |
S_IFIFO | FIFO special | S_IFLNK | Symboliczne łącze |
Tryb pliku | Opis | Tryb pliku | Opis |
---|---|---|---|
S_IRWXU | Czytaj, pisz, wykonuj / szukaj według właściciela | S_IWGRP | Napisz pozwolenie, grupa |
S_IRUSR | Czytaj pozwolenie, właściciel | S_IXGRP | Uprawnienia do wykonywania / wyszukiwania, grupa |
S_IWUSR | Napisz pozwolenie, właścicielu | S_IRWXO | Czytaj, pisz, wykonuj / wyszukuj przez innych |
S_IXUSR | Uprawnienia do wykonywania / wyszukiwania, właściciel | S_IROTH | Czytaj pozwolenie, inni |
S_IRWXG | Czytaj, pisz, wykonuj / szukaj według grup | S_IWOTH | Napisz pozwolenie, inni |
S_IRGRP | Pozwolenie na odczyt, grupa | S_IXOTH | Uprawnienia do wykonywania / wyszukiwania, inne |
Tryb pliku można również przedstawić w notacji ósemkowej, takiej jak 0XYZ, gdzie X reprezentuje właściciela, Y reprezentuje grupę, a Z reprezentuje innych. Wartość X, Y lub Z może wynosić od 0 do 7. Wartości do odczytu, zapisu i wykonania to odpowiednio 4, 2, 1. W razie potrzeby w połączeniu z odczytem, zapisem i wykonaniem, należy odpowiednio dodać wartości.
Powiedzmy, że jeśli wspomnimy, 0640, to oznacza to odczyt i zapis (4 + 2 = 6) dla właściciela, odczyt (4) dla grupy i brak uprawnień (0) dla innych.
To wywołanie zwróci zero w przypadku sukcesu i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
Ta funkcja biblioteczna tworzy specjalny plik FIFO, który jest używany dla nazwanego potoku. Argumentami tej funkcji są nazwa pliku i tryb. Nazwa pliku może być ścieżką bezwzględną lub ścieżką względną. Jeśli pełna nazwa ścieżki (lub ścieżka bezwzględna) nie zostanie podana, plik zostanie utworzony w bieżącym folderze wykonywanego procesu. Informacje o trybie plików są takie, jak opisano w wywołaniu systemowym mknod ().
To wywołanie zwróci zero w przypadku sukcesu i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
Rozważmy program uruchamiający serwer na jednym terminalu i uruchamiający klienta na innym terminalu. Program wykonywałby tylko jednokierunkową komunikację. Klient akceptuje dane wejściowe użytkownika i wysyła wiadomość do serwera, a serwer drukuje wiadomość na wyjściu. Proces jest kontynuowany do momentu wpisania przez użytkownika ciągu „koniec”.
Zrozummy to na przykładzie -
Step 1 - Utwórz dwa procesy, jeden to fifoserver, a drugi to fifoclient.
Step 2 - Proces serwera wykonuje następujące czynności -
Tworzy nazwany potok (za pomocą funkcji systemowej mknod ()) o nazwie „MYFIFO”, jeśli nie został utworzony.
Otwiera nazwany potok tylko do odczytu.
Tutaj utworzono FIFO z uprawnieniami odczytu i zapisu dla właściciela. Odczyt dla grupy i brak uprawnień dla innych.
Oczekuje w nieskończoność na wiadomość od Klienta.
Jeśli wiadomość otrzymana od klienta nie „koniec”, drukuje wiadomość. Jeśli komunikat brzmi „koniec”, zamyka kolejkę i kończy proces.
Step 3 - Proces klienta wykonuje następujące czynności -
Otwiera nazwany potok tylko do zapisu.
Akceptuje ciąg od użytkownika.
Sprawdza, czy użytkownik wpisuje „koniec” czy inny niż „koniec”. Tak czy inaczej, wysyła wiadomość do serwera. Jeśli jednak ciąg to „koniec”, zamyka to FIFO i kończy proces.
Powtarza się w nieskończoność, aż użytkownik wpisze ciąg „end”.
Przyjrzyjmy się teraz plikowi serwera FIFO.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
Kroki kompilacji i wykonywania
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
Przyjrzyjmy się teraz przykładowemu kodowi klienta FIFO.
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Spójrzmy na otrzymane wyjście.
Kroki kompilacji i wykonywania
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
Dwukierunkowa komunikacja przy użyciu nazwanych potoków
Komunikacja między rurami ma być jednokierunkowa. Rury były ogólnie ograniczone do komunikacji jednokierunkowej i wymagały co najmniej dwóch rur do komunikacji dwukierunkowej. Rury są przeznaczone tylko do powiązanych ze sobą procesów. Potoki nie mogą być używane do komunikacji z niepowiązanymi procesami, powiedzmy, jeśli chcemy wykonać jeden proces z jednego terminala, a inny proces z innego terminala, nie jest to możliwe w przypadku potoków. Czy mamy jakiś prosty sposób komunikowania się między dwoma procesami, powiedzmy niepowiązanymi procesami w prosty sposób? Odpowiedź brzmi tak. Nazwany potok jest przeznaczony do komunikacji między dwoma lub więcej niepowiązanymi procesami i może również mieć komunikację dwukierunkową.
Już widzieliśmy jednokierunkową komunikację między nazwanymi potokami, tj. Wiadomościami od klienta do serwera. Przyjrzyjmy się teraz komunikacji dwukierunkowej, tj. Klientowi wysyłającemu wiadomość do serwera i serwerowi odbierającemu wiadomość i wysyłając kolejną wiadomość do klienta za pomocą tego samego nazwanego potoku.
Oto przykład -
Step 1 - Utwórz dwa procesy, jeden to fifoserver_twoway, a drugi to fifoclient_twoway.
Step 2 - Proces serwera wykonuje następujące czynności -
Tworzy nazwany potok (przy użyciu funkcji biblioteki mkfifo ()) o nazwie „fifo_twoway” w katalogu / tmp, jeśli nie został utworzony.
Otwiera nazwany potok do celów odczytu i zapisu.
Tutaj utworzono FIFO z uprawnieniami odczytu i zapisu dla właściciela. Odczyt dla grupy i brak uprawnień dla innych.
Oczekuje nieskończenie na wiadomość od klienta.
Jeśli wiadomość otrzymana od klienta nie jest „koniec”, wyświetla komunikat i odwraca ciąg. Odwrócony ciąg jest odsyłany do klienta. Jeśli komunikat brzmi „koniec”, zamyka kolejkę i kończy proces.
Step 3 - Proces klienta wykonuje następujące czynności -
Otwiera nazwany potok do celów odczytu i zapisu.
Akceptuje ciąg znaków od użytkownika.
Sprawdza, czy użytkownik wpisuje „koniec” czy inny niż „koniec”. Tak czy inaczej, wysyła wiadomość do serwera. Jeśli jednak ciąg to „koniec”, zamyka to FIFO i kończy proces.
Jeśli wiadomość zostanie wysłana jako „nie koniec”, czeka na wiadomość (odwrócony łańcuch) od klienta i wypisuje odwrócony łańcuch.
Powtarza się w nieskończoność, aż użytkownik wprowadzi ciąg „koniec”.
Przyjrzyjmy się teraz przykładowemu kodowi serwera FIFO.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
Kroki kompilacji i wykonywania
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
Przyjrzyjmy się teraz przykładowemu kodowi klienta FIFO.
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Kroki kompilacji i wykonywania
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3