Comunicación entre procesos: tuberías con nombre

Las tuberías estaban destinadas a la comunicación entre procesos relacionados. ¿Podemos usar tuberías para la comunicación de procesos no relacionados, digamos, queremos ejecutar el programa cliente desde una terminal y el programa servidor desde otra terminal? La respuesta es No. Entonces, ¿cómo podemos lograr procesos de comunicación no relacionados? La respuesta simple es Named Pipes. Aunque esto funciona para procesos relacionados, no tiene sentido usar las canalizaciones con nombre para la comunicación de procesos relacionados.

Usamos una tubería para la comunicación unidireccional y dos tuberías para la comunicación bidireccional. ¿Se aplica la misma condición a las canalizaciones con nombre? La respuesta es no, podemos usar una tubería con nombre único que se puede usar para la comunicación bidireccional (comunicación entre el servidor y el cliente, más el cliente y el servidor al mismo tiempo) ya que la tubería con nombre admite la comunicación bidireccional.

Otro nombre para tubería con nombre es FIFO (First-In-First-Out). Veamos la llamada al sistema (mknod ()) para crear una tubería con nombre, que es una especie de archivo especial.

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

Esta llamada al sistema crearía un archivo especial o un nodo del sistema de archivos, como un archivo normal, un archivo de dispositivo o FIFO. Los argumentos para la llamada al sistema son nombre de ruta, modo y dev. El nombre de la ruta junto con los atributos del modo y la información del dispositivo. El nombre de la ruta es relativo, si no se especifica el directorio, se creará en el directorio actual. El modo especificado es el modo de archivo que especifica el tipo de archivo, como el tipo de archivo y el modo de archivo, como se menciona en las siguientes tablas. El campo dev es para especificar información del dispositivo, como números de dispositivo principales y secundarios.

Tipo de archivo Descripción Tipo de archivo Descripción
S_IFBLK bloque especial S_IFREG Archivo regular
S_IFCHR personaje especial S_IFDIR Directorio
S_IFIFO FIFO especial S_IFLNK Enlace simbólico
Modo de archivo Descripción Modo de archivo Descripción
S_IRWXU Leer, escribir, ejecutar / buscar por propietario S_IWGRP Permiso de escritura, grupo
S_IRUSR Leer permiso, propietario S_IXGRP Permiso de ejecución / búsqueda, grupo
S_IWUSR Permiso de escritura, propietario S_IRWXO Leer, escribir, ejecutar / buscar por otros
S_IXUSR Permiso de ejecución / búsqueda, propietario S_IROTH Leer permiso, otros
S_IRWXG Leer, escribir, ejecutar / buscar por grupo S_IWOTH Permiso de escritura, otros
S_IRGRP Leer permiso, grupo S_IXOTH Permiso de ejecución / búsqueda, otros

El modo de archivo también se puede representar en notación octal como 0XYZ, donde X representa al propietario, Y representa al grupo y Z representa a otros. El valor de X, Y o Z puede variar de 0 a 7. Los valores de lectura, escritura y ejecución son 4, 2, 1 respectivamente. Si es necesario en combinación de lectura, escritura y ejecución, agregue los valores en consecuencia.

Digamos, si mencionamos, 0640, entonces esto significa leer y escribir (4 + 2 = 6) para el propietario, leer (4) para el grupo y sin permisos (0) para los demás.

Esta llamada devolvería cero en caso de éxito y -1 en caso de error. Para conocer la causa de la falla, verifique con la variable errno o la función perror ().

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

int mkfifo(const char *pathname, mode_t mode)

Esta función de biblioteca crea un archivo especial FIFO, que se utiliza para la tubería con nombre. Los argumentos de esta función son el nombre del archivo y el modo. El nombre del archivo puede ser una ruta absoluta o una ruta relativa. Si no se proporciona el nombre de ruta completo (o ruta absoluta), el archivo se creará en la carpeta actual del proceso en ejecución. La información del modo de archivo es como se describe en la llamada al sistema mknod ().

Esta llamada devolvería cero en caso de éxito y -1 en caso de error. Para conocer la causa de la falla, verifique con la variable errno o la función perror ().

Consideremos un programa para ejecutar el servidor en un terminal y ejecutar el cliente en otro terminal. El programa solo realizaría comunicación unidireccional. El cliente acepta la entrada del usuario y envía el mensaje al servidor, el servidor imprime el mensaje en la salida. El proceso continúa hasta que el usuario ingresa la cadena "fin".

Entendamos esto con un ejemplo:

Step 1 - Crea dos procesos, uno es Fifoserver y otro es Fifoclient.

Step 2 - El proceso del servidor realiza lo siguiente -

  • Crea una tubería con nombre (usando la llamada al sistema mknod ()) con el nombre "MYFIFO", si no se creó.

  • Abre la tubería con nombre para fines de solo lectura.

  • Aquí, creó FIFO con permisos de lectura y escritura para el propietario. Leer para el grupo y sin permisos para otros.

  • Espera infinitamente el mensaje del cliente.

  • Si el mensaje recibido del cliente no es “fin”, imprime el mensaje. Si el mensaje es “fin”, cierra el quince y finaliza el proceso.

Step 3 - El proceso del cliente realiza lo siguiente -

  • Abre la canalización con nombre solo con fines de escritura.

  • Acepta la cadena del usuario.

  • Comprueba si el usuario ingresa "fin" u otro que no sea "fin". De cualquier manera, envía un mensaje al servidor. Sin embargo, si la cadena es "fin", esto cierra el FIFO y también finaliza el proceso.

  • Se repite infinitamente hasta que el usuario ingresa la cadena "fin".

Ahora echemos un vistazo al archivo del servidor 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;
}

Pasos de compilación y ejecución

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

Ahora, echemos un vistazo al código de muestra del cliente 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;
}

Tomemos una en la salida que llega.

Pasos de compilación y ejecución

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

Comunicación bidireccional mediante canalizaciones con nombre

La comunicación entre tuberías debe ser unidireccional. Las tuberías estaban restringidas a la comunicación unidireccional en general y necesitan al menos dos tuberías para la comunicación bidireccional. Las tuberías están destinadas únicamente a procesos interrelacionados. Las tuberías no se pueden usar para la comunicación de procesos no relacionados, digamos, si queremos ejecutar un proceso desde una terminal y otro proceso desde otra terminal, no es posible con tuberías. ¿Tenemos alguna forma sencilla de comunicarnos entre dos procesos, digamos procesos no relacionados de forma sencilla? La respuesta es sí. La tubería con nombre está destinada a la comunicación entre dos o más procesos no relacionados y también puede tener comunicación bidireccional.

Ya hemos visto la comunicación unidireccional entre canalizaciones con nombre, es decir, los mensajes del cliente al servidor. Ahora, echemos un vistazo a la comunicación bidireccional, es decir, el cliente envía un mensaje al servidor y el servidor recibe el mensaje y devuelve otro mensaje al cliente utilizando el mismo canal con nombre.

A continuación se muestra un ejemplo:

Step 1 - Cree dos procesos, uno es Fifoserver_twoway y otro es Fifoclient_twoway.

Step 2 - El proceso del servidor realiza lo siguiente -

  • Crea una tubería con nombre (usando la función de biblioteca mkfifo ()) con el nombre "FIFo_twoway" en el directorio / tmp, si no se creó.

  • Abre la tubería con nombre para fines de lectura y escritura.

  • Aquí, creó FIFO con permisos de lectura y escritura para el propietario. Leer para el grupo y sin permisos para otros.

  • Espera infinitamente un mensaje del cliente.

  • Si el mensaje recibido del cliente no es “fin”, imprime el mensaje e invierte la cadena. La cadena invertida se envía de vuelta al cliente. Si el mensaje es “fin”, cierra el quince y finaliza el proceso.

Step 3 - El proceso del cliente realiza lo siguiente -

  • Abre la tubería con nombre para fines de lectura y escritura.

  • Acepta cadena del usuario.

  • Comprueba si el usuario ingresa "fin" u otro que no sea "fin". De cualquier manera, envía un mensaje al servidor. Sin embargo, si la cadena es "fin", esto cierra el FIFO y también finaliza el proceso.

  • Si el mensaje se envía como no “final”, espera el mensaje (cadena invertida) del cliente e imprime la cadena invertida.

  • Se repite infinitamente hasta que el usuario ingresa la cadena "fin".

Ahora, echemos un vistazo al código de muestra del servidor 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;
}

Pasos de compilación y ejecución

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

Ahora, echemos un vistazo al código de muestra del cliente 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;
}

Pasos de compilación y ejecución

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