Objectif
Les System V message queues permettent à plusieurs processus d'échanger des messages structurés via une file maintenue par le kernel.
Contrairement à un pipe, les frontières des messages sont conservées.
1. Principe
Code: Select all
Processus A
|
| msgsnd()
v
[ FILE DE MESSAGES KERNEL ]
|
| msgrcv()
v
Processus B
Chaque message possède :
- un type ;
- des données.
Code: Select all
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
Prototype :
Code: Select all
int msgget(key_t key, int msgflg);
- identifiant de queue en cas de succès ;
- -1 en cas d'erreur.
Code: Select all
key_t key = ftok("/tmp/msg.key", 'M');
int msqid = msgget(key, IPC_CREAT | 0600);
if (msqid == -1) {
perror("msgget");
}
Code: Select all
int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
La structure passée à msgsnd() et msgrcv() commence obligatoirement par un long représentant le type.
Exemple :
Code: Select all
struct message {
long mtype;
char text[256];
};
Le reste de la structure constitue le corps du message.
5. Envoi : msgsnd()
Prototype :
Code: Select all
int msgsnd(int msqid,
const void *msgp,
size_t msgsz,
int msgflg);
Code: Select all
struct message msg;
msg.mtype = 1;
strcpy(msg.text, "Bonjour");
if (msgsnd(msqid, &msg, strlen(msg.text) + 1, 0) == -1) {
perror("msgsnd");
}
Exemple sûr :
Code: Select all
msgsnd(msqid, &msg, sizeof(msg.text), 0);
Si la queue est pleine, msgsnd() peut bloquer jusqu'à ce qu'il y ait suffisamment de place.
Pour demander un comportement non bloquant :
Code: Select all
IPC_NOWAIT
Code: Select all
msgsnd(msqid, &msg, sizeof(msg.text), IPC_NOWAIT);
7. Réception : msgrcv()
Prototype :
Code: Select all
ssize_t msgrcv(int msqid,
void *msgp,
size_t msgsz,
long msgtyp,
int msgflg);
Code: Select all
struct message msg;
ssize_t n = msgrcv(msqid,
&msg,
sizeof(msg.text),
0,
0);
if (n == -1) {
perror("msgrcv");
}
Le paramètre msgtyp permet de sélectionner quels messages doivent être reçus.
msgtyp == 0
Retourne le premier message de la queue.
Code: Select all
msgrcv(msqid, &msg, sizeof(msg.text), 0, 0);
Retourne le premier message correspondant exactement au type demandé.
Code: Select all
msgrcv(msqid, &msg, sizeof(msg.text), 5, 0);
msgtyp < 0
Sélection spéciale basée sur la plus petite valeur de type satisfaisant la limite donnée.
Cette possibilité peut être utilisée pour implémenter une forme de priorité.
9. IPC_NOWAIT avec msgrcv()
Par défaut, si aucun message correspondant n'existe, msgrcv() peut bloquer.
Avec :
Code: Select all
IPC_NOWAIT
10. MSG_NOERROR
Si le buffer fourni à msgrcv() est plus petit que le message :
- sans MSG_NOERROR : erreur ;
- avec MSG_NOERROR : le message peut être tronqué.
Code: Select all
msgrcv(msqid,
&msg,
sizeof(msg.text),
0,
MSG_NOERROR);
Lorsqu'un message est reçu avec succès via msgrcv(), il est normalement retiré de la queue.
La file agit donc comme une file de messages consommables.
12. Contrôle : msgctl()
Prototype :
Code: Select all
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Code: Select all
IPC_STAT
IPC_SET
IPC_RMID
Suppression :
Code: Select all
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl IPC_RMID");
}
14. IPC_STAT
Récupération des informations :
Code: Select all
struct msqid_ds ds;
if (msgctl(msqid, IPC_STAT, &ds) == -1) {
perror("msgctl IPC_STAT");
}
- permissions ;
- nombre de messages ;
- nombre d'octets ;
- PID du dernier émetteur ;
- PID du dernier récepteur ;
- timestamps ;
- limites de la queue.
IPC_SET permet de modifier certains champs autorisés de la structure associée à la queue.
Cela peut notamment concerner certaines permissions ou limites, selon les droits du processus.
16. Modèle client/serveur
Deux modèles classiques existent.
Une seule queue
Code: Select all
Client ---> queue commune ---> Serveur
Serveur ---> même queue ---> Client
Exemple :
Code: Select all
type 1 = requête serveur
type 100+PID = réponse pour un client
Code: Select all
Client ----> Queue serveur ----> Serveur
Serveur ----> Queue client ----> Client
17. Exemple minimal complet
Création :
Code: Select all
key_t key = ftok("/tmp/msg.key", 'M');
int msqid = msgget(key, IPC_CREAT | 0600);
if (msqid == -1)
perror("msgget");
Code: Select all
struct message {
long mtype;
char text[128];
};
struct message msg;
msg.mtype = 1;
snprintf(msg.text, sizeof(msg.text), "hello");
if (msgsnd(msqid, &msg, strlen(msg.text) + 1, 0) == -1)
perror("msgsnd");
Code: Select all
struct message msg;
if (msgrcv(msqid, &msg, sizeof(msg.text), 1, 0) == -1)
perror("msgrcv");
printf("%s\n", msg.text);
Code: Select all
msgctl(msqid, IPC_RMID, NULL);
Pipe :
Code: Select all
flux d'octets
pas de notion native de message
read() lit une quantité d'octets
Code: Select all
messages séparés
type associé à chaque message
sélection possible par type
objet persistant dans le kernel
Le kernel impose des limites sur :
- taille maximale d'un message ;
- quantité totale de données dans une queue ;
- nombre de queues ;
- ressources globales utilisées.
Il n'est pas utile de mémoriser leurs valeurs exactes.
20. Inspection
Afficher les queues :
Code: Select all
ipcs -q
Code: Select all
cat /proc/sysvipc/msg
Code: Select all
ipcrm -q <msqid>
Cas fréquents :
- EEXIST : IPC_CREAT | IPC_EXCL et queue déjà existante.
- EACCES : permissions insuffisantes.
- EAGAIN : IPC_NOWAIT et opération impossible immédiatement.
- EINTR : appel bloquant interrompu par un signal.
- EIDRM : objet supprimé pendant qu'un processus attendait dessus.
- EINVAL : identifiant ou paramètres invalides.
- frontières des messages conservées ;
- communication entre processus indépendants ;
- sélection des messages par type ;
- pas besoin de gérer soi-même un protocole de framing.
- API historique et assez lourde ;
- objets persistants à nettoyer explicitement ;
- limites kernel ;
- identifiants System V spécifiques ;
- moins naturelle que sockets ou POSIX message queues pour beaucoup de nouveaux projets.
Code: Select all
msgget() // créer / ouvrir
msgsnd() // envoyer
msgrcv() // recevoir
msgctl() // contrôler / supprimer
Code: Select all
key / ftok
|
v
msgget()
|
v
msqid
|
+----> msgsnd()
|
+----> msgrcv()
|
+----> msgctl()
|
+-- IPC_STAT
+-- IPC_SET
+-- IPC_RMID
