C Language
Communication interprocessus (IPC)
Recherche…
Introduction
Les mécanismes de communication entre processus (IPC) permettent à différents processus indépendants de communiquer entre eux. La norme C ne fournit aucun mécanisme IPC. Par conséquent, tous ces mécanismes sont définis par le système d'exploitation hôte. POSIX définit un ensemble étendu de mécanismes IPC; Windows définit un autre ensemble; et d'autres systèmes définissent leurs propres variantes.
Sémaphores
Les sémaphores sont utilisés pour synchroniser les opérations entre deux processus ou plus. POSIX définit deux ensembles différents de fonctions de sémaphore:
- 'System V IPC' -
semctl()
,semop()
,semget()
. - «Semaphores POSIX» -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
Cette section décrit les sémaphores System V IPC, appelés ainsi car ils proviennent de Unix System V.
Tout d'abord, vous devez inclure les en-têtes requis. Anciennes versions de POSIX requises #include <sys/types.h>
; POSIX moderne et la plupart des systèmes ne l'exigent pas.
#include <sys/sem.h>
Ensuite, vous devrez définir une clé à la fois pour le parent et pour l'enfant.
#define KEY 0x1111
Cette clé doit être la même dans les deux programmes ou ne pas faire référence à la même structure IPC. Il existe des moyens de générer une clé convenue sans coder en dur sa valeur.
Ensuite, en fonction de votre compilateur, vous pouvez ou non avoir besoin de faire cette étape: déclarer une union pour les opérations de sémaphore.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
Ensuite, définissez vos semwait
try ( semwait
) et raise ( semsignal
). Les noms P et V proviennent du néerlandais
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Maintenant, commencez par obtenir l'identifiant de votre sémaphore IPC.
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
Dans le parent, initialisez le sémaphore pour avoir un compteur de 1.
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
Maintenant, vous pouvez décrémenter ou incrémenter le sémaphore selon vos besoins. Au début de votre section critique, vous décrémentez le compteur en utilisant la fonction semop()
:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Pour incrémenter le sémaphore, vous utilisez &v
au lieu de &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Notez que chaque fonction retourne 0
en cas de succès et -1
en cas d'échec. Ne pas vérifier ces états de retour peut entraîner des problèmes dévastateurs.
Exemple 1.1: Course avec des threads
Le programme ci-dessous aura un processus de fork
un enfant et parent et enfant tenteront d'imprimer des caractères sur le terminal sans aucune synchronisation.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
Sortie (1er tour):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2ème manche):
aabbccAABddBCeeCffgDDghEEhFFGGHH
Compiler et exécuter ce programme devrait vous donner un résultat différent à chaque fois.
Exemple 1.2: Évitez les courses avec les sémaphores
En modifiant l' exemple 1.1 pour utiliser les sémaphores, nous avons:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
Sortie:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Compiler et exécuter ce programme vous donnera le même résultat à chaque fois.