Communication
Introduction
Dans tous systèmes embarqués, l’échange d’information est essentiel entre différents systèmes et processus formant le logiciel de ces systèmes
On peut schématiquement distinguer 4 niveaux de communication :
- Communication entre stations de gestion et équipements
- Communication entre équipements mis en réseau
- Communication entre éléments d’un même équipement
- Communication entre processus d’un même élément
IPC - Inter-Process Communication
Dans ce dernier cas, on parle de communication interprocessus (Inter-Process Communication - IPC), laquelle comprend 2 catégories principales de mécanismes
- Mécanismes d’échange d’information ou de données
- Fichiers (Files)
- Mémoires partagées (Shared Memories)
- Tubes nommés ou non (FIFO, Pipes)
- Files d’attente de messages (Messages Queues)
- Sockets (Unix ou Internet)
- Protocoles de communication/gestion (D-Bus, SOAP, SNMP, REST, RPC, gRPC, XML-RPC, etc.)
- Mécanismes de synchronisation
- Signaux
- Sémaphores
- Verrous (locks)
Il est intéressant de noter que les mécanismes d’échange d’information servent souvent également de mécanisme de synchronisation.
Pipes
Les pipes implémentent un canal de communication unidirectionnel entre 2 processus.
Ce canal est accessible par l’intermédiaire de 2 descripteurs de fichiers
virtuels. Le premier descripteur (fd[0]
) sert à la lecture, tandis que le
deuxième (fd[1]
) sert à l’écriture
Le canal est de type byte stream, c’est à dire sans concept de trames délimitant le début et la fin d’un message.
Pour la mise en œuvre des pipes, Linux propose les services suivants
Service | syscall |
---|---|
Création d’un pipe | pipe |
Lecture de données | read |
Ecriture de données | write |
Fermeture de pipe | close |
Création d’un pipe
Pour créer un pipe, Linux propose l’appel système pipe()
.
#include <unistd.h>
int pipe (int fd[2]);
Exemple
int fd[2];
int err = pipe(fd); // create unidirectional pipe
if (err == -1)
/* error*/
pid_t pid = fork();
if (pid == 0) { // child
close(fd[0]); // close unused read descriptor
write (fd[1], msg, sizeof(msg));
} else { // parent
close(fd[1]); // close unused write descriptor
int len = read (fd[0], msg, sizeof(msg));
}
Comportement
- La fonction
pipe()
crée un pipe et retourne deux descripteurs, le 1erfd[0]
pour la réception de données et le 2efd[1]
pour l’envoi de données.
FIFO - Named Pipes
Les FIFO (named pipes) implémentent un canal de communication unidirectionnel généralement entre un processus récepteur et un ou plusieurs processus émetteur.
Le FIFO est un fichier spécial situé dans le système de fichiers virtuels. Il peut être accédé par les opérations utilisées sur les fichiers ordinaires, pour autant de disposer des droits d’accès nécessaires.
Le canal est de type byte stream, c’est à dire sans concept de trames délimitant le début et la fin d’un message
FIFO - Opérations
Avant de pouvoir utiliser un FIFO, celui-ci doit être créé soit avant le lancement des applications le mettant en œuvre soit par les applications elles-mêmes.
- Création d’un FIFO à l’aide de commandes utilisateur
mkfifo [--mode=MODE] FIFO_NAME
- Création d’un FIFO par l’application
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char* pathname, mode_t mode);
Pour accéder au FIFO, Linux propose les services utilisés pour les fichiers ordinaux
Service | syscall |
---|---|
Ouverture du FIFO | open |
Lecture du FIFO | read |
Ecriture dans le FIFO | write |
Fermeture du FIFO | close |
Destruction du FIFO | unlink |
Message Queues
Les files d’attente de messages (message queues) offrent un canal de communication unidirectionnel généralement entre un processus récepteur et un ou plusieurs processus émetteurs.
Contrairement aux pipes et aux FIFO, les files d’attente de messages sont de type datagramme, c’est à dire chaque message ou paquet est stocké de façon indépendante dans la file d’attente.
La taille maximale d’un message ainsi que le nombre de messages pouvant être stockés dans la file d’attente sont déterminés lors de sa création.
Message Queues - Opérations
Pour accéder aux services des files d’attente de messages, Linux propose
une série de méthodes spécifiques avec la bibliothèque <mqueue.h>
.
Opération | syscall |
---|---|
Ouverture et création d’une file d’attente | mq_open |
Lecture d’un message d’une file | mq_receive |
Ecriture d’un message dans une file | mq_send |
Fermeture d’une file d’attente | mq_close |
Destruction d’une file d’attente | mq_unlink |
Lecture des attributs d’une file d’attente | mq_getattr |
La struct mq_attr
permet de décrire les caractérisques d’une file d’attente
struct mq_attr {
long mq_flags; // flags: 0 or O_NONBLOCK
long mq_maxmsg; // maximum number of messages on queue
long mq_msgsize; // maximum message size in bytes
long mq_curmsgs; // number of messages currently in queue
};
Sockets
Les sockets ont été conçus par l’université de Berkeley au début des années 1980. Ils proposent un ensemble de services normalisés pour l’échange d’information entre processus locaux ou distants.
Le socket fournit une prise permettant aux différents processus d’une application ou d’un système d’envoyer et de recevoir des données. Cette prise est très polyvalente et permet d’interfacer une large palette de protocole, dont le plus fréquemment utilisé TCP/IP.
Les sockets proposent deux grands modes pour le transfert de données :
SOCK_STREAM
: bidirectionnel, sûr/fiable, flux de donnéesSOCK_DGRAM
: bidirectionnel, pas sûr/fiable, paquet de données
Opération | syscall |
---|---|
Création d’un socket | socket |
Liaison du socket à une adresse donnée | bind |
Connexion du socket à un socket distant | connect |
Ecoute sur de nouvelles connexions | listen |
Acception d’une nouvelle connexion | accept |
Envoi de données | write , send , sendto |
Réception de données | read , recv , recvfrom |
Fermeture du socket | close |
Socketpair
socketpair offre un service bidirectionnel très intéressant pour l’échange de données entre deux processus.
#include <sys/types.h>
#include <sys/socket.h>
int socketpair (int domain, int type, int protocol, int fd[2]);
Exemple
int fd[2];
int err = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
if (err == -1)
/* error*/
Comportement
- La fonction
socketpair()
crée un canal de communication bidirectionnelle sous la forme d’un socket Unix (le seul supporté par Linux) et retourne deux descripteurs, le 1erfd[0]
pour un processus et le 2efd[1]
pour le deuxième processus. Ces descripteurs permettent d’émettre des données avec la méthodewrite()
et d’en recevoir avec la méthoderead()
.