Colas de mensajes

¿Por qué necesitamos colas de mensajes cuando ya tenemos la memoria compartida? Sería por múltiples razones, intentemos dividir esto en múltiples puntos para simplificar:

  • Como se entiende, una vez que un proceso recibe el mensaje, ya no estará disponible para ningún otro proceso. Mientras que en la memoria compartida, los datos están disponibles para que múltiples procesos accedan.

  • Si queremos comunicarnos con pequeños formatos de mensaje.

  • Los datos de la memoria compartida deben protegerse con sincronización cuando varios procesos se comunican al mismo tiempo.

  • La frecuencia de escritura y lectura usando la memoria compartida es alta, entonces sería muy complejo implementar la funcionalidad. No vale la pena con respecto a la utilización en este tipo de casos.

  • ¿Qué pasa si todos los procesos no necesitan acceder a la memoria compartida pero muy pocos procesos solo la necesitan? Sería mejor implementar con colas de mensajes.

  • Si queremos comunicarnos con diferentes paquetes de datos, digamos que el proceso A envía el tipo de mensaje 1 al proceso B, el tipo de mensaje 10 al proceso C y el tipo de mensaje 20 al proceso D. En este caso, es más sencillo implementarlo con colas de mensajes. Para simplificar el tipo de mensaje dado como 1, 10, 20, puede ser 0 o + ve o –ve como se describe a continuación.

  • Por supuesto, el orden de la cola de mensajes es FIFO (Primero en entrar, primero en salir). El primer mensaje insertado en la cola es el primero que se recupera.

El uso de la memoria compartida o las colas de mensajes depende de la necesidad de la aplicación y de la eficacia con la que se pueda utilizar.

La comunicación mediante colas de mensajes puede ocurrir de las siguientes formas:

  • Escribir en la memoria compartida por un proceso y leer de la memoria compartida por otro proceso. Como sabemos, la lectura también se puede realizar con múltiples procesos.

  • Escritura en la memoria compartida por un proceso con diferentes paquetes de datos y lectura de ella por múltiples procesos, es decir, según el tipo de mensaje.

Habiendo visto cierta información sobre las colas de mensajes, ahora es el momento de verificar la llamada al sistema (Sistema V) que admite las colas de mensajes.

Para realizar la comunicación utilizando colas de mensajes, los siguientes son los pasos:

Step 1 - Cree una cola de mensajes o conéctese a una cola de mensajes ya existente (msgget ())

Step 2 - Escribir en la cola de mensajes (msgsnd ())

Step 3 - Leer de la cola de mensajes (msgrcv ())

Step 4 - Realizar operaciones de control en la cola de mensajes (msgctl ())

Ahora, verifiquemos la sintaxis y cierta información sobre las llamadas anteriores.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg)

Esta llamada al sistema crea o asigna una cola de mensajes de System V. Se deben pasar los siguientes argumentos:

  • El primer argumento, clave, reconoce la cola de mensajes. La clave puede ser un valor arbitrario o uno que se pueda derivar de la función de biblioteca ftok ().

  • El segundo argumento, shmflg, especifica los indicadores de la cola de mensajes requeridos, como IPC_CREAT (creando una cola de mensajes si no existe) o IPC_EXCL (se usa con IPC_CREAT para crear la cola de mensajes y la llamada falla, si la cola de mensajes ya existe). También es necesario aprobar los permisos.

Note - Consulte las secciones anteriores para obtener detalles sobre los permisos.

Esta llamada devolvería un identificador de cola de mensajes válido (utilizado para más llamadas de cola de mensajes) 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 ().

Varios errores con respecto a esta llamada son EACCESS (permiso denegado), EEXIST (la cola ya existe no se puede crear), ENOENT (la cola no existe), ENOMEM (no hay suficiente memoria para crear la cola), etc.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)

Esta llamada al sistema envía / agrega un mensaje a la cola de mensajes (Sistema V). Se deben pasar los siguientes argumentos:

  • El primer argumento, msgid, reconoce la cola de mensajes, es decir, el identificador de la cola de mensajes. El valor del identificador se recibe tras el éxito de msgget ()

  • El segundo argumento, msgp, es el puntero al mensaje, enviado a la persona que llama, definido en la estructura de la siguiente forma:

struct msgbuf {
   long mtype;
   char mtext[1];
};

La variable mtype se utiliza para comunicarse con diferentes tipos de mensajes, explicados en detalle en la llamada msgrcv (). La variable mtext es una matriz u otra estructura cuyo tamaño está especificado por msgsz (valor positivo). Si no se menciona el campo mtext, se considera un mensaje de tamaño cero, lo cual está permitido.

  • El tercer argumento, msgsz, es el tamaño del mensaje (el mensaje debe terminar con un carácter nulo)

  • El cuarto argumento, msgflg, indica ciertos indicadores como IPC_NOWAIT (regresa inmediatamente cuando no se encuentra ningún mensaje en la cola o MSG_NOERROR (trunca el texto del mensaje, si hay más de msgsz bytes)

Esta llamada devolvería 0 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/ipc.h>
#include <sys/msg.h>

int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)

Esta llamada al sistema recupera el mensaje de la cola de mensajes (Sistema V). Se deben pasar los siguientes argumentos:

  • El primer argumento, msgid, reconoce la cola de mensajes, es decir, el identificador de la cola de mensajes. El valor del identificador se recibe tras el éxito de msgget ()

  • El segundo argumento, msgp, es el puntero del mensaje recibido de la persona que llama. Se define en la estructura de la siguiente forma:

struct msgbuf {
   long mtype;
   char mtext[1];
};

La variable mtype se utiliza para comunicarse con diferentes tipos de mensajes. La variable mtext es una matriz u otra estructura cuyo tamaño está especificado por msgsz (valor positivo). Si no se menciona el campo mtext, se considera un mensaje de tamaño cero, lo cual está permitido.

  • El tercer argumento, msgsz, es el tamaño del mensaje recibido (el mensaje debe terminar con un carácter nulo)

  • El cuarto argumento, msgtype, indica el tipo de mensaje:

    • If msgtype is 0 - Lee el primer mensaje recibido en la cola

    • If msgtype is +ve - Lee el primer mensaje en la cola de tipo msgtype (si msgtype es 10, entonces lee solo el primer mensaje de tipo 10 aunque otros tipos pueden estar en la cola al principio)

    • If msgtype is –ve - Lee el primer mensaje del tipo más bajo menor o igual al valor absoluto del tipo de mensaje (digamos, si msgtype es -5, entonces lee el primer mensaje de tipo menor que 5, es decir, el tipo de mensaje de 1 a 5)

  • El quinto argumento, msgflg, indica ciertos indicadores como IPC_NOWAIT (regresa inmediatamente cuando no se encuentra ningún mensaje en la cola o MSG_NOERROR (trunca el texto del mensaje si hay más de msgsz bytes)

Esta llamada devolvería el número de bytes realmente recibidos en la matriz mtext en caso de éxito y -1 en caso de falla. Para conocer la causa de la falla, verifique con la variable errno o la función perror ().

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msgid, int cmd, struct msqid_ds *buf)

Esta llamada al sistema realiza operaciones de control de la cola de mensajes (Sistema V). Se deben pasar los siguientes argumentos:

  • El primer argumento, msgid, reconoce la cola de mensajes, es decir, el identificador de la cola de mensajes. El valor del identificador se recibe tras el éxito de msgget ()

  • El segundo argumento, cmd, es el comando para realizar la operación de control requerida en la cola de mensajes. Los valores válidos para cmd son:

IPC_STAT- Copia información de los valores actuales de cada miembro de la estructura msqid_ds a la estructura pasada señalada por buf. Este comando requiere permiso de lectura en la cola de mensajes.

IPC_SET - Establece el ID de usuario, ID de grupo del propietario, permisos, etc. señalados por la estructura buf.

IPC_RMID - Elimina la cola de mensajes inmediatamente.

IPC_INFO - Devuelve información sobre los límites y parámetros de la cola de mensajes en la estructura señalada por buf, que es de tipo struct msginfo

MSG_INFO - Devuelve una estructura msginfo que contiene información sobre los recursos del sistema consumidos por la cola de mensajes.

  • El tercer argumento, buf, es un puntero a la estructura de la cola de mensajes denominada struct msqid_ds. Los valores de esta estructura se usarían para set o get según cmd.

Esta llamada devolvería el valor dependiendo del comando pasado. El éxito de IPC_INFO y MSG_INFO o MSG_STAT devuelve el índice o identificador de la cola de mensajes o 0 para otras operaciones y -1 en caso de falla. Para conocer la causa de la falla, verifique con la variable errno o la función perror ().

Habiendo visto la información básica y las llamadas al sistema con respecto a las colas de mensajes, ahora es el momento de verificar con un programa.

Veamos la descripción antes de mirar el programa -

Step 1 - Cree dos procesos, uno para enviar a la cola de mensajes (msgq_send.c) y otro para recuperar de la cola de mensajes (msgq_recv.c)

Step 2- Creando la clave, usando la función ftok (). Para esto, inicialmente se crea el archivo msgq.txt para obtener una clave única.

Step 3 - El proceso de envío realiza lo siguiente.

  • Lee la entrada de cadena del usuario

  • Elimina la nueva línea, si existe

  • Se envía a la cola de mensajes

  • Repite el proceso hasta el final de la entrada (CTRL + D)

  • Una vez que se recibe el final de la entrada, envía el mensaje "fin" para indicar el final del proceso

Step 4 - En el proceso de recepción, realiza lo siguiente.

  • Lee el mensaje de la cola
  • Muestra la salida
  • Si el mensaje recibido es "fin", finaliza el proceso y sale

Para simplificar, no estamos usando el tipo de mensaje para esta muestra. Además, un proceso está escribiendo en la cola y otro proceso está leyendo desde la cola. Esto se puede extender según sea necesario, es decir, idealmente un proceso escribiría en la cola y varios procesos leerían de la cola.

Ahora, verifiquemos el proceso (envío de mensajes a la cola) - Archivo: msgq_send.c

/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int len;
   key_t key;
   system("touch msgq.txt");
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to send messages.\n");
   printf("Enter lines of text, ^D to quit:\n");
   buf.mtype = 1; /* we don't really care in this case */
   
   while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
      len = strlen(buf.mtext);
      /* remove newline at end, if it exists */
      if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
      if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
      perror("msgsnd");
   }
   strcpy(buf.mtext, "end");
   len = strlen(buf.mtext);
   if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
   perror("msgsnd");
   
   if (msgctl(msqid, IPC_RMID, NULL) == -1) {
      perror("msgctl");
      exit(1);
   }
   printf("message queue: done sending messages.\n");
   return 0;
}

Pasos de compilación y ejecución

message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.

A continuación se muestra el código del proceso de recepción de mensajes (recuperando el mensaje de la cola) - Archivo: msgq_recv.c

/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int toend;
   key_t key;
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to receive messages.\n");
   
   for(;;) { /* normally receiving never ends but just to make conclusion 
             /* this program ends wuth string of end */
      if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
         perror("msgrcv");
         exit(1);
      }
      printf("recvd: \"%s\"\n", buf.mtext);
      toend = strcmp(buf.mtext,"end");
      if (toend == 0)
      break;
   }
   printf("message queue: done receiving messages.\n");
   system("rm msgq.txt");
   return 0;
}

Pasos de compilación y ejecución

message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.