Comunicazione tra processi - Named Pipes

I tubi erano destinati alla comunicazione tra processi correlati. Possiamo usare pipe per comunicazioni di processo non correlate, diciamo, vogliamo eseguire il programma client da un terminale e il programma server da un altro terminale? La risposta è No. Allora come possiamo ottenere una comunicazione di processi non correlati, la risposta semplice è Named Pipes. Anche se funziona per i processi correlati, non ha alcun significato utilizzare le named pipe per la comunicazione del processo correlato.

Abbiamo utilizzato un tubo per la comunicazione unidirezionale e due tubi per la comunicazione bidirezionale. La stessa condizione si applica ai tubi denominati. La risposta è no, possiamo usare una singola named pipe che può essere utilizzata per la comunicazione bidirezionale (comunicazione tra il server e il client, più il client e il server allo stesso tempo) poiché Named Pipe supporta la comunicazione bidirezionale.

Un altro nome per named pipe è FIFO (First-In-First-Out). Vediamo la chiamata di sistema (mknod ()) per creare una named pipe, che è una specie di file speciale.

#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);

Questa chiamata di sistema creerebbe un file speciale o un nodo del file system come un file ordinario, un file di dispositivo o FIFO. Gli argomenti della chiamata di sistema sono pathname, mode e dev. Il nome del percorso insieme agli attributi della modalità e alle informazioni sul dispositivo. Il percorso è relativo, se la directory non è specificata verrebbe creata nella directory corrente. La modalità specificata è la modalità del file che specifica il tipo di file come il tipo di file e la modalità del file come indicato nelle tabelle seguenti. Il campo dev serve a specificare le informazioni sul dispositivo come i numeri di dispositivo principale e secondario.

Tipo di file Descrizione Tipo di file Descrizione
S_IFBLK blocco speciale S_IFREG File regolare
S_IFCHR carattere speciale S_IFDIR Directory
S_IFIFO Speciale FIFO S_IFLNK Collegamento simbolico
Modalità file Descrizione Modalità file Descrizione
S_IRWXU Leggere, scrivere, eseguire / cercare dal proprietario S_IWGRP Autorizzazione di scrittura, gruppo
S_IRUSR Permesso di lettura, proprietario S_IXGRP Autorizzazione di esecuzione / ricerca, gruppo
S_IWUSR Permesso di scrittura, proprietario S_IRWXO Leggere, scrivere, eseguire / cercare da altri
S_IXUSR Esecuzione / autorizzazione di ricerca, proprietario S_IROTH Leggi il permesso, altri
S_IRWXG Leggi, scrivi, esegui / cerca per gruppo S_IWOTH Scrivere il permesso, altri
S_IRGRP Autorizzazione di lettura, gruppo S_IXOTH Autorizzazioni di esecuzione / ricerca, altri

La modalità file può anche essere rappresentata in notazione ottale come 0XYZ, dove X rappresenta il proprietario, Y rappresenta il gruppo e Z rappresenta gli altri. Il valore di X, Y o Z può variare da 0 a 7. I valori per lettura, scrittura ed esecuzione sono 4, 2, 1 rispettivamente. Se necessario in combinazione di lettura, scrittura ed esecuzione, quindi aggiungere i valori di conseguenza.

Diciamo, se menzioniamo, 0640, allora questo significa leggere e scrivere (4 + 2 = 6) per il proprietario, leggere (4) per il gruppo e nessun permesso (0) per gli altri.

Questa chiamata restituirà zero in caso di successo e -1 in caso di fallimento. Per conoscere la causa del fallimento, controlla con la variabile errno o la funzione perror ().

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

int mkfifo(const char *pathname, mode_t mode)

Questa funzione di libreria crea un file speciale FIFO, che viene utilizzato per named pipe. Gli argomenti di questa funzione sono il nome del file e la modalità. Il nome del file può essere percorso assoluto o percorso relativo. Se non viene fornito il nome completo del percorso (o il percorso assoluto), il file verrà creato nella cartella corrente del processo in esecuzione. Le informazioni sulla modalità file sono come descritte nella chiamata di sistema mknod ().

Questa chiamata restituirà zero in caso di successo e -1 in caso di fallimento. Per conoscere la causa del fallimento, controlla con la variabile errno o la funzione perror ().

Consideriamo un programma che esegue il server su un terminale e il client su un altro terminale. Il programma eseguirà solo comunicazioni unidirezionali. Il client accetta l'input dell'utente e invia il messaggio al server, il server stampa il messaggio sull'output. Il processo continua fino a quando l'utente non immette la stringa "end".

Facci capire questo con un esempio:

Step 1 - Crea due processi, uno è fifoserver e un altro è fifoclient.

Step 2 - Il processo del server esegue quanto segue:

  • Crea una named pipe (utilizzando la chiamata di sistema mknod ()) con il nome "MYFIFO", se non creata.

  • Apre la named pipe per scopi di sola lettura.

  • Qui, creato FIFO con i permessi di lettura e scrittura per il proprietario. Leggi per Gruppo e nessun permesso per Altri.

  • Aspetta infinitamente il messaggio dal Cliente.

  • Se il messaggio ricevuto dal client non è "end", stampa il messaggio. Se il messaggio è "end", chiude la fifo e termina il processo.

Step 3 - Il processo client esegue quanto segue:

  • Apre la named pipe per scopi di sola scrittura.

  • Accetta la stringa dall'utente.

  • Controlla se l'utente inserisce "end" o diverso da "end". In ogni caso, invia un messaggio al server. Tuttavia, se la stringa è "end", chiude il FIFO e termina anche il processo.

  • Si ripete all'infinito finché l'utente non immette la stringa "end".

Ora diamo un'occhiata al file del server 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;
}

Fasi di compilazione ed esecuzione

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

Diamo ora uno sguardo al codice di esempio del client 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;
}

Facciamo una all'uscita in arrivo.

Fasi di compilazione ed esecuzione

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

Comunicazione bidirezionale tramite named pipe

La comunicazione tra i tubi deve essere unidirezionale. I tubi erano limitati alla comunicazione unidirezionale in generale e necessitavano di almeno due tubi per la comunicazione bidirezionale. I tubi sono pensati solo per processi correlati. Le pipe non possono essere utilizzate per la comunicazione di processi non correlati, ad esempio, se vogliamo eseguire un processo da un terminale e un altro processo da un altro terminale, non è possibile con le pipe. Abbiamo un modo semplice per comunicare tra due processi, diciamo processi non correlati in modo semplice? La risposta è si. Il named pipe è pensato per la comunicazione tra due o più processi non correlati e può anche avere una comunicazione bidirezionale.

Abbiamo già visto la comunicazione unidirezionale tra named pipe, cioè i messaggi dal client al server. Ora, diamo uno sguardo alla comunicazione bidirezionale, cioè, il client che invia il messaggio al server e il server che riceve il messaggio e che invia un altro messaggio al client utilizzando la stessa pipe denominata.

Di seguito è riportato un esempio:

Step 1 - Crea due processi, uno è fifoserver_twoway e un altro è fifoclient_twoway.

Step 2 - Il processo del server esegue quanto segue:

  • Crea una named pipe (utilizzando la funzione di libreria mkfifo ()) con il nome "fifo_twoway" nella directory / tmp, se non creata.

  • Apre la named pipe per scopi di lettura e scrittura.

  • Qui, creato FIFO con i permessi di lettura e scrittura per il proprietario. Leggi per Gruppo e nessun permesso per Altri.

  • Aspetta infinitamente un messaggio dal cliente.

  • Se il messaggio ricevuto dal client non è "end", stampa il messaggio e inverte la stringa. La stringa invertita viene restituita al client. Se il messaggio è "end", chiude la fifo e termina il processo.

Step 3 - Il processo client esegue quanto segue:

  • Apre la named pipe per scopi di lettura e scrittura.

  • Accetta la stringa dall'utente.

  • Controlla se l'utente inserisce "end" o diverso da "end". In ogni caso, invia un messaggio al server. Tuttavia, se la stringa è "end", chiude il FIFO e termina anche il processo.

  • Se il messaggio viene inviato come non "end", attende il messaggio (stringa invertita) dal client e stampa la stringa invertita.

  • Si ripete all'infinito finché l'utente non immette la stringa "end".

Ora, diamo un'occhiata al codice di esempio del server 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;
}

Fasi di compilazione ed esecuzione

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

Diamo ora un'occhiata al codice di esempio del client 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;
}

Fasi di compilazione ed esecuzione

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