Accueil > > > COMMUNICATION INTER PROCESSUS PAR IPC SOUS *NIX
COMMUNICATION INTER PROCESSUS PAR IPC SOUS *NIX
Information sur la source
Description
Ce programme illustre l'utilisation des mécanismes de communication inter processus par IPC avec les systèmes d'exploitation reprenant l'implémentation IPC SYSTEM V. Si vous voulez plus d'information sur ce sujet, je vous invite à lire le tutorial qui va avec et qui est posté sur le même site CS. Ce programme illustre également la mise en place d'un mécanisme de temporisation par l'intermédiaire des signaux et/ou du timer système. Le scénario global est l'envoi de signaux SIGUSR1 par l'emetteur au récepteur. Puis toutes les 2 secondes, les deux processus affichent l'un combien il en a envoyé et l'autre combien il en a reçu. Note : la synchronisation n'est pas réalisée sur l'émission/réception des signaux, pour montrer la faiblesse de ces mécanismes (ils sont plus anciens que les IPC). Comme vous le montrera une exécution, l'émetteur envoie plus de signaux que n'en reçoit le récepteur. L'émetteur envoie ses signaux en rafale, et comme il n'existe qu'un seul "slot" système pour chaque signal, certains sont écrasés avant que le récepteur n'ait le temps de les capter.
Source
- //
- // Fichier d'entête commune : msg.h
- //
- /* type booleen */
- enum { FAUX, VRAI };
-
- /* Mode d'alarme utilisable (alarme simple ou timer) */
- enum { NOALRM, ALARM, TIMER };
-
- /* mode d'ipc utilisable (file de messages ou segment de memoire partage */
- enum { NOIPC, MSG, SHM };
-
- /* type des messages attendus */
- enum { MSGPIDE = 1, MSGPIDR, MSGALRM, MSGIPC, MSGCPT };
-
- /* structure des messages ipc */
- typedef struct bufipc {
- long mtype; /* type de message */
- int info; /* contenu du message */
- } msgipc;
-
- /* structure d'un message par segment de memoire partage */
- typedef struct bufshm {
- int pide; /* pid de l'emetteur */
- int pidr; /* pid du recepteur */
- int ModeAlrm; /* mode d'alarme */
- int cptr; /* nombre de signaux recus */
- } msgshm;
-
- /* 256 octets pour un segment de memoire partage */
- #define TAILLESHM 256
-
-
- //
- // Fichier emetteur.c
- //
- /*
- Petit exercice sur les IPC
- Auteur : Le gaijin
- Fichier : emetteur.c = emetteur de signaux
- Scenario :
-
- 1) Un fork,
- Parent a un compteur de SIGUSR1 recus
- Enfant a un compteur de SIGUSR1 emis
- 2) Enfant envoit l'alarme toute les 2 secondes a Parent
- 3) une fonction unique pour la gestion de tous les signaux
- 4) un gestionnaire global des signaux
-
- 5) Couper le fichier en deux :
- - un avec le code du pere
- - un avec le code du fils
- apres le fork, on fait un exec pour lancer le fils
- on passe le pid du pere dans les arguments
-
- 6) Plus de fork : un emetteur, un recepteur, les IPC
- - emetteur : l'ancien fils
- - recepteur : l'ancien pere
-
- Communication par IPC-file de messages :
- Avant de demarrer la sequence des signaux, les processus se synchronisent :
- - le premier processus lance determine la methode de travail (ALARM ou TIMER)
- et la communique au second.
- - Communication respective de leur PID pour la synchronisation des terminaisons
- - A chaque alarme, Recepteur envoie son compteur de signaux recus par la file
- de messages
-
- Synchronisation par signal :
- le processus qui recoit le signal de terminaison le regenere pour l'autre
- afin de synchroniser leur terminaison
-
- 7) Double canal de communication : file de message et segment de memoire partage :
- - D'abord par la file de message pour savoir quelle sera la methode de
- communication dans la suite du programme (file ou segment)
-
- N.B. : Les deux IPC utilisent la meme cle
-
- Communication par IPC-segment de memoire partages :
- - A chaque alarme, Recepteur ecrit la valeur de son compteur de signaux recus
- dans le segment
-
- 8) Correction des bugs
- Raffinement de la sortie du programme (atexit), pour nettoyer les IPC.
- */
-
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <sys/shm.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <signal.h>
- #include <getopt.h>
-
- #include "msg.h"
-
- /* Macro pour la lisibilite */
- #define ERREUR(msg) { perror(msg); exit(errno); }
-
- /* Qui est le premier processus lance (emetteur ou recepteur) */
- int EmetteurPremier = FAUX;
-
- /* Mode d'alarme (alarme simple ou timer) */
- int ModeAlarme = NOALRM;
-
- /* Mode d'IPC (file de messages ou segment de memoire partage) */
- int ModeIPC = NOIPC;
-
- int cpte = 0; /* compteur de signaux emis */
- int cptr = 0; /* compteur de signaux recus */
-
- int pidr = 0; /* pid du recepteur */
-
- key_t cle = 0; /* cle de l'ipc */
- int msqid = 0; /* dipc de la file de messages */
- int shmid = 0; /* dipc du segment de memoire */
- msgshm *seg = NULL; /* adresse du debut du segment */
-
- msgipc message; /* un message IPC */
-
- struct msqid_ds md; /* pour les stats de la file de messages */
- struct shmid_ds sd; /* pour les stats du segment de memoire */
-
- /* Affichage des usages */
- void AfficherLesUsages (char **argv) {
- printf("Usage : %s [-t(imer)|-a(larm)] [-m(essages)|-s(egment)]\n", argv[0]);
- exit (1);
- }
-
- /* fonction de terminaison du programme */
- void DetruireIPC (void) {
- msgctl(msqid, IPC_RMID, NULL); /* detruire la file de messages */
- if (ModeIPC == SHM )
- shmctl(shmid, IPC_RMID, NULL); /* detruire le segment de memoire */
- }
-
- /* gestionnaire global des signaux */
- void GestSig (int sig, siginfo_t *siginfo, void *pvoid) {
-
- switch (sig) {
- case SIGALRM:
- kill(pidr, SIGALRM); /* envoi du signal de synchro au recepteur */
- switch (ModeIPC) {
- case MSG:
- /* reception du message du recepteur : nombre de signaux recus */
- if (msgrcv (msqid, &message, sizeof(message.info), MSGCPT, 0) == -1 )
- ERREUR("msgrcv emetteur")
- /* mise a jour de cptr en fonction du message recu */
- cptr = message.info;
- break;
- case SHM:
- /* lire le nombre de signaux recus dans le segment */
- while (cptr == seg->cptr); /* synchro optionnelle */
- cptr = seg->cptr; /* lecture du compteur depuis le segment */
- break;
- }
-
- /* rearmement de l'alarme */
- if (ModeAlarme == ALARM) alarm(2);
-
- /* affichage du message apres synchro */
- printf("Emetteur = %8d, Recepteur = %8d\n", cpte, cptr);
- break;
- case SIGINT:
- kill (pidr, SIGINT); /* regenerer le signal pour le recepteur */
- exit(errno);
- break;
- }
- }
-
- int main (int argc, char **argv) {
- struct sigaction act;
- struct itimerval val, oval;
- int c;
-
- /* gestion de la terminaison */
- atexit (DetruireIPC);
-
- /* gestion des arguments */
- if (argc != 1) {
- while ( (c = getopt(argc, argv, "atms")) != EOF ) {
- switch ( c ) {
- case 'a' : ModeAlarme = ALARM; break;
- case 't' : ModeAlarme = TIMER; break;
- case 'm' : ModeIPC = MSG; break;
- case 's' : ModeIPC = SHM; break;
- default : AfficherLesUsages(argv); break;
- }
- }
- }
-
- /* Armement des signaux */
- act.sa_flags = SA_SIGINFO;
- act.sa_sigaction = GestSig;
- sigemptyset (&act.sa_mask);
- if ( sigaction (SIGUSR1, &act, NULL) == -1 ) ERREUR("sigaction SIGUSR1")
- if ( sigaction (SIGALRM, &act, NULL) == -1 ) ERREUR("sigaction SIGALRM")
- if ( sigaction (SIGINT, &act, NULL) == -1 ) ERREUR("sigaction SIGINT")
-
- /* creer ou retrouver l'ipc */
- if ( (cle = ftok ("/bin", 'a')) == -1 ) ERREUR("ftok emetteur")
-
- /* lire d'abord la file de message pour savoir si emetteur est le premier */
- /* processus lance, auquel cas */
- /* il impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
- if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
- ERREUR("msgget emetteur")
-
- /* obtenir les stats de la file de message pour savoir qui est le createur */
- /* et donc qui impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
- if ( msgctl (msqid, IPC_STAT, &md) == -1 )
- ERREUR("msgctl emetteur IPC_STAT")
-
- /* Est-ce qu'un message a deja ete envoye par un autre processus ? */
- if ( md.msg_lspid == 0 ) {
- EmetteurPremier = VRAI;
- /* Emetteur est le createur : il impose le mode d'ipc */
- if ( ModeIPC == NOIPC ) ModeIPC = MSG; /* forcer le mode d'IPC */
- /* preparer le message */
- message.mtype = MSGIPC;
- message.info = ModeIPC;
- /* envoyer le message : mode d'IPC */
- if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
- ERREUR("msgsnd emetteur MSGIPC")
- } else {
- /* Emetteur n'est pas le createur */
- /* lire un autre message : mode d'ipc */
- if ( msgrcv (msqid, &message, sizeof(message.info), MSGIPC, 0) == -1 )
- ERREUR("msgrcv emetteur MSGIPC")
- if ( ModeIPC == NOIPC )
- ModeIPC = message.info;
- else
- if ( ModeIPC != message.info ) {
- printf ("Emetteur n'est pas le createur, mode d'ipc deja donne : %s\n",
- (ModeIPC = message.info) == MSG ? "Message" : "Segment");
- }
- }
-
- switch (ModeIPC) {
- case MSG:
- if ( EmetteurPremier ) {
- /* Emetteur est le createur : il impose le mode d'alarme */
- /* et la communique au recepteur */
- /* envoyer un message : mode d'alarme utilise */
- if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
- /* preparer le message */
- message.mtype = MSGALRM;
- message.info = ModeAlarme;
- /* envoyer le message : mode d'alarme */
- if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
- ERREUR("msgsnd emetteur MSGALRM")
- } else {
- /* Emetteur n'est pas le createur */
- /* lire un autre message : mode d'alarme */
- if ( msgrcv (msqid, &message, sizeof(message.info), MSGALRM, 0) == -1 )
- ERREUR("msgrcv emetteur MSGALRM")
- if ( ModeAlarme == NOALRM )
- ModeAlarme = message.info;
- else
- if ( ModeAlarme != message.info )
- printf ("Emetteur n'est pas le createur, mode d'alarme deja donne : %s\n",
- (ModeAlarme = message.info) == ALARM ? "ALARM" : "TIMER");
- }
-
- /* preparer le message */
- message.mtype = MSGPIDE;
- message.info = getpid();
- /* envoyer le message : pid de l'emetteur */
- if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
- ERREUR("msgsnd emetteur MSGPIDE")
-
- /* attente bloquante du premier message, le pid du recepteur */
- if ( msgrcv (msqid, &message, sizeof(message.info), MSGPIDR, 0) == -1 )
- ERREUR("msgrcv emetteur")
-
- /* initialiser le pidr en fonction du message recu */
- pidr = message.info;
- break;
-
- case SHM:
- /* creer ou retrouver le segment de memoire partage et le mapper */
- if ( (shmid = shmget (cle, TAILLESHM, IPC_CREAT|0660)) == -1 )
- ERREUR("shmget emetteur")
- if ( (seg = (msgshm *) shmat(shmid, NULL, 0)) == NULL )
- ERREUR("shmat emetteur")
- if ( EmetteurPremier ) {
- /* Emetteur est le createur : il impose le mode d'alarme */
- /* et la communique au recepteur */
- if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
- seg->pide = getpid(); /* ecrire le pid de l'emetteur */
- seg->ModeAlrm = ModeAlarme; /* imposer le mode d'alarme */
- while ( !(pidr = seg->pidr) ); /* attendre le pid du recepteur */
- } else {
- seg->pide = getpid(); /* ecrire le pid de l'emetteur */
- while ( !(pidr = seg->pidr) ); /* attendre le pid du recepteur */
- if ( ModeAlarme == NOALRM )
- ModeAlarme = seg->ModeAlrm; /* lire le mode d'alarme du recepteur */
- else
- if ( ModeAlarme != seg->ModeAlrm )
- printf ("Emetteur n'est pas le createur, mode d'alarme deja donne : %s\n",
- (ModeAlarme = seg->ModeAlrm) == ALARM ? "ALARM" : "TIMER");
- }
- break;
- }
-
- /* mise en place du mode d'alarme */
- switch (ModeAlarme) {
- case ALARM :
- alarm(2);
- break;
- case TIMER :
- /* mise en place du timer */
- val.it_interval.tv_sec = 2; /* toutes les deux secondes */
- val.it_interval.tv_usec = 0;
- val.it_value.tv_sec = 2; /* a partir de la 2 eme seconde du prog */
- val.it_value.tv_usec = 0;
- setitimer (ITIMER_REAL, &val, &oval); /* Armement du timer */
- break;
- }
-
- /* message d'initialisation */
- printf ("Emetteur (pid = %-8d) : %s IPC et Mode %s\n", getpid(),
- ModeIPC == MSG ? "Message" : "Segment",
- ModeAlarme == ALARM ? "ALARM" : "TIMER");
-
- /* emission des signaux */
- while (1) {
- kill(pidr,SIGUSR1);
- cpte++;
- }
- }
-
- #undef ERREUR
-
-
- //
- // Fichier recepteur.c
- //
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <sys/shm.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <signal.h>
- #include <getopt.h>
-
- #include "msg.h"
-
- /* Macro pour la lisibilite */
- #define ERREUR(msg) { perror(msg); exit(errno); }
-
- /* Qui est le premier processus lance (emetteur ou recepteur) */
- int RecepteurPremier = FAUX;
-
- /* Mode d'alarme (alarme simple ou timer) */
- int ModeAlarme = NOALRM;
-
- /* Mode d'IPC (file de messages ou segment de memoire partage) */
- int ModeIPC = NOIPC;
-
- int pide = 0; /* pid de l'emetteur */
-
- key_t cle = 0; /* cle de l'ipc */
- int msqid = 0; /* dipc de la file de messages */
- int shmid = 0; /* dipc du segment de memoire */
- msgshm *seg = NULL; /* adresse de debut du segment */
-
- msgipc message; /* un message IPC */
-
- struct msqid_ds md; /* pour les stats de la file de messages */
- struct shmid_ds sd; /* pour les stats du segment de memoire */
-
- /* Affichage des usages */
- void AfficherLesUsages (char **argv) {
- printf("Usage : %s [-t(imer)|-a(larm)] [-m(essages)|-s(egment)]\n", argv[0]);
- exit (1);
- }
-
- /* fonction de terminaison du programme */
- void DetruireIPC (void) {
- msgctl(msqid, IPC_RMID, NULL); /* detruire la file de messages */
- if (ModeIPC == SHM )
- shmctl(shmid, IPC_RMID, NULL); /* detruire le segment de memoire */
- }
-
- /* gestionnaire global des signaux */
- void GestSig (int sig, siginfo_t *siginfo, void *pvoid) {
- static int cptr = 0; /* compteurs de signaux recus */
-
- switch (sig) {
- case SIGUSR1 :
- cptr++;
- break;
- case SIGALRM:
- switch (ModeIPC) {
- case MSG:
- /* preparer le message */
- message.mtype = MSGCPT;
- message.info = cptr;
- /* envoyer le message : nombre de signaux recus */
- if (msgsnd (msqid, &message, sizeof (message.info), 0) == -1)
- ERREUR("msgsnd recepteur MSGCPT")
- break;
- case SHM:
- /* ecriture du nombre de signaux recus dans le segment */
- seg->cptr = cptr;
- break;
- }
- break;
- case SIGINT:
- kill (pide, SIGINT); /* regenerer le signal pour l'emetteur */
- exit(errno);
- break;
- }
- }
-
- int main (int argc, char **argv) {
- struct sigaction act;
- struct itimerval val, oval;
- int c;
-
- /* gestion de la terminaison */
- atexit (DetruireIPC);
-
- /* gestion des arguments */
- if (argc != 1)
- while ( (c = getopt(argc, argv, "atms")) != EOF ) {
- switch ( c ) {
- case 'a' : ModeAlarme = ALARM; break;
- case 't' : ModeAlarme = TIMER; break;
- case 'm' : ModeIPC = MSG; break;
- case 's' : ModeIPC = SHM; break;
- default : AfficherLesUsages(argv); break;
- }
- }
-
- /* Armement des signaux */
- act.sa_flags = SA_SIGINFO;
- act.sa_sigaction = GestSig;
- sigemptyset (&act.sa_mask);
- if ( sigaction (SIGUSR1, &act, NULL) == -1 ) ERREUR("sigaction SIGUSR1")
- if ( sigaction (SIGALRM, &act, NULL) == -1 ) ERREUR("sigaction SIGALRM")
- if ( sigaction (SIGINT, &act, NULL) == -1 ) ERREUR("sigaction SIGINT")
-
- /* creer ou retrouver l'ipc */
- if ( (cle = ftok ("/bin", 'a')) == -1 ) ERREUR("ftok recepteur")
-
- /* lire d'abord la file de message pour savoir si recepteur est le premier */
- /* processus lance, auquel cas */
- /* il impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
- if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
- ERREUR("msgget recepteur")
-
- /* obtenir les stats de la file de message pour savoir qui est le createur */
- /* et donc qui impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
- if ( msgctl (msqid, IPC_STAT, &md) == -1 )
- ERREUR("msgctl recepteur IPC_STAT")
-
- /* Est-ce qu'un message a deja ete envoye par un autre processus ? */
- if ( md.msg_lspid == 0 ) {
- RecepteurPremier = VRAI;
- /* Recepteur est le createur : il impose le mode d'ipc */
- if ( ModeIPC == NOIPC ) ModeIPC = MSG; /* forcer le mode d'IPC */
- /* preparer le message */
- message.mtype = MSGIPC;
- message.info = ModeIPC;
- /* envoyer le message : mode d'IPC */
- if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
- ERREUR("msgsnd recepteur MSGIPC")
- } else {
- /* Recepteur n'est pas le createur */
- /* lire un autre message : mode d'ipc */
- if ( msgrcv (msqid, &message, sizeof(message.info), MSGIPC, 0) == -1 )
- ERREUR("msgrcv recepteur MSGIPC")
- if ( ModeIPC == NOIPC )
- ModeIPC = message.info;
- else
- if ( ModeIPC != message.info ) {
- printf ("Recepteur n'est pas le createur, mode d'ipc deja donne : %s\n",
- (ModeIPC = message.info) == MSG ? "Message" : "Segment");
- }
- }
-
- switch (ModeIPC) {
- case MSG:
- if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
- ERREUR("msgget recepteur")
-
- /* obtenir les stats de la liste d'ipc pour savoir qui est le createur */
- /* et donc qui impose le mode d'alarme (alarm ou timer) */
- if ( msgctl (msqid, IPC_STAT, &md) == -1 )
- ERREUR("msgctl emetteur IPC_STAT")
-
- if ( RecepteurPremier ) {
- /* Recepteur est le createur : il impose le mode d'alarme */
- /* et la communique au recepteur */
- /* envoyer un message : mode d'alarme utilise */
- if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
- /* preparer le message */
- message.mtype = MSGALRM;
- message.info = ModeAlarme;
- /* envoyer le message : mode d'alarme */
- if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
- ERREUR("msgsnd recepteur MSGALRM")
- } else {
- /* Recepteur n'est pas le createur */
- /* lire un message : mode d'alarme */
- if ( msgrcv (msqid, &message, sizeof(message.info), MSGALRM, 0) == -1 )
- ERREUR("msgrcv recepteur MSGALRM")
- if ( ModeAlarme == NOALRM )
- ModeAlarme = message.info;
- else
- if ( ModeAlarme != message.info )
- printf ("Recepteur n'est pas le createur, mode d'alarme deja donne : %s\n",
- (ModeAlarme = message.info) == ALARM ? "ALARM": "TIMER");
- }
-
- /* preparer le message */
- message.mtype = MSGPIDR;
- message.info = getpid();
- /* envoyer le message : pid du recepteur */
- if (msgsnd (msqid, &message, sizeof (message.info), 0) == -1)
- ERREUR("msgsnd recepteur MSGPIDR")
-
- /* attente bloquante du pid de l'emetteur */
- if (msgrcv (msqid, &message, sizeof(message.info), MSGPIDE, 0) == -1)
- ERREUR("msgrcv recepteur")
-
- /* initialiser le pide en fonction du message recu */
- pide = message.info;
- break;
-
- case SHM:
- /* creer ou retrouver le segment de memoire partage et le mapper */
- if ( (shmid = shmget (cle, TAILLESHM, IPC_CREAT|0660)) == -1 )
- ERREUR("shmget recepteur")
- if ( (seg = (msgshm *) shmat(shmid, NULL, 0)) == NULL )
- ERREUR("shmat recepteur")
- if ( RecepteurPremier ) {
- /* Recepteur est le createur : il impose le mode d'alarme */
- /* et la communique a l'emetteur */
- if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
- seg->pidr = getpid(); /* ecrire le pid de l'emetteur */
- seg->ModeAlrm = ModeAlarme; /* imposer le mode d'alarme */
- while ( !(pide = seg->pide) ); /* attendre que le pid de l'emetteur */
- } else {
- seg->pidr = getpid(); /* ecrire le pid du recepteur */
- while ( !(pide = seg->pide) ); /* attendre que le pid de l'emetteur */
- if ( ModeAlarme == NOALRM )
- ModeAlarme = seg->ModeAlrm; /* lire le mode d'alarme du recepteur */
- else
- if ( ModeAlarme != seg->ModeAlrm )
- printf ("Recepteur n'est pas le createur, mode d'alarme deja donne : %s\n",
- (ModeAlarme = seg->ModeAlrm) == ALARM ? "ALARM" : "TIMER");
- }
- break;
- }
-
- /* message d'initialisation */
- printf ("Recepteur (pid = %-8d) : %s IPC et Mode %s\n", getpid(),
- ModeIPC == MSG ? "Message" : "Segment",
- ModeAlarme == ALARM ? "ALARM" : "TIMER");
-
- /* attente des signaux et de la terminaison de l'emetteur */
- while (1) pause();
- }
-
- #undef ERREUR
-
//
// Fichier d'entête commune : msg.h
//
/* type booleen */
enum { FAUX, VRAI };
/* Mode d'alarme utilisable (alarme simple ou timer) */
enum { NOALRM, ALARM, TIMER };
/* mode d'ipc utilisable (file de messages ou segment de memoire partage */
enum { NOIPC, MSG, SHM };
/* type des messages attendus */
enum { MSGPIDE = 1, MSGPIDR, MSGALRM, MSGIPC, MSGCPT };
/* structure des messages ipc */
typedef struct bufipc {
long mtype; /* type de message */
int info; /* contenu du message */
} msgipc;
/* structure d'un message par segment de memoire partage */
typedef struct bufshm {
int pide; /* pid de l'emetteur */
int pidr; /* pid du recepteur */
int ModeAlrm; /* mode d'alarme */
int cptr; /* nombre de signaux recus */
} msgshm;
/* 256 octets pour un segment de memoire partage */
#define TAILLESHM 256
//
// Fichier emetteur.c
//
/*
Petit exercice sur les IPC
Auteur : Le gaijin
Fichier : emetteur.c = emetteur de signaux
Scenario :
1) Un fork,
Parent a un compteur de SIGUSR1 recus
Enfant a un compteur de SIGUSR1 emis
2) Enfant envoit l'alarme toute les 2 secondes a Parent
3) une fonction unique pour la gestion de tous les signaux
4) un gestionnaire global des signaux
5) Couper le fichier en deux :
- un avec le code du pere
- un avec le code du fils
apres le fork, on fait un exec pour lancer le fils
on passe le pid du pere dans les arguments
6) Plus de fork : un emetteur, un recepteur, les IPC
- emetteur : l'ancien fils
- recepteur : l'ancien pere
Communication par IPC-file de messages :
Avant de demarrer la sequence des signaux, les processus se synchronisent :
- le premier processus lance determine la methode de travail (ALARM ou TIMER)
et la communique au second.
- Communication respective de leur PID pour la synchronisation des terminaisons
- A chaque alarme, Recepteur envoie son compteur de signaux recus par la file
de messages
Synchronisation par signal :
le processus qui recoit le signal de terminaison le regenere pour l'autre
afin de synchroniser leur terminaison
7) Double canal de communication : file de message et segment de memoire partage :
- D'abord par la file de message pour savoir quelle sera la methode de
communication dans la suite du programme (file ou segment)
N.B. : Les deux IPC utilisent la meme cle
Communication par IPC-segment de memoire partages :
- A chaque alarme, Recepteur ecrit la valeur de son compteur de signaux recus
dans le segment
8) Correction des bugs
Raffinement de la sortie du programme (atexit), pour nettoyer les IPC.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include "msg.h"
/* Macro pour la lisibilite */
#define ERREUR(msg) { perror(msg); exit(errno); }
/* Qui est le premier processus lance (emetteur ou recepteur) */
int EmetteurPremier = FAUX;
/* Mode d'alarme (alarme simple ou timer) */
int ModeAlarme = NOALRM;
/* Mode d'IPC (file de messages ou segment de memoire partage) */
int ModeIPC = NOIPC;
int cpte = 0; /* compteur de signaux emis */
int cptr = 0; /* compteur de signaux recus */
int pidr = 0; /* pid du recepteur */
key_t cle = 0; /* cle de l'ipc */
int msqid = 0; /* dipc de la file de messages */
int shmid = 0; /* dipc du segment de memoire */
msgshm *seg = NULL; /* adresse du debut du segment */
msgipc message; /* un message IPC */
struct msqid_ds md; /* pour les stats de la file de messages */
struct shmid_ds sd; /* pour les stats du segment de memoire */
/* Affichage des usages */
void AfficherLesUsages (char **argv) {
printf("Usage : %s [-t(imer)|-a(larm)] [-m(essages)|-s(egment)]\n", argv[0]);
exit (1);
}
/* fonction de terminaison du programme */
void DetruireIPC (void) {
msgctl(msqid, IPC_RMID, NULL); /* detruire la file de messages */
if (ModeIPC == SHM )
shmctl(shmid, IPC_RMID, NULL); /* detruire le segment de memoire */
}
/* gestionnaire global des signaux */
void GestSig (int sig, siginfo_t *siginfo, void *pvoid) {
switch (sig) {
case SIGALRM:
kill(pidr, SIGALRM); /* envoi du signal de synchro au recepteur */
switch (ModeIPC) {
case MSG:
/* reception du message du recepteur : nombre de signaux recus */
if (msgrcv (msqid, &message, sizeof(message.info), MSGCPT, 0) == -1 )
ERREUR("msgrcv emetteur")
/* mise a jour de cptr en fonction du message recu */
cptr = message.info;
break;
case SHM:
/* lire le nombre de signaux recus dans le segment */
while (cptr == seg->cptr); /* synchro optionnelle */
cptr = seg->cptr; /* lecture du compteur depuis le segment */
break;
}
/* rearmement de l'alarme */
if (ModeAlarme == ALARM) alarm(2);
/* affichage du message apres synchro */
printf("Emetteur = %8d, Recepteur = %8d\n", cpte, cptr);
break;
case SIGINT:
kill (pidr, SIGINT); /* regenerer le signal pour le recepteur */
exit(errno);
break;
}
}
int main (int argc, char **argv) {
struct sigaction act;
struct itimerval val, oval;
int c;
/* gestion de la terminaison */
atexit (DetruireIPC);
/* gestion des arguments */
if (argc != 1) {
while ( (c = getopt(argc, argv, "atms")) != EOF ) {
switch ( c ) {
case 'a' : ModeAlarme = ALARM; break;
case 't' : ModeAlarme = TIMER; break;
case 'm' : ModeIPC = MSG; break;
case 's' : ModeIPC = SHM; break;
default : AfficherLesUsages(argv); break;
}
}
}
/* Armement des signaux */
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = GestSig;
sigemptyset (&act.sa_mask);
if ( sigaction (SIGUSR1, &act, NULL) == -1 ) ERREUR("sigaction SIGUSR1")
if ( sigaction (SIGALRM, &act, NULL) == -1 ) ERREUR("sigaction SIGALRM")
if ( sigaction (SIGINT, &act, NULL) == -1 ) ERREUR("sigaction SIGINT")
/* creer ou retrouver l'ipc */
if ( (cle = ftok ("/bin", 'a')) == -1 ) ERREUR("ftok emetteur")
/* lire d'abord la file de message pour savoir si emetteur est le premier */
/* processus lance, auquel cas */
/* il impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
ERREUR("msgget emetteur")
/* obtenir les stats de la file de message pour savoir qui est le createur */
/* et donc qui impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
if ( msgctl (msqid, IPC_STAT, &md) == -1 )
ERREUR("msgctl emetteur IPC_STAT")
/* Est-ce qu'un message a deja ete envoye par un autre processus ? */
if ( md.msg_lspid == 0 ) {
EmetteurPremier = VRAI;
/* Emetteur est le createur : il impose le mode d'ipc */
if ( ModeIPC == NOIPC ) ModeIPC = MSG; /* forcer le mode d'IPC */
/* preparer le message */
message.mtype = MSGIPC;
message.info = ModeIPC;
/* envoyer le message : mode d'IPC */
if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
ERREUR("msgsnd emetteur MSGIPC")
} else {
/* Emetteur n'est pas le createur */
/* lire un autre message : mode d'ipc */
if ( msgrcv (msqid, &message, sizeof(message.info), MSGIPC, 0) == -1 )
ERREUR("msgrcv emetteur MSGIPC")
if ( ModeIPC == NOIPC )
ModeIPC = message.info;
else
if ( ModeIPC != message.info ) {
printf ("Emetteur n'est pas le createur, mode d'ipc deja donne : %s\n",
(ModeIPC = message.info) == MSG ? "Message" : "Segment");
}
}
switch (ModeIPC) {
case MSG:
if ( EmetteurPremier ) {
/* Emetteur est le createur : il impose le mode d'alarme */
/* et la communique au recepteur */
/* envoyer un message : mode d'alarme utilise */
if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
/* preparer le message */
message.mtype = MSGALRM;
message.info = ModeAlarme;
/* envoyer le message : mode d'alarme */
if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
ERREUR("msgsnd emetteur MSGALRM")
} else {
/* Emetteur n'est pas le createur */
/* lire un autre message : mode d'alarme */
if ( msgrcv (msqid, &message, sizeof(message.info), MSGALRM, 0) == -1 )
ERREUR("msgrcv emetteur MSGALRM")
if ( ModeAlarme == NOALRM )
ModeAlarme = message.info;
else
if ( ModeAlarme != message.info )
printf ("Emetteur n'est pas le createur, mode d'alarme deja donne : %s\n",
(ModeAlarme = message.info) == ALARM ? "ALARM" : "TIMER");
}
/* preparer le message */
message.mtype = MSGPIDE;
message.info = getpid();
/* envoyer le message : pid de l'emetteur */
if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
ERREUR("msgsnd emetteur MSGPIDE")
/* attente bloquante du premier message, le pid du recepteur */
if ( msgrcv (msqid, &message, sizeof(message.info), MSGPIDR, 0) == -1 )
ERREUR("msgrcv emetteur")
/* initialiser le pidr en fonction du message recu */
pidr = message.info;
break;
case SHM:
/* creer ou retrouver le segment de memoire partage et le mapper */
if ( (shmid = shmget (cle, TAILLESHM, IPC_CREAT|0660)) == -1 )
ERREUR("shmget emetteur")
if ( (seg = (msgshm *) shmat(shmid, NULL, 0)) == NULL )
ERREUR("shmat emetteur")
if ( EmetteurPremier ) {
/* Emetteur est le createur : il impose le mode d'alarme */
/* et la communique au recepteur */
if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
seg->pide = getpid(); /* ecrire le pid de l'emetteur */
seg->ModeAlrm = ModeAlarme; /* imposer le mode d'alarme */
while ( !(pidr = seg->pidr) ); /* attendre le pid du recepteur */
} else {
seg->pide = getpid(); /* ecrire le pid de l'emetteur */
while ( !(pidr = seg->pidr) ); /* attendre le pid du recepteur */
if ( ModeAlarme == NOALRM )
ModeAlarme = seg->ModeAlrm; /* lire le mode d'alarme du recepteur */
else
if ( ModeAlarme != seg->ModeAlrm )
printf ("Emetteur n'est pas le createur, mode d'alarme deja donne : %s\n",
(ModeAlarme = seg->ModeAlrm) == ALARM ? "ALARM" : "TIMER");
}
break;
}
/* mise en place du mode d'alarme */
switch (ModeAlarme) {
case ALARM :
alarm(2);
break;
case TIMER :
/* mise en place du timer */
val.it_interval.tv_sec = 2; /* toutes les deux secondes */
val.it_interval.tv_usec = 0;
val.it_value.tv_sec = 2; /* a partir de la 2 eme seconde du prog */
val.it_value.tv_usec = 0;
setitimer (ITIMER_REAL, &val, &oval); /* Armement du timer */
break;
}
/* message d'initialisation */
printf ("Emetteur (pid = %-8d) : %s IPC et Mode %s\n", getpid(),
ModeIPC == MSG ? "Message" : "Segment",
ModeAlarme == ALARM ? "ALARM" : "TIMER");
/* emission des signaux */
while (1) {
kill(pidr,SIGUSR1);
cpte++;
}
}
#undef ERREUR
//
// Fichier recepteur.c
//
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include "msg.h"
/* Macro pour la lisibilite */
#define ERREUR(msg) { perror(msg); exit(errno); }
/* Qui est le premier processus lance (emetteur ou recepteur) */
int RecepteurPremier = FAUX;
/* Mode d'alarme (alarme simple ou timer) */
int ModeAlarme = NOALRM;
/* Mode d'IPC (file de messages ou segment de memoire partage) */
int ModeIPC = NOIPC;
int pide = 0; /* pid de l'emetteur */
key_t cle = 0; /* cle de l'ipc */
int msqid = 0; /* dipc de la file de messages */
int shmid = 0; /* dipc du segment de memoire */
msgshm *seg = NULL; /* adresse de debut du segment */
msgipc message; /* un message IPC */
struct msqid_ds md; /* pour les stats de la file de messages */
struct shmid_ds sd; /* pour les stats du segment de memoire */
/* Affichage des usages */
void AfficherLesUsages (char **argv) {
printf("Usage : %s [-t(imer)|-a(larm)] [-m(essages)|-s(egment)]\n", argv[0]);
exit (1);
}
/* fonction de terminaison du programme */
void DetruireIPC (void) {
msgctl(msqid, IPC_RMID, NULL); /* detruire la file de messages */
if (ModeIPC == SHM )
shmctl(shmid, IPC_RMID, NULL); /* detruire le segment de memoire */
}
/* gestionnaire global des signaux */
void GestSig (int sig, siginfo_t *siginfo, void *pvoid) {
static int cptr = 0; /* compteurs de signaux recus */
switch (sig) {
case SIGUSR1 :
cptr++;
break;
case SIGALRM:
switch (ModeIPC) {
case MSG:
/* preparer le message */
message.mtype = MSGCPT;
message.info = cptr;
/* envoyer le message : nombre de signaux recus */
if (msgsnd (msqid, &message, sizeof (message.info), 0) == -1)
ERREUR("msgsnd recepteur MSGCPT")
break;
case SHM:
/* ecriture du nombre de signaux recus dans le segment */
seg->cptr = cptr;
break;
}
break;
case SIGINT:
kill (pide, SIGINT); /* regenerer le signal pour l'emetteur */
exit(errno);
break;
}
}
int main (int argc, char **argv) {
struct sigaction act;
struct itimerval val, oval;
int c;
/* gestion de la terminaison */
atexit (DetruireIPC);
/* gestion des arguments */
if (argc != 1)
while ( (c = getopt(argc, argv, "atms")) != EOF ) {
switch ( c ) {
case 'a' : ModeAlarme = ALARM; break;
case 't' : ModeAlarme = TIMER; break;
case 'm' : ModeIPC = MSG; break;
case 's' : ModeIPC = SHM; break;
default : AfficherLesUsages(argv); break;
}
}
/* Armement des signaux */
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = GestSig;
sigemptyset (&act.sa_mask);
if ( sigaction (SIGUSR1, &act, NULL) == -1 ) ERREUR("sigaction SIGUSR1")
if ( sigaction (SIGALRM, &act, NULL) == -1 ) ERREUR("sigaction SIGALRM")
if ( sigaction (SIGINT, &act, NULL) == -1 ) ERREUR("sigaction SIGINT")
/* creer ou retrouver l'ipc */
if ( (cle = ftok ("/bin", 'a')) == -1 ) ERREUR("ftok recepteur")
/* lire d'abord la file de message pour savoir si recepteur est le premier */
/* processus lance, auquel cas */
/* il impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
ERREUR("msgget recepteur")
/* obtenir les stats de la file de message pour savoir qui est le createur */
/* et donc qui impose le mode d'ipc et le mode d'alarme (alarme ou timer) */
if ( msgctl (msqid, IPC_STAT, &md) == -1 )
ERREUR("msgctl recepteur IPC_STAT")
/* Est-ce qu'un message a deja ete envoye par un autre processus ? */
if ( md.msg_lspid == 0 ) {
RecepteurPremier = VRAI;
/* Recepteur est le createur : il impose le mode d'ipc */
if ( ModeIPC == NOIPC ) ModeIPC = MSG; /* forcer le mode d'IPC */
/* preparer le message */
message.mtype = MSGIPC;
message.info = ModeIPC;
/* envoyer le message : mode d'IPC */
if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
ERREUR("msgsnd recepteur MSGIPC")
} else {
/* Recepteur n'est pas le createur */
/* lire un autre message : mode d'ipc */
if ( msgrcv (msqid, &message, sizeof(message.info), MSGIPC, 0) == -1 )
ERREUR("msgrcv recepteur MSGIPC")
if ( ModeIPC == NOIPC )
ModeIPC = message.info;
else
if ( ModeIPC != message.info ) {
printf ("Recepteur n'est pas le createur, mode d'ipc deja donne : %s\n",
(ModeIPC = message.info) == MSG ? "Message" : "Segment");
}
}
switch (ModeIPC) {
case MSG:
if ( (msqid = msgget (cle, IPC_CREAT|0660)) == -1 )
ERREUR("msgget recepteur")
/* obtenir les stats de la liste d'ipc pour savoir qui est le createur */
/* et donc qui impose le mode d'alarme (alarm ou timer) */
if ( msgctl (msqid, IPC_STAT, &md) == -1 )
ERREUR("msgctl emetteur IPC_STAT")
if ( RecepteurPremier ) {
/* Recepteur est le createur : il impose le mode d'alarme */
/* et la communique au recepteur */
/* envoyer un message : mode d'alarme utilise */
if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
/* preparer le message */
message.mtype = MSGALRM;
message.info = ModeAlarme;
/* envoyer le message : mode d'alarme */
if ( msgsnd (msqid, &message, sizeof (message.info), 0) == -1 )
ERREUR("msgsnd recepteur MSGALRM")
} else {
/* Recepteur n'est pas le createur */
/* lire un message : mode d'alarme */
if ( msgrcv (msqid, &message, sizeof(message.info), MSGALRM, 0) == -1 )
ERREUR("msgrcv recepteur MSGALRM")
if ( ModeAlarme == NOALRM )
ModeAlarme = message.info;
else
if ( ModeAlarme != message.info )
printf ("Recepteur n'est pas le createur, mode d'alarme deja donne : %s\n",
(ModeAlarme = message.info) == ALARM ? "ALARM": "TIMER");
}
/* preparer le message */
message.mtype = MSGPIDR;
message.info = getpid();
/* envoyer le message : pid du recepteur */
if (msgsnd (msqid, &message, sizeof (message.info), 0) == -1)
ERREUR("msgsnd recepteur MSGPIDR")
/* attente bloquante du pid de l'emetteur */
if (msgrcv (msqid, &message, sizeof(message.info), MSGPIDE, 0) == -1)
ERREUR("msgrcv recepteur")
/* initialiser le pide en fonction du message recu */
pide = message.info;
break;
case SHM:
/* creer ou retrouver le segment de memoire partage et le mapper */
if ( (shmid = shmget (cle, TAILLESHM, IPC_CREAT|0660)) == -1 )
ERREUR("shmget recepteur")
if ( (seg = (msgshm *) shmat(shmid, NULL, 0)) == NULL )
ERREUR("shmat recepteur")
if ( RecepteurPremier ) {
/* Recepteur est le createur : il impose le mode d'alarme */
/* et la communique a l'emetteur */
if ( ModeAlarme == NOALRM ) ModeAlarme = ALARM; /* forcer le mode d'alarme */
seg->pidr = getpid(); /* ecrire le pid de l'emetteur */
seg->ModeAlrm = ModeAlarme; /* imposer le mode d'alarme */
while ( !(pide = seg->pide) ); /* attendre que le pid de l'emetteur */
} else {
seg->pidr = getpid(); /* ecrire le pid du recepteur */
while ( !(pide = seg->pide) ); /* attendre que le pid de l'emetteur */
if ( ModeAlarme == NOALRM )
ModeAlarme = seg->ModeAlrm; /* lire le mode d'alarme du recepteur */
else
if ( ModeAlarme != seg->ModeAlrm )
printf ("Recepteur n'est pas le createur, mode d'alarme deja donne : %s\n",
(ModeAlarme = seg->ModeAlrm) == ALARM ? "ALARM" : "TIMER");
}
break;
}
/* message d'initialisation */
printf ("Recepteur (pid = %-8d) : %s IPC et Mode %s\n", getpid(),
ModeIPC == MSG ? "Message" : "Segment",
ModeAlarme == ALARM ? "ALARM" : "TIMER");
/* attente des signaux et de la terminaison de l'emetteur */
while (1) pause();
}
#undef ERREUR
Conclusion
Pour compiler : make emetteur make recepteur
ou utiliser le script mak (pour les feignants)
Pour tester : emetteur [-options] & recepteur [-option]
ou l'inverse, cela n'a pas d'importance puisque les processus se synchronisent.
Les options sont : -a : utilisation d'une temporisation par la primitive alarm() -t : utilisation d'une temporisation par la primitive setitimer() -m : utilisation d'une file de message -s : utilisation d'un segment de mémoire partagé
NB : il faut un système *NIX, l'implémentation des IPC sous cygwin étant incomplète.
Historique
- 02 juin 2006 07:50:08 :
- Ajout d'une note sur les signaux.
Sources du même auteur
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
communication inter processus sous linux [ par boumarsel ]
voila un processus cree un processus fils par fork, le fils execute un traitement (fonction) et retourne une valeur que je veux recuperer par le proce
timer asynchrone [ par ToToL ]
BonjourJ'ai besoin dans un programme en cour de création d'un timer asynchrone. je m'explique :j'ai besoin d'executer des actions a des heures bien pr
temps d'exécution trop long [ par diable007 ]
bonjour, j'ai une application parallèle en c++ et MPI, j'ai une partie qui ne nécessite pas de communication avec mpi entre les processeurs. En séqu
L'événement est il un IPC ? [ par Booster ]
Bonjour,Voila question à 300€ ^^ que je me suis posé, est ce que les évenements font partie de la catégorie IPC (Inter processus communication) ?Vu qu
Communication Par TCP sous LINUX [ par Smeix ]
Bonjour tout le monde je suis débutant en Linux ,je veux développer un script en mode TCP qui permet envoie d'un message à une adresse ip spécifié c
question sur programmation linux [ par yolhaiji ]
YOL.HAI.JIsalut:je voulais savoir comment sur linux peut on etablir une connexion entre un processus pere et ces deux processus fils avec les pipes (
processus sous linux [g++] [ par satanik_mike ]
Bonjour,Je suis en train de d?velopper un serveur POP, SMTP en C++ sous linux, mais je bloque sur une petit probl?me d'ex?cution.J'ai fait un ex?cutbl
le "kill(0,SIGTERM)" de Linux sous Win32 ? [ par Wulfaz ]
je cherche une fonction C pour winNT & win98, équivalente à celle sous Linux/Unix : kill(0,SIGTERM). Pour ceux qui ne la connaisse pas elle envoie un
[debutant] Interruption timer sous linux [ par PierrotLeCelte ]
Bonjour,Je voudrais ecrire un petit programme ou une fonctionqui s'execute a chaque interruption de l'irq timer(irq = 0). D'abord est ce possible ? Et
timer avec linux[?] [ par dmothes ]
Help!!!quelqu'un pourrait-il m'aider? j'ai besoin d'utiliser un timer sous linux (si possible, en millisecondes)...faut-il utiliser linux/timer.h? com
|
Derniers Blogs
IMAGINE CUP 2012, MAKE A SIGN EN FINALEIMAGINE CUP 2012, MAKE A SIGN EN FINALE par junarnoalg
Voilà qui est fait, la nouvelle est officielle ! L'équipe belge "Make a Sign" va au pays des kangourous défendre son projet dans la catégorie Software Design. http://www.imaginecup.com/CompetitionsContent/Competition/WorldwideFinalists.aspx V...
Cliquez pour lire la suite de l'article par junarnoalg KINECT 1.5 IS OUT !KINECT 1.5 IS OUT ! par Vko
La version 1.5 du Kinect For Microsoft vient tout juste de sortir ! Plein de nouveautés: Tracking de squelette en Near Mode Détection en position assise Détection faciale avec un SDK dédié Documentation et des guideline (enfin) Un out...
Cliquez pour lire la suite de l'article par Vko LES ACTUALITéS DE LA SEMAINE SUR C2I.FR (14 MAI - 20 MAI) LES ACTUALITéS DE LA SEMAINE SUR C2I.FR (14 MAI - 20 MAI) par richardc
Mise à jour des Web API du 14 Mai
Réservez dès maintenant votre journée du 20 juin pour le Windows Azure Dev Camp 2012 à Paris
Mise à jour de Team Foundation Service
MechCommander 2 sur Windows 8
Entity Framework 5 Release Candidate e...
Cliquez pour lire la suite de l'article par richardc REACTIVE EXTENSIONS : CONSOMMER DES SERVICES AVEC RX PARTIE 3, LES PIèGES à éVITERREACTIVE EXTENSIONS : CONSOMMER DES SERVICES AVEC RX PARTIE 3, LES PIèGES à éVITER par Groc
Une mauvaise utilisation de rx lors de l'écriture d'une couche d'accès à des services peut conduire à des cas embarassants avec des erreurs mal gérées, des appels qui ne partent lorsqu'ils le devraient, et même des résultats incorrects . le tout nuis...
Cliquez pour lire la suite de l'article par Groc SHAREPOINT BLOG SITE, PROBLèME D'ARCHIVESSHAREPOINT BLOG SITE, PROBLèME D'ARCHIVES par junarnoalg
Dernièrement, nous avons migré le site
myTIC
vers un nouveau serveur SharePoint 2010. Dans les contenus que nous vouloins récupérer, nous avions un certain nombre de blogs.
Nous avons utilisé les commandes Power...
Cliquez pour lire la suite de l'article par junarnoalg
Forum
MATRICE TEMPLATEMATRICE TEMPLATE par hjr2610
Cliquez pour lire la suite par hjr2610 RE : SAC A DOS RE : SAC A DOS par hadjkaddour
Cliquez pour lire la suite par hadjkaddour
Logiciels
sDEVIS-FACTURES vlPRO (8.1.0.3)SDEVIS-FACTURES VLPRO (8.1.0.3)sDEVIS-FACTURES vlPRO a été mis au point pour les particuliers, créateurs, entrepreneurs, artisa... Cliquez pour télécharger sDEVIS-FACTURES vlPRO 974 Application Server (12.2.4.6)974 APPLICATION SERVER (12.2.4.6)Développez de puissantes applications dans un environnement de 'cloud computing', clusterisé, séc... Cliquez pour télécharger 974 Application Server vPicture (1.4.2.1)VPICTURE (1.4.2.1)Avec vPicture, hébergez vos images facilement et rapidement.
vPicture est un utilitaire simple, ... Cliquez pour télécharger vPicture Easy-Planning (2.2.1.6)EASY-PLANNING (2.2.1.6)Easy-Planning permet de créer des plannings sous la représentation de diagrammes et est adapté au... Cliquez pour télécharger Easy-Planning COM-BACKUP (2.0)COM-BACKUP (2.0)
COM-BACKUP est un logiciel de sauvegarde qui permet de planifier les sauvegardes de vos dossiers ...
Cliquez pour télécharger COM-BACKUP
|