1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
| Guillaume Bénétrix
13/07/2004, 12h49
Bonjour Madmac,
Sous linux, il existe plusieurs techniques pour faire communiquer des processus entre eux. Les tubes, les FIFO et les IPC (Inter Communication Process). Comme j'ai pas trouvé en recherchant, voici un petit résumé sur ces techniques. A noter que je n'ai jamais programmé avec les tubes ou les FIFO, par contre les ipc, un peu.
Les TUBES. Un tube est un flux de données à sens uniques entre des processus : toutes données écrites par un processus vers le tube sont acheminées par le noyau vers un autre processus, qui peut ainsi les lire. Les commandes shell (dans le terminal) |, > et < permettent cet acheminement. De même que les commandes systèmes (dans un programme C, C++,...) pipe(), read(), write() et close(). Les tubes sont à sens unique, il en faudra deux pour relier deux processus...
Les FIFO. Depuis la sortie de System V Release 3, les FIFO sont implémentés en tant qu'objets fullduplex (bidirectionnels). Les fichiers FIFO comportent un inode de disque (et donc un nom de fichier accessible par le programme), mais n'utilisent pas de blocs de données. Les données sont stockées dans des tampons du noyau, ce qui est plus performant que d'utiliser des fichiers temporaires.... Un processus créer un FIFO en émettant l'appel système mknod() (qui peut servir à créer presque n'importe quel type fichier, sauf répertoires et sockets). Toutefois mkfifo() existe depuis System V Release 4. Les appels systèmes open(), read(), write() et close() peuvent accèder au FIFO une fois qu'il est créé.
Les IPC : ces techniques permettent à des processus de partager de la mémoire partagée, de se synchroniser grâce à des sémaphores et de s'échanger des messages. Voici comment utiliser la mémoire partagée :
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key, int size, int shmflg);
char *shmat(int shmid, car *shmadrr, int shmflg);
int shmdt(chmadrr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmget() donne l'identifiant du segment ayant la clef key (Pour constituer une clef, utiliser key_t ftok(char *pathname, char project)). Un nouveau segment de taille size est crée si key est IPC_PRIVATE, ou bien si les indicateurs de shmflg conntiennent IPC_CREAT. Combinées, les options IPC_EXCL | IPC_CRET indiquent que le segment ne doit pas exister auparavant. 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ées dans shmflag (SHM_R, SHM_W, SHM_RDONLY). shmadrr précise où ce segment doit être situé dans l'espace mémoire (la valeur NULL demande un emplacement automatique). shmat() renvoie l'adresse où le segment a été placé.
shmdt() sert à détacher le segmentsi on ne l'utilise plus.
shmctl() permet de paramétrer ou de supprimer un segment partagé.
// extraits choisis d'un simulateur d'ordonnanceurs
int id, err;
struct bcp * b;
// Création du segment de mémoire partagée
id = shmget( ftok( ".", '@'), sizeof( struct bcp )+10000, IPC_CREAT | IPC_EXCL | 0777 );
if ( id < 0 )
{
printf("erreur shmget().\n");
exit( -1 );
}
// Attribution de la mémoire partagée à la structure de données.
b = (struct bcp *)shmat( id, 0, SHM_W );
if ( b == NULL )
{
printf("erreur, shmat() == null.");
exit( -1 );
}
// Détachement du segment de la structure.
err = shmctl( bcp->id, IPC_RMID, NULL );
if (err==-1)
err = shmdt(bcp);
printf("kill bcp err = %d.\n", err );
La commande ipcs affiche des informations sur les segments/sémaphores/messages qui existent ; la commande ipcrm les supprime.
[bnet@localhost bnet]$ ipcs
------ Segment de mémoire partagé --------
clé shmid propriétaire perms octets nattch états
0x00000000 32768 bnet 600 393216 2 dest
------ Tableaux de sémaphores --------
clé semid propriétaire perms nsems
------ Queues de messages --------
clé msqid propriétaire perms octets-utilisés messages
Synchroniser des processus :
#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);
La fonction semget() demande à travailler sur le sémaphore généralisé identifié par la clef key (cf. ci-dessus), et qui contient nsems sémaphores individuels. Un nouveau sémaphore est créé, avec les droits données par les 9 bits de poids faible de semflg, si key est IPC_PRIVATE, ou si semflg contient IPC_CREAT.
La fonction 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.
struct sembuf {
...
short sem_num;
short sem_op;
short sem_flg;
}
sem_num est le numéro du sémaphore individuel sur lequel porte l'opération
sem_op est un entier destiné (sauf s'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. Rq : les valeurs des sémaphores ne sont mises à jour que lorsqu'aucun d'eux n'est bloqué.
La fonction 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);
#include <unistd.h>
#include <sys/time.h>
#include <sys/sem.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
// Opération V ( +1 ) sur le 1er sémaphore de l'ensemble d'id sem.
void V( int sem )
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;
// printf("< V(%d) dans pid %d . \n", sem, getpid() );
semop( sem, &buf, 1 );
// printf("V(%d) dans pid %d. > \n", sem, getpid() );
}
// Opération P ( -1 ) sur le 1er sémaphore de l'ensemble d'id sem.
void P( int sem )
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;
// printf("< P(%d) dans pid %d . \n", sem, getpid() );
semop( sem, &buf, 1 );
// printf("P(%d) dans pid %d. > \n", sem, getpid() );
}
int new_semaphore( char id )
{
int _id, err;
// printf("semaphore %d en création.\n", id );
_id = semget( ftok( ".", id ), 1, IPC_CREAT | IPC_EXCL | 0777 );
if ( _id == -1 )
{
printf("semget id : %c.\n", id );
perror( "semget()" );
exit( -1 );
return -1;
}
err = semctl( _id, 0, SETVAL, 1 );
if ( err == -1 )
{
perror( "semclt(SETVAL)" );
exit( -1 );
return -1;
}
// printf("semaphore %d crée ->%d \n", id, _id );
return _id;
}
// détruit l'ensemble de sémaphores identifié par sema.
void kill_semaphore( int sema )
{
// printf("kill semaphore %d.\n", sema );
semctl( sema, 0, IPC_RMID, NULL );
}
Files de messages : ce mécanismes 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 *msgp, int msgflg);
int msgrcv(int msqid, struct *msgp, int msgsz, int 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 clef key et 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 est de la forme :
struct msgbuf {
long mtype;
char mtext[..];
}
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é. Renvoie la taille du corps du message. Là par contre, pas d'exemple.
En espérant que tu y trouvera ton bonheur, je te souhaite bon courage.
++ |
Partager