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