Page suivante Page précédente Table des matières

15. Communication entre processus (IPC System V)

Les mécanismes de communication entre processus (InterProcess Communication, ou IPC) d'Unix System V ont été repris dans de nombreuses variantes d'Unix. Il y a 3 mécanismes :

Ces trois types d'objets sont identifiés par des clés.

15.1 ftok() constitution d'une clé

# include <sys/types.h>
# include <sys/ipc.h>

key_t ftok ( char *pathname, char project )

La fonction ftok() constitue une clé à partir d'un chemin d'accès et d'un caractère indiquant un « projet ». Plutôt que de risquer une explication abstraite, étudions deux cas fréquents :

15.2 Mémoires partagées

Ce mécanisme permet à plusieurs programmes de partager des segments mémoire. Chaque segment mémoire est identifié, au niveau du système, par une clé à laquelle correspond un identifiant. Lorsqu'un segment est attaché à un programme, les données qu'il contient sont accessibles en mémoire par l'intermédiaire d'un pointeur.

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, int size, int shmflg);
char *shmat (int shmid, char *shmaddr, int shmflg )
int shmdt (char *shmaddr)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

La fonction shmget() donne l'identifiant du segment ayant la clé key. Un nouveau segment (de taille size) est créé si key est IPC_PRIVATE, ou bien si les indicateurs de shmflg contiennent IPC_CREAT. Combinées, les options IPC_EXCL | IPC_CREAT indiquent que le segment ne doit pas exister préalablement. Les bits de poids faible de shmflg indiquent les droits d'accès.

shmat() attache le segment shmid en mémoire, avec les droits spécifiés dans shmflag (SHM_R, SHM_W, SHM_RDONLY). shmaddr précise où ce segment doit être situé dans l'espace mémoire (la valeur NULL demande un placement automatique). shmat() renvoie l'adresse où le segment a été placé.

shmdt() ``libère'' le segment. shmctl() permet diverses opérations, dont la destruction d'une mémoire partagée (voir exemple).

Exemple (deux programmes):

Le producteur :


 
  1     /* prod.c */

  2     /*
  3        Ce programme lit une suite de nombres, et effectue le cumul dans une
  4        variable en mémoire partagée. */

  5     #include <sys/ipc.h>
  6     #include <sys/shm.h>
  7     #include <sys/types.h>
  8     #include <stdlib.h>
  9     #include <stdio.h>
 10     #include <errno.h>

 11     struct donnees {
 12             int nb;
 13             int total;
 14     };

 15     int main(void)
 16     {
 17             key_t cle;
 18             int id;
 19             struct donnees *commun;
 20             int reponse;

 21             cle = ftok(getenv("HOME"), 'A');
 22             if (cle == -1) {
 23                     perror("ftok");
 24                     exit(EXIT_FAILURE);
 25             }

 26             id = shmget(cle, sizeof(struct donnees),
 27                         IPC_CREAT | IPC_EXCL | 0666);
 28             if (id == -1) {
 29                     switch (errno) {
 30                     case EEXIST:
 31                             fprintf(stderr, "Le segment existe déjà\n");
 32                             break;
 33                     default:
 34                             perror("shmget");
 35                             break;
 36                     }
 37                     exit(EXIT_FAILURE);
 38             }

 39             commun = (struct donnees *) shmat(id, NULL, SHM_R | SHM_W);
 40             if (commun == NULL) {
 41                     perror("shmat");
 42                     exit(EXIT_FAILURE);
 43             }

 44             commun->nb = 0;
 45             commun->total = 0;

 46             while (1) {
 47                     printf("+ ");
 48                     if (scanf("%d", &reponse) != 1)
 49                             break;
 50                     commun->nb++;
 51                     commun->total += reponse;
 52                     printf("sous-total %d= %d\n", commun->nb,
 53                            commun->total);
 54             };
 55             printf("---\n");

 56             if (shmdt((char *) commun) == -1) {
 57                     perror("shmdt");
 58                     exit(EXIT_FAILURE);
 59             }
 60             /* suppression segment */
 61             if (shmctl(id, IPC_RMID, NULL) == -1) {
 62                     perror("shmctl(remove)");
 63                     exit(EXIT_FAILURE);
 64             };
 65             exit(EXIT_SUCCESS);
 66     }
 

Le consommateur :


 
  1     /* cons.c */

  2     /*
  3        Ce programme affiche périodiquement le contenu de la 
  4        mémoire partagée. Arrêt par Contrôle-C
  5     */

  6     #include <sys/ipc.h>
  7     #include <sys/shm.h>
  8     #include <sys/types.h>
  9     #include <unistd.h>
 10     #include <stdlib.h>
 11     #include <stdio.h>
 12     #include <errno.h>
 13     #include <signal.h>

 14     #define DELAI 2

 15     struct donnees {
 16             int nb;
 17             int total;
 18     };

 19     int encore;

 20     void arret(int signal)
 21     {
 22             encore = 0;
 23     }

 24     int main(void)
 25     {
 26             key_t cle;
 27             int id;
 28             struct donnees *commun;
 29             struct sigaction a;

 30             cle = ftok(getenv("HOME"), 'A');
 31             if (cle == -1) {
 32                     perror("ftok");
 33                     exit(EXIT_FAILURE);
 34             }

 35             id = shmget(cle, sizeof(struct donnees), 0);
 36             if (id == -1) {
 37                     switch (errno) {
 38                     case ENOENT:
 39                             printf("pas de segment\n");
 40                             exit(EXIT_SUCCESS);
 41                     default:
 42                             perror("shmget");
 43                             exit(EXIT_FAILURE);
 44                     }
 45             }
 46             commun = (struct donnees *) shmat(id, NULL, SHM_R);
 47             if (commun == NULL) {
 48                     perror("shmat");
 49                     exit(EXIT_FAILURE);
 50             }

 51             encore = 1;

 52             a.sa_handler = arret;
 53             sigemptyset(&a.sa_mask);
 54             a.sa_flags = 0;
 55             sigaction(SIGINT, &a, NULL);

 56             while (encore) {
 57                     sleep(DELAI);
 58                     printf("sous-total %d= %d\n", commun->nb,
 59                            commun->total);
 60             }

 61             printf("---\n");
 62             if (shmdt((char *) commun) == -1) {
 63                     perror("shmdt");
 64                     exit(EXIT_FAILURE);
 65             }
 66             exit(EXIT_SUCCESS);
 67     }
 

Question : le second programme n'affiche pas forcément des informations cohérentes. Pourquoi ? Qu'y faire ?

Problème : écrire deux programmes qui partagent deux variables i, j. Voici le pseudo-code:

processus P1                    processus P2
| i=0 j=0                       | tant que i==j 
| repeter indefiniment          |   faire rien
|   i++ j++                     | ecrire i
fin                             fin
Au bout de combien de temps le processus P2 s'arrête-t-il ? Faire plusieurs essais.

Exercice : la commande ipcs affiche des informations sur les segments qui existent. Ecrire une commande qui permet d'afficher le contenu d'un segment (on donne le shmid et la longueur en paramètres).

15.3 Sémaphores

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>   

int semget(key_t key, int nsems, int semflg )
int semop(int semid, struct sembuf *sops, unsigned nsops)
int semctl(int semid, int semnum, int cmd, union semun arg )

Les opérations System V travaillent en fait sur des tableaux de sémaphores généralisés (pouvant évoluer par une valeur entière quelconque).

La fonction semget() demande à travailler sur le sémaphore généralisé qui est identifié par la clé key (même notion que pour les clés des segments partagés) et qui contient nsems sémaphores individuels. Un nouveau sémaphore est créé, avec les droits donnés par les 9 bits de poids faible de semflg, si key est IPC_PRIVATE, ou si semflg contient IPC_CREAT.

semop() agit sur le sémaphore semid en appliquant simultanément à plusieurs sémaphores individuels les actions décrites dans les nsops premiers éléments du tableau sops. Chaque sembuf est une structure de la forme:

struct sembuf
{ 
  ...
  short sem_num;  /* semaphore number: 0 = first */
  short sem_op;   /* semaphore operation */
  short sem_flg;  /* operation flags */
  ...
}
sem_flg est une combinaison d'indicateur qui peut contenir IPC_NOWAIT et SEM_UNDO (voir manuel). Ici nous supposons que sem_flg est 0.

sem_num indique le numéro du sémaphore individuel sur lequel porte l'opération. sem_op est un entier destiné (sauf si il est nul) à être ajouté à la valeur courante semval du sémaphore. L'opération se bloque si sem_op + semval < 0.

Cas particulier : si sem_op est 0, l'opération est bloquée tant que semval est non nul.

Les valeurs des sémaphores ne sont mises à jour que lorsque aucun d'eux n'est bloqué.

semctl permet de réaliser diverses opérations sur les sémaphores, selon la commande demandée. En particulier, on peut fixer le n-ième sémaphore à la valeur val en faisant :

semctl(sem,n,SETVAL,val);

Exemple: primitives sur les sémaphores traditionnels.


 
  1     /* sem.c */

  2     /* 
  3        Opérations sur des sémaphores 
  4     */

  5     #include <sys/types.h>
  6     #include <sys/ipc.h>
  7     #include <sys/sem.h>
  8     #include <unistd.h>
  9     #include <stdio.h>
 10     #include <stdlib.h>
 11     #include <ctype.h>

 12     typedef int SEMAPHORE;

 13     void detruire_sem(SEMAPHORE sem)
 14     {
 15             if (semctl(sem, 0, IPC_RMID, 0) != 0) {
 16                     perror("detruire_sem");
 17                     exit(EXIT_FAILURE);
 18             };
 19     }

 20     void changer_sem(SEMAPHORE sem, int val)
 21     {
 22             struct sembuf sb[1];
 23             sb[0].sem_num = 0;
 24             sb[0].sem_op = val;
 25             sb[0].sem_flg = 0;
 26             if (semop(sem, sb, 1) != 0) {
 27                     perror("changer_sem");
 28                     exit(EXIT_FAILURE);
 29             };
 30     }

 31     SEMAPHORE creer_sem(key_t key)
 32     {
 33             SEMAPHORE sem;
 34             int r;

 35             sem = semget(key, 1, IPC_CREAT | 0666);
 36             if (sem < 0) {
 37                     perror("creer_sem");
 38                     exit(EXIT_FAILURE);
 39             };
 40             r = semctl(sem, 0, SETVAL, 0);  /* valeur initiale = 0 */
 41             if (r < 0) {
 42                     perror("initialisation sémaphore");
 43                     exit(EXIT_FAILURE);
 44             };
 45             return sem;
 46     }

 47     void P(SEMAPHORE sem)
 48     {
 49             changer_sem(sem, -1);
 50     }

 51     void V(SEMAPHORE sem)
 52     {
 53             changer_sem(sem, 1);
 54     }

 55     /* --------------------------------------------- */

 56     int main(int argc, char *argv[])
 57     {
 58             SEMAPHORE sem;
 59             key_t key;
 60             int encore = 1;

 61             if (argc != 2) {
 62                     fprintf(stderr, "Usage: %s cle\n", argv[0]);
 63                     exit(EXIT_FAILURE);
 64             };
 65             key = atoi(argv[1]);
 66             sem = creer_sem(key);
 67             while (encore) {
 68                     char reponse;
 69                     printf("p,v,x,q ? ");
 70                     if (scanf("%c", &reponse) != 1)
 71                             break;
 72                     switch (toupper(reponse)) {
 73                     case 'P':
 74                             P(sem);
 75                             printf("Ok.\n");
 76                             break;
 77                     case 'V':
 78                             V(sem);
 79                             printf("Ok.\n");
 80                             break;
 81                     case 'X':
 82                             detruire_sem(sem);
 83                             printf("Sémaphore détruit\n");
 84                             encore = 0;
 85                             break;
 86                     case 'Q':
 87                             encore = 0;
 88                             break;
 89                     default:
 90                             printf("?\n");
 91                     };
 92             };
 93             printf("Bye.\n");
 94             exit(EXIT_SUCCESS);
 95     }
 

Exercice : que se passe-t-il si on essaie d'interrompre semop() ?

Exercice : utilisez les sémaphores pour ``sécuriser'' l'exemple présenté sur les mémoires partagées.

15.4 Files de messages

Ce mécanisme permet l'échange de messages par des processus. Chaque message possède un corps de longueur variable, et un type (entier strictement positif) qui peut servir à préciser la nature des informations contenues dans le corps.

Au moment de la réception, on peut choisir de sélectionner les messages d'un type donné.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget (key_t key, int msgflg)
int msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
int msgrcv (int msqid, struct msgbuf *msgp, int msgsz, 
                       long msgtyp, int msgflg)
int msgctl ( int msqid, int  cmd, struct msqid_ds *buf )

msgget() demande l'accès à (ou la création de) la file de message avec la clé key. msgget() retourne la valeur de l'identificateur de file.

msgsnd() envoie un message dans la file msqid. Le corps de ce message contient msgsz octets, il est placé, précédé par le type dans le tampon pointé par msgp. Ce tampon de la forme:

struct msgbuf {
     long mtype;     /* message type, must be > 0 */
     char mtext[...] /* message data */
};

msgrcv() lit dans la file un message d'un type donné (si type > 0) ou indifférent (si type==0), et le place dans le tampon pointé par msgp. La taille du corps ne pourra excéder msgsz octets, sinon il sera tronqué. msgrcv() renvoie la taille du corps du message.

Exemple. Deux programmes, l'un pour envoyer des messages (lignes de texte) sur une file avec un type donné, l'autre pour afficher les messages reçus.


 
  1     /* snd.c */

  2     /*
  3        envoi des messages dans une file (IPC System V)
  4     */

  5     #include <errno.h>
  6     #include <stdio.h>
  7     #include <stdlib.h>
  8     #include <stdio.h>

  9     #include <sys/types.h>
 10     #include <sys/ipc.h>
 11     #include <sys/msg.h>

 12     #define MAXTEXTE 1000
 13     struct tampon {
 14             long mtype;
 15             char mtext[MAXTEXTE];
 16     };

 17     int main(int argc, char *argv[])
 18     {
 19             int cle, id, mtype;
 20             if (argc != 3) {
 21                     fprintf(stderr, "Usage: %s clé type\n", argv[0]);
 22                     exit(1);
 23             };
 24             cle = atoi(argv[1]);
 25             mtype = atoi(argv[2]);
 26             id = msgget(cle, 0666);
 27             if (id == -1) {
 28                     perror("msgget");
 29                     exit(EXIT_FAILURE);
 30             };

 31             while (1) {
 32                     int l;
 33                     struct tampon msg;
 34                     printf("> ");
 35                     fgets(msg.mtext, MAXTEXTE, stdin);
 36                     l = strlen(msg.mtext);
 37                     msg.mtype = mtype;
 38                     if (msgsnd(id, (struct msgbuf *) &msg, l + 1, 0) ==
 39                         -1) {
 40                             perror("msgsnd");
 41                             exit(EXIT_FAILURE);
 42                     };
 43             };
 44     }
 


 
  1     /* rcv.c */

  2     /*
  3        affiche les messages qui proviennent 
  4        d'une file (IPC System V)
  5     */

  6     #include <errno.h>
  7     #include <stdio.h>
  8     #include <stdlib.h>
  9     #include <stdio.h>

 10     #include <sys/types.h>
 11     #include <sys/ipc.h>
 12     #include <sys/msg.h>

 13     #define MAXTEXTE 1000
 14     struct tampon {
 15             long mtype;
 16             char mtext[MAXTEXTE];
 17     };

 18     int encore = 1;

 19     int main(int argc, char *argv[])
 20     {
 21             int cle, id;
 22             if (argc != 2) {
 23                     fprintf(stderr, "Usage: %s cle\n", argv[0]);
 24                     exit(1);
 25             };
 26             cle = atoi(argv[1]);
 27             id = msgget(cle, IPC_CREAT | 0666);
 28             if (id == -1) {
 29                     perror("msgget");
 30                     exit(EXIT_FAILURE);
 31             };

 32             while (encore) {
 33                     int l;
 34                     struct tampon msg;
 35                     l = msgrcv(id, (struct msgbuf *) &msg, MAXTEXTE, 0L,
 36                                0);
 37                     if (l == -1) {
 38                             perror("msgrcv");
 39                             exit(EXIT_FAILURE);
 40                     };
 41                     printf("(type=%ld) %s\n", msg.mtype, msg.mtext);
 42             };

 43             exit(EXIT_SUCCESS);
 44     }
 


Page suivante Page précédente Table des matières