Il est nécessaire que les processus communiquent entre-eux (pour le partage d’information, la répartition des calculs, la modularité et la facilité). La communication inter-process sont très courrant sous UNIX et servent à résoudre ce problème.
Différentes options
- Fichiers, cependant c’est très lent et difficile à synchroniser
- Tube nommé ou non-nommé
- Files de messages
- Mémoire partagée, qui a l’avantage d’être très rapide
- Socket (échanges via le réseau) qui est universel
Les tubes
Les tubes sont des petits fichiers géré en file circulaire, ils sont si petit qu’ils sont souvent en cache (ce qui est donc très efficace). Si le message devient trop grand, il sera alors découpé en blocs.
Tubes non-nommés
Les tubes non-nommés sont des tubes temporaires, ils sont alloué via
l’appel système pipe()
Il existe différents tubes standards :
stdin
tube de lecture (via le clavier, genrescanf
)stdout
tube de sortie (affichage à l’écran, genreprintf
)stderr
est un tube de sortie pour les messages d’erreurs
Il est ainsi possible de rediriger ces tubes.
Opérations
- Ecriture dans le tube avec appel système
write(int h, char* b, int s)
(h étant le tube, s les premiers octets, et b le buffer) - Lecture dans le tube avec appel système
read(int h, char* b, int s)
- Fermeture du tube via
close(int h)
Note les fonctions read
et write
retournent 0 si on tente d’écrire
ou de lire un tube sans qu’il n’y a pas de processus à l’autre bout du
tube.
Exemple
Redirections
Par défault les 3 tubes standard sont dirigé vers le stdout (ou stderr si configuré autremenet).
On peut également rediriger ces tubes, ainis ce qui était affiché à l’écran est alors dirigé automatiquement dans le tube ou peut être lu à partir d’un tube.
Utilisation en shell
Fonctionnement
Voici un exemple de redirection :
Dans cet exemple :
- On crée un tube
- On ferme le stdout
- On copie notre sortie de tube comme étant le stdout
- On écrit dans le stdout → donc dans notre tube
Exemple en C
Autre exemple (avec execl)
Lorsque l’on redirige un pipe, le pipe reste redirigé si on exécute un
autre programme par après avec execl
, on peut donc passer l’output
d’un programme dans un autre programme. Voici un exemple de pipe qui
prends le résultat du ls et compte le nombre de lignes, c’est
l’équivalent de ls | wc -l
. Notez cependant que les path de ls et wc
sont très certainement différent sur votre système, pour connaitre
le PATH réel faites la commande whereis ls
et whereis wc
.
Tubes nommés
Les tubes nommés sont permanent via des fichiers spéciaux dans le filesystem.
On peut en créer un en utilisant mkfifo(const char* nom, mode_t mode)
(le nom préise le nom du tube et le mode précise les permissions).
Les processus non-només sont liés entre père et fils, tandis qu’ici les processus nommés peuvent être utilisé par des processus qui bien que sont complètement indépendant l’un de l’autre.
Un processus peut ouvrir un tube en utilisant
open(const char* nom, int flags)
(qui est bloquant par défaut tant que
le tube n’est pas ouvert des deux cotés), les flags définissent le mode
d’ouverture (écriture, lecture ou les deux bien que cela ne soit pas
recommandé).
On peut écrire dans un pipe avec write(int fd, char* buf, int size)
et
lire avec read(int fd, char* buf, int size)
On peut enfin fermer un tube avec close(int fd)
Mémoire partagée
La mémoire partagée est un moyen très commun pour partager des informations entre processus, la zone de mémoire est commune à plusieurs processus. La taille est complètement configurable (comme avec malloc) et après un fork, le processus fils hérite de la mémoire partagée.
Shmget - Allocation
L’allocation se fait via int shmget(key_t key, int s, int fl)
où
- La clé est l’identifiant de la mémoire partagée
s
est la taille en octetsfl
est le flag de permission sur la zone
Petite note sur les permissions
Les permissions se font via un code tel que 0664
:
- Le premier
0
indique que le nombre est en octal et non pas en décimal. Ainsi0644
c’est110 110 100
en binaire, et777
est1 100 001 001
en binaire. - Premier
6
→ est le propriétaire signifie que le propriétaire peut lire et écrire(read (1) write (1) execute (0) = 110 = 6) - Deuxème
6
→ est le groupe qui peut lire et écrire également (read (1) write (1) execute (0) = 110 = 6) - Enfin le
4
→ les autres utilisateurs peuvent seulement lire (read (1) write (0) execute (0) = 100 = 4)
Shmat - Récupération de pointeur
L’appel shmat
permet de récupérer un pointeur vers la zone mémoire
partagée. Sa signature de méthode est la suivante :
char* shmat(int shmid, char* addr, int flags)
où
char*
est le pointeur retournéint shmid
est l’identifiant retourné par shmgetchar* addr
est l’addresse souhaitée (généralement positionnée à 0 pour laisser le système choisir)int flags
pour les paramètres de restriction (par exemple SHMRDONLY donne un pointeur en lecture seule)
Shmdt - Détacher la zone
L’appel shmdt
(qui prends en argument le pointeur) va détacher la zone
mémoire sans pour autant la libérer.
Shmctl - Gérer la zone
L’appel int shmctl(int shmid, int cmd, struct shmid_ds* ds)
permet de
gérer la zone de mémoire.
shmid
est le descripteur de la zone retourné par shmgetcmd
détermine l’opération souhaitée (pour supprimer on utiliseIPC_RMID
mais il existe également IPCSTAT pour avoir des informations, IPCSET pour modifier les valeurs associées, etc)ds
contient les données en rapport avec les commandes STAT et SET
Exemple
Disons que l’on veut faire 2 programme, 1 premier écrit dans la zone mémoire et le deuxième la lit :
- Premier programme :
- Deuxième programme :
Commande ipcs pour lister les mémoires partagées
Si vous souhaitez voir la liste des zones partagées on peut utiliser la
commande ipcs
.
[snowcode@snowcode:~]$ gcc mempar.c
[snowcode@snowcode:~]$ ./a.out
[snowcode@snowcode:~]$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000914 4 snowcode 777 1024 0
------ Semaphore Arrays --------
key semid owner perms nsems