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