Filas de mensagens

Por que precisamos de filas de mensagens quando já temos a memória compartilhada? Seria por várias razões, vamos tentar dividir isso em vários pontos para simplificação -

  • Conforme entendido, uma vez que a mensagem é recebida por um processo, ela não estará mais disponível para qualquer outro processo. Enquanto na memória compartilhada, os dados estão disponíveis para vários processos acessarem.

  • Se quisermos nos comunicar com pequenos formatos de mensagem.

  • Os dados da memória compartilhada precisam ser protegidos com sincronização quando vários processos se comunicam ao mesmo tempo.

  • A frequência de escrita e leitura usando a memória compartilhada é alta, então seria muito complexo implementar a funcionalidade. Não vale a pena no que diz respeito à utilização neste tipo de casos.

  • E se todos os processos não precisassem acessar a memória compartilhada, mas muito poucos processos precisassem apenas disso, seria melhor implementar com filas de mensagens.

  • Se quisermos nos comunicar com diferentes pacotes de dados, digamos que o processo A está enviando mensagem do tipo 1 para o processo B, do tipo de mensagem 10 para o processo C e do tipo de mensagem 20 para o processo D. Nesse caso, é mais simples implementar com filas de mensagens. Para simplificar o tipo de mensagem fornecido como 1, 10, 20, pode ser 0 ou + ve ou –ve conforme discutido abaixo.

  • Claro, a ordem da fila de mensagens é FIFO (First In First Out). A primeira mensagem inserida na fila é a primeira a ser recuperada.

O uso de memória compartilhada ou filas de mensagens depende da necessidade do aplicativo e da eficácia com que pode ser utilizado.

A comunicação por meio de filas de mensagens pode acontecer das seguintes maneiras:

  • Escrita na memória compartilhada por um processo e leitura da memória compartilhada por outro processo. Como sabemos, a leitura também pode ser feita com vários processos.

  • Escrita na memória compartilhada por um processo com diferentes pacotes de dados e leitura por vários processos, ou seja, conforme o tipo de mensagem.

Tendo visto certas informações sobre as filas de mensagens, agora é hora de verificar a chamada do sistema (System V) que suporta as filas de mensagens.

Para realizar a comunicação usando filas de mensagens, a seguir estão as etapas -

Step 1 - Crie uma fila de mensagens ou conecte-se a uma fila de mensagens já existente (msgget ())

Step 2 - Escreva na fila de mensagens (msgsnd ())

Step 3 - Lê da fila de mensagens (msgrcv ())

Step 4 - Executar operações de controle na fila de mensagens (msgctl ())

Agora, vamos verificar a sintaxe e certas informações nas chamadas acima.

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

int msgget(key_t key, int msgflg)

Esta chamada do sistema cria ou aloca uma fila de mensagens do System V. Os seguintes argumentos precisam ser passados ​​-

  • O primeiro argumento, chave, reconhece a fila de mensagens. A chave pode ser um valor arbitrário ou derivado da função de biblioteca ftok ().

  • O segundo argumento, shmflg, especifica os sinalizadores de fila de mensagens necessários, como IPC_CREAT (criando fila de mensagens se não existir) ou IPC_EXCL (usado com IPC_CREAT para criar a fila de mensagens e a chamada falhar, se a fila de mensagens já existir). Precisa passar as permissões também.

Note - Consulte as seções anteriores para obter detalhes sobre as permissões.

Esta chamada retornaria um identificador de fila de mensagens válido (usado para chamadas adicionais da fila de mensagens) em caso de sucesso e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função perror ().

Vários erros com relação a esta chamada são EACCESS (permissão negada), EEXIST (fila já existe, não pode ser criada), ENOENT (fila não existe), ENOMEM (memória insuficiente para criar a fila), 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 chamada do sistema envia / anexa uma mensagem à fila de mensagens (Sistema V). Os seguintes argumentos precisam ser passados ​​-

  • O primeiro argumento, msgid, reconhece a fila de mensagens, ou seja, o identificador da fila de mensagens. O valor do identificador é recebido após o sucesso de msgget ()

  • O segundo argumento, msgp, é o ponteiro para a mensagem, enviada ao chamador, definido na estrutura da seguinte forma -

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

A variável mtype é usada para se comunicar com diferentes tipos de mensagem, explicados em detalhes na chamada msgrcv (). A variável mtext é um array ou outra estrutura cujo tamanho é especificado por msgsz (valor positivo). Se o campo mtext não for mencionado, é considerado uma mensagem de tamanho zero, o que é permitido.

  • O terceiro argumento, msgsz, é o tamanho da mensagem (a mensagem deve terminar com um caractere nulo)

  • O quarto argumento, msgflg, indica certos sinalizadores como IPC_NOWAIT (retorna imediatamente quando nenhuma mensagem é encontrada na fila ou MSG_NOERROR (trunca o texto da mensagem, se houver mais de bytes de msgsz)

Esta chamada retornaria 0 em caso de sucesso e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função 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 chamada do sistema recupera a mensagem da fila de mensagens (Sistema V). Os seguintes argumentos precisam ser passados ​​-

  • O primeiro argumento, msgid, reconhece a fila de mensagens, ou seja, o identificador da fila de mensagens. O valor do identificador é recebido após o sucesso de msgget ()

  • O segundo argumento, msgp, é o ponteiro da mensagem recebida do chamador. É definido na estrutura da seguinte forma -

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

A variável mtype é usada para se comunicar com diferentes tipos de mensagens. A variável mtext é um array ou outra estrutura cujo tamanho é especificado por msgsz (valor positivo). Se o campo mtext não for mencionado, é considerado uma mensagem de tamanho zero, o que é permitido.

  • O terceiro argumento, msgsz, é o tamanho da mensagem recebida (a mensagem deve terminar com um caractere nulo)

  • O quarto argumento, msgtype, indica o tipo de mensagem -

    • If msgtype is 0 - Lê a primeira mensagem recebida na fila

    • If msgtype is +ve - Lê a primeira mensagem na fila do tipo msgtype (se msgtype for 10, então lê apenas a primeira mensagem do tipo 10, embora outros tipos possam estar na fila no início)

    • If msgtype is –ve - Lê a primeira mensagem do tipo mais baixo menor ou igual ao valor absoluto do tipo de mensagem (digamos, se msgtype for -5, então ele lê a primeira mensagem do tipo menor que 5, ou seja, tipo de mensagem de 1 a 5)

  • O quinto argumento, msgflg, indica certos sinalizadores, como IPC_NOWAIT (retorna imediatamente quando nenhuma mensagem é encontrada na fila ou MSG_NOERROR (trunca o texto da mensagem se houver mais de bytes msgsz)

Essa chamada retornaria o número de bytes realmente recebidos na matriz mtext em caso de sucesso e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função perror ().

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

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

Esta chamada de sistema executa operações de controle da fila de mensagens (Sistema V). Os seguintes argumentos precisam ser passados ​​-

  • O primeiro argumento, msgid, reconhece a fila de mensagens, ou seja, o identificador da fila de mensagens. O valor do identificador é recebido após o sucesso de msgget ()

  • O segundo argumento, cmd, é o comando para executar a operação de controle necessária na fila de mensagens. Os valores válidos para cmd são -

IPC_STAT- Copia informações dos valores atuais de cada membro de struct msqid_ds para a estrutura passada apontada por buf. Este comando requer permissão de leitura na fila de mensagens.

IPC_SET - Define o ID do usuário, ID do grupo do proprietário, permissões, etc. apontados pela estrutura buf.

IPC_RMID - Remove a fila de mensagens imediatamente.

IPC_INFO - Retorna informações sobre os limites e parâmetros da fila de mensagens na estrutura apontada por buf, que é do tipo struct msginfo

MSG_INFO - Retorna uma estrutura msginfo contendo informações sobre os recursos do sistema consumidos pela fila de mensagens.

  • O terceiro argumento, buf, é um ponteiro para a estrutura da fila de mensagens chamada struct msqid_ds. Os valores dessa estrutura seriam usados ​​para definir ou obter como por cmd.

Esta chamada retornaria o valor dependendo do comando passado. O sucesso de IPC_INFO e MSG_INFO ou MSG_STAT retorna o índice ou identificador da fila de mensagens ou 0 para outras operações e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função perror ().

Depois de ver as informações básicas e as chamadas do sistema em relação às filas de mensagens, agora é hora de verificar com um programa.

Vamos ver a descrição antes de olhar para o programa -

Step 1 - Crie dois processos, um é para enviar para a fila de mensagens (msgq_send.c) e outro é para recuperar da fila de mensagens (msgq_recv.c)

Step 2- Criação da chave, usando a função ftok (). Para isso, inicialmente é criado o arquivo msgq.txt para obter uma chave única.

Step 3 - O processo de envio executa o seguinte.

  • Lê a entrada de string do usuário

  • Remove a nova linha, se existir

  • Envia para a fila de mensagens

  • Repete o processo até o final da entrada (CTRL + D)

  • Assim que o fim da entrada for recebido, envia a mensagem "fim" para significar o fim do processo

Step 4 - No processo de recebimento, realiza o seguinte.

  • Lê a mensagem da fila
  • Exibe a saída
  • Se a mensagem recebida for “fim”, termina o processo e sai

Para simplificar, não estamos usando o tipo de mensagem para esta amostra. Além disso, um processo está gravando na fila e outro processo está lendo na fila. Isso pode ser estendido conforme necessário, ou seja, o ideal é que um processo grave na fila e vários processos sejam lidos da fila.

Agora, vamos verificar o processo (envio de mensagens para a fila) - Arquivo: 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;
}

Etapas de compilação e execução

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 seguir está o código do processo de recebimento de mensagens (recuperando a mensagem da fila) - Arquivo: 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;
}

Etapas de compilação e execução

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