Code di messaggi
Perché abbiamo bisogno di code di messaggi quando abbiamo già la memoria condivisa? Sarebbe per molteplici ragioni, proviamo a suddividerlo in più punti per semplificazione -
Come inteso, una volta ricevuto il messaggio da un processo, non sarebbe più disponibile per nessun altro processo. Mentre nella memoria condivisa, i dati sono disponibili per l'accesso a più processi.
Se vogliamo comunicare con piccoli formati di messaggio.
I dati della memoria condivisa devono essere protetti con la sincronizzazione quando più processi comunicano contemporaneamente.
La frequenza di scrittura e lettura utilizzando la memoria condivisa è elevata, quindi sarebbe molto complesso implementare la funzionalità. Non vale per quanto riguarda l'utilizzo in questo tipo di casi.
E se tutti i processi non avessero bisogno di accedere alla memoria condivisa ma solo pochi processi ne avessero bisogno, sarebbe meglio implementarli con le code di messaggi.
Se vogliamo comunicare con diversi pacchetti di dati, supponiamo che il processo A invii il tipo di messaggio 1 al processo B, il tipo di messaggio 10 al processo C e il tipo di messaggio 20 al processo D. In questo caso, è più semplice implementarlo con le code di messaggi. Per semplificare il tipo di messaggio dato come 1, 10, 20, può essere 0 o + ve o –ve come discusso di seguito.
Naturalmente, l'ordine della coda dei messaggi è FIFO (First In First Out). Il primo messaggio inserito in coda è il primo ad essere recuperato.
L'utilizzo della memoria condivisa o delle code di messaggi dipende dalle necessità dell'applicazione e dall'efficacia con cui possono essere utilizzate.
La comunicazione tramite le code di messaggi può avvenire nei seguenti modi:
Scrittura nella memoria condivisa da un processo e lettura dalla memoria condivisa da un altro processo. Come sappiamo, la lettura può essere eseguita anche con più processi.
Scrittura nella memoria condivisa da un processo con diversi pacchetti di dati e lettura da esso da più processi, cioè, in base al tipo di messaggio.
Dopo aver visto alcune informazioni sulle code dei messaggi, ora è il momento di controllare la chiamata di sistema (System V) che supporta le code dei messaggi.
Per eseguire la comunicazione utilizzando le code di messaggi, di seguito sono riportati i passaggi:
Step 1 - Crea una coda di messaggi o connettiti a una coda di messaggi già esistente (msgget ())
Step 2 - Scrivi nella coda dei messaggi (msgsnd ())
Step 3 - Leggi dalla coda dei messaggi (msgrcv ())
Step 4 - Eseguire operazioni di controllo sulla coda dei messaggi (msgctl ())
Ora, controlliamo la sintassi e alcune informazioni sulle chiamate precedenti.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
Questa chiamata di sistema crea o alloca una coda di messaggi di System V. Devono essere passati i seguenti argomenti:
Il primo argomento, key, riconosce la coda dei messaggi. La chiave può essere un valore arbitrario o uno che può essere derivato dalla funzione di libreria ftok ().
Il secondo argomento, shmflg, specifica i flag della coda dei messaggi richiesti come IPC_CREAT (creazione della coda messaggi se non esiste) o IPC_EXCL (utilizzato con IPC_CREAT per creare la coda messaggi e la chiamata non riesce, se la coda messaggi esiste già). È necessario passare anche le autorizzazioni.
Note - Fare riferimento alle sezioni precedenti per i dettagli sulle autorizzazioni.
Questa chiamata restituirà un identificatore di coda di messaggi valido (utilizzato per ulteriori chiamate di coda di messaggi) in caso di successo e -1 in caso di errore. Per conoscere la causa del fallimento, controlla con la variabile errno o la funzione perror ().
Vari errori rispetto a questa chiamata sono EACCESS (autorizzazione negata), EEXIST (la coda esiste già non può creare), ENOENT (la coda non esiste), ENOMEM (memoria insufficiente per creare la coda), ecc.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
Questa chiamata di sistema invia / aggiunge un messaggio alla coda dei messaggi (Sistema V). Devono essere passati i seguenti argomenti:
Il primo argomento, msgstr, riconosce la coda dei messaggi, cioè l'identificatore della coda dei messaggi. Il valore dell'identificatore viene ricevuto dopo il successo di msgget ()
Il secondo argomento, msgp, è il puntatore al messaggio, inviato al chiamante, definito nella struttura del seguente form -
struct msgbuf {
long mtype;
char mtext[1];
};
La variabile mtype viene utilizzata per comunicare con diversi tipi di messaggi, spiegati in dettaglio nella chiamata msgrcv (). La variabile mtext è un array o un'altra struttura la cui dimensione è specificata da msgsz (valore positivo). Se il campo mtext non viene menzionato, viene considerato come messaggio di dimensione zero, il che è consentito.
Il terzo argomento, msgsz, è la dimensione del messaggio (il messaggio dovrebbe terminare con un carattere nullo)
Il quarto argomento, msgflg, indica determinati flag come IPC_NOWAIT (restituisce immediatamente quando nessun messaggio viene trovato in coda o MSG_NOERROR (tronca il testo del messaggio, se più di msgsz byte)
Questa chiamata restituirà 0 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/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)
Questa chiamata di sistema recupera il messaggio dalla coda dei messaggi (Sistema V). Devono essere passati i seguenti argomenti:
Il primo argomento, msgstr, riconosce la coda dei messaggi, cioè l'identificativo della coda dei messaggi. Il valore dell'identificatore viene ricevuto dopo il successo di msgget ()
Il secondo argomento, msgp, è il puntatore del messaggio ricevuto dal chiamante. È definito nella struttura del modulo seguente:
struct msgbuf {
long mtype;
char mtext[1];
};
La variabile mtype viene utilizzata per comunicare con diversi tipi di messaggi. La variabile mtext è un array o un'altra struttura la cui dimensione è specificata da msgsz (valore positivo). Se il campo mtext non viene menzionato, viene considerato come messaggio di dimensione zero, il che è consentito.
Il terzo argomento, msgsz, è la dimensione del messaggio ricevuto (il messaggio dovrebbe terminare con un carattere nullo)
Il quarto argomento, msgtype, indica il tipo di messaggio -
If msgtype is 0 - Legge il primo messaggio ricevuto nella coda
If msgtype is +ve - Legge il primo messaggio nella coda di tipo msgtype (se msgtype è 10, quindi legge solo il primo messaggio di tipo 10 anche se altri tipi potrebbero essere nella coda all'inizio)
If msgtype is –ve - Legge il primo messaggio di tipo più basso minore o uguale al valore assoluto del tipo di messaggio (ad esempio, se msgtype è -5, allora legge il primo messaggio di tipo inferiore a 5 cioè, tipo di messaggio da 1 a 5)
Il quinto argomento, msgflg, indica determinati flag come IPC_NOWAIT (restituisce immediatamente quando non viene trovato alcun messaggio nella coda o MSG_NOERROR (tronca il testo del messaggio se più di msgsz byte)
Questa chiamata restituirà il numero di byte effettivamente ricevuti nell'array mtext in caso di successo e -1 in caso di errore. Per conoscere la causa del fallimento, controlla con la variabile errno o la funzione perror ().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf)
Questa chiamata di sistema esegue le operazioni di controllo della coda dei messaggi (Sistema V). Devono essere passati i seguenti argomenti:
Il primo argomento, msgstr, riconosce la coda dei messaggi, cioè l'identificativo della coda dei messaggi. Il valore dell'identificatore viene ricevuto dopo il successo di msgget ()
Il secondo argomento, cmd, è il comando per eseguire l'operazione di controllo richiesta sulla coda dei messaggi. I valori validi per cmd sono -
IPC_STAT- Copia le informazioni dei valori correnti di ogni membro di struct msqid_ds nella struttura passata indicata da buf. Questo comando richiede l'autorizzazione di lettura sulla coda dei messaggi.
IPC_SET - Imposta l'ID utente, l'ID gruppo del proprietario, i permessi, ecc. Puntati dalla struttura buf.
IPC_RMID - Rimuove immediatamente la coda dei messaggi.
IPC_INFO - Restituisce informazioni sui limiti e sui parametri della coda di messaggi nella struttura indicata da buf, che è di tipo struct msginfo
MSG_INFO - Restituisce una struttura msginfo contenente informazioni sulle risorse di sistema consumate dalla coda dei messaggi.
Il terzo argomento, buf, è un puntatore alla struttura della coda dei messaggi denominata struct msqid_ds. I valori di questa struttura verrebbero usati per set o get come da cmd.
Questa chiamata restituirà il valore a seconda del comando passato. Il successo di IPC_INFO e MSG_INFO o MSG_STAT restituisce l'indice o l'identificatore della coda dei messaggi o 0 per altre operazioni e -1 in caso di errore. Per conoscere la causa del fallimento, controlla con la variabile errno o la funzione perror ().
Dopo aver visto le informazioni di base e le chiamate di sistema relative alle code di messaggi, ora è il momento di controllare con un programma.
Vediamo la descrizione prima di guardare il programma -
Step 1 - Creare due processi, uno per l'invio nella coda dei messaggi (msgq_send.c) e un altro per il recupero dalla coda dei messaggi (msgq_recv.c)
Step 2- Creazione della chiave, utilizzando la funzione ftok (). Per questo, inizialmente viene creato il file msgq.txt per ottenere una chiave univoca.
Step 3 - Il processo di invio esegue quanto segue.
Legge l'input della stringa dall'utente
Rimuove la nuova riga, se esiste
Invia alla coda dei messaggi
Ripete il processo fino alla fine dell'inserimento (CTRL + D)
Una volta ricevuta la fine dell'input, invia il messaggio “end” per indicare la fine del processo
Step 4 - Nel processo di ricezione, esegue quanto segue.
- Legge il messaggio dalla coda
- Visualizza l'output
- Se il messaggio ricevuto è "end", termina il processo ed esce
Per semplificare, non stiamo usando il tipo di messaggio per questo esempio. Inoltre, un processo sta scrivendo nella coda e un altro processo sta leggendo dalla coda. Questo può essere esteso in base alle necessità, ad esempio, idealmente un processo scriverebbe nella coda e più processi leggerebbero dalla coda.
Ora controlliamo il processo (invio del messaggio in coda) - File: 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;
}
Fasi di compilazione ed esecuzione
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.
Di seguito è riportato il codice dal processo di ricezione del messaggio (recupero del messaggio dalla coda) - File: 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;
}
Fasi di compilazione ed esecuzione
message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.