A voir également:
- Probleme de segment mémoire en C
- Mémoire vive - Guide
- RAM : type, format, CAS, vitesse, tout sur la mémoire vive - Guide
- Nettoyer memoire iphone - Guide
- Mémoire virtuelle pour 32 go de ram - Guide
- Test memoire pc - Guide
3 réponses
Bon, c'est encore moi!
J'ai quelque peu réécris le programme
Lors d'un récent post, je t'avais dit qu'après appel à 'fork(s)', il est indispensable d'attendre que les fils soient finis et de lire leur code de retour avant de terminer le programme... n'aurais-je pas été assez clair ?
D'autre part, il est indispensable, si l'on veut construire des programmes corrects, de gérer systématiquement les retours de certaines fonctions:
- supposons que l'appel 'fork' se passe mal et qu'il n'y a pas de fils créé, que se passe-t-il ?
- supposons que execv("C9", NULL); ne se passe pas bien, que se passe-t-il ? Le programme fils se poursuit par if (!(pidB = fork())) et imagine la pagaille.
Bonne réflexion.
J'ai quelque peu réécris le programme
Programme 1: ----------- typedef struct { int nb; char bin[16]; char hex[16]; }SEGMENT; SEGMENT* pSeg; int shmid; void erreur(char *mes) { printf("%s\n", mes); exit(-1); } void lire(int sig) { printf(">>> decimal =%d\n", pSeg->nb); printf(">>> hexa =%s\n", pSeg->hex); printf(">>> binaire =%s\n", pSeg->bin); } int main(void) { int pidB, pidC; //creation du fils C if (!(pidC = fork())) { usleep(1000); // Pour permettre la création du segment partagé execv("C9", NULL); } //creation du fils B if (!(pidB = fork())) { usleep(1000); // Pour permettre la création du segment partagé char chpidC[10]; sprintf(chpidC, "%d", pidC); char* argv[3] = { "B9", chpidC, NULL }; execv("B9",argv); } int cle = ftok("fic", '1'); shmid = shmget(cle, sizeof(SEGMENT), IPC_CREAT|0666); if (shmid == -1) erreur("P1: pb creation de segment\n"); pSeg = (SEGMENT*)shmat(shmid, NULL, 0); signal(SIGUSR2, lire); do { printf("P1: taper le nombre en binaire\n"); scanf("%s", pSeg->bin); // On envoie un signal a B pour lui dire de lire kill(pidB, SIGUSR1); usleep(1000); // Attente pour synchro } while(pSeg->nb); shmdt(pSeg); shmctl(shmid, IPC_RMID, NULL); while (wait(NULL) > 0); return(0); } Programme 2: ----------- typedef struct { int nb; char bin[16]; char hex[16]; }SEGMENT; SEGMENT* pSeg; int pidC, shmid; void erreur(char *mes) { printf("%s\n", mes); exit(-1); } void lireEcrire(int sig) { pSeg->nb = 0; int i = 0; while (pSeg->bin[i]) { pSeg->nb <<= 1; pSeg->nb += pSeg->bin[i++] - '0'; } printf("P2: le nombre decimal dans B est %d\n", pSeg->nb); // On envoie un signal a C pour lui dire de lire kill(pidC, SIGUSR1); if(!pSeg->nb) { // Nbre saisi nul -> on termine shmdt(pSeg); shmctl(shmid, IPC_RMID, NULL); exit(0); } } int main(int argc, char *argv[]) { pidC = atoi(argv[1]); int cle = ftok("fic", '1'); shmid = shmget(cle, sizeof(SEGMENT), SHM_RDONLY); if (shmid == -1) erreur("P2: pb ouv de segment\n"); pSeg = (SEGMENT*)shmat(shmid, NULL, 0); signal(SIGUSR1, lireEcrire); for (;;) pause(); return(0); } Programme 3: ----------- typedef struct { int nb; char bin[16]; char hex[16]; }SEGMENT; SEGMENT* pSeg; int pidC, shmid; void erreur(char *mes) { printf("%s\n", mes); exit(-1); } void lireEcrire(int sig) { if (pSeg->nb) { sprintf(pSeg->hex, "%x", pSeg->nb); printf("P3: le nombre hexa dans C est %s\n", pSeg->hex); // On envoie un signal a A pour lui dire de lire kill(getppid(), SIGUSR2); } else { // Nbre saisi nul -> on termine shmdt(pSeg); shmctl(shmid, IPC_RMID,NULL); exit(0); } } int main(void) { int cle = ftok("fic", '1'); shmid = shmget(cle, sizeof(SEGMENT), SHM_RDONLY); if (shmid == -1) erreur("P3: pb ouv de segment\n"); pSeg = (SEGMENT*)shmat(shmid, NULL, 0); signal(SIGUSR1, lireEcrire); for (;;) pause(); return(0); }Tu as sans doute remarqué que j'ai introduit quelques 'sleep' pour des raisons de synchronisation. En effet, l'accès à des ressources partagées se font normalement sous le contrôle de sémaphores; ce sera peut-être le prochain exercice que tu nous proposeras de corriger ;-)))
Lors d'un récent post, je t'avais dit qu'après appel à 'fork(s)', il est indispensable d'attendre que les fils soient finis et de lire leur code de retour avant de terminer le programme... n'aurais-je pas été assez clair ?
D'autre part, il est indispensable, si l'on veut construire des programmes corrects, de gérer systématiquement les retours de certaines fonctions:
- supposons que l'appel 'fork' se passe mal et qu'il n'y a pas de fils créé, que se passe-t-il ?
- supposons que execv("C9", NULL); ne se passe pas bien, que se passe-t-il ? Le programme fils se poursuit par if (!(pidB = fork())) et imagine la pagaille.
Bonne réflexion.
merci loupius !
"Lors d'un récent post, je t'avais dit qu'après appel à 'fork(s)', il est indispensable d'attendre que les fils soient finis et de lire leur code de retour avant de terminer le programme..."
-> c'est vrai... c'est grace aux sleep, en attendant que les fils soient finis, que vous faites ca ?
Le code que j'avais initialement marchait mais 1 seule fois ??? Comment peut on l'expliquer ? Est ce du au fait que je n'attendais pas la mort du fils ? (j'etais oblige de faire ctrl+C pour l'arreter...mais apres un ps ne me disait pas que les fils restaient ouverts...)
Enfin, qu'est ce que le "<<=" dans le prog 2 ?
Merci encore
"Lors d'un récent post, je t'avais dit qu'après appel à 'fork(s)', il est indispensable d'attendre que les fils soient finis et de lire leur code de retour avant de terminer le programme..."
-> c'est vrai... c'est grace aux sleep, en attendant que les fils soient finis, que vous faites ca ?
Le code que j'avais initialement marchait mais 1 seule fois ??? Comment peut on l'expliquer ? Est ce du au fait que je n'attendais pas la mort du fils ? (j'etais oblige de faire ctrl+C pour l'arreter...mais apres un ps ne me disait pas que les fils restaient ouverts...)
Enfin, qu'est ce que le "<<=" dans le prog 2 ?
Merci encore
Bon, y'a encore des explications à donner ;-)
### x <<= y : '<<' est l'opérateur de décalage à gauche, donc <<= décale l'opérande 'x' de 'y' fois; en l'occurence, ici, on décale 1 fois ce qui revient à multiplier l'entier par 2.
### Non, on attend que les fils soient finis par while (wait(NULL) > 0);
### mais apres un ps ne me disait pas que les fils restaient ouverts, c'est là qu'intervient l'intelligence des concepteurs. Normalement, lorsque le fils se termine, il devient 'zombie en attendant que son père lise son code de retour (tant que le père n'est pas fini, un 'ps' t'indiquera que le fils existe toujours) et si le père se termine sans lire le code de retour, le fils qui était attaché à son père ne peut plus l'être et comme tout processus à un père, le fils est alors attaché au processus 'init' (d'ailleurs le seul qui n'a pas de père, eh oui il en faut bien un !) qui, à la place du père, lit le code de retour ce qui fait disparaître définitivement le fils.
### A propos de la synchronisation: il faut bien comprendre que les différents processus ne 'tournent' pas en même temps (oui, je sais qu'il y a des multi-coeurs mais de toutes façons il y a des limites, si vous avez huit coeurs et vingt process, tout le monde ne peut tourner en même temps), donc pour des opérations particulières il est important de s'assurer que les opérations seront exécutées dans un certain ordre. Par exemple, étant donné que l'on a choisi que le père crée le segment partagé, il est important que les fils ne l'ouvrent qu'après; or, lorsque le 'fork' se produit, un processus est créé; alors deux processus existent et on est incapable de prévoir quel processus se poursuivra avant l'autre. Il faut donc attendre un 'certain temps' avant d'ouvrir la mémoire partagée. Evidemment, le mieux, pour gérer la synchronisation, est le système de sémaphores. Et si ton code ne marchait pas bien, c'était dû justement à un problème de synchro.
Bonne réflexion.
### x <<= y : '<<' est l'opérateur de décalage à gauche, donc <<= décale l'opérande 'x' de 'y' fois; en l'occurence, ici, on décale 1 fois ce qui revient à multiplier l'entier par 2.
### Non, on attend que les fils soient finis par while (wait(NULL) > 0);
### mais apres un ps ne me disait pas que les fils restaient ouverts, c'est là qu'intervient l'intelligence des concepteurs. Normalement, lorsque le fils se termine, il devient 'zombie en attendant que son père lise son code de retour (tant que le père n'est pas fini, un 'ps' t'indiquera que le fils existe toujours) et si le père se termine sans lire le code de retour, le fils qui était attaché à son père ne peut plus l'être et comme tout processus à un père, le fils est alors attaché au processus 'init' (d'ailleurs le seul qui n'a pas de père, eh oui il en faut bien un !) qui, à la place du père, lit le code de retour ce qui fait disparaître définitivement le fils.
### A propos de la synchronisation: il faut bien comprendre que les différents processus ne 'tournent' pas en même temps (oui, je sais qu'il y a des multi-coeurs mais de toutes façons il y a des limites, si vous avez huit coeurs et vingt process, tout le monde ne peut tourner en même temps), donc pour des opérations particulières il est important de s'assurer que les opérations seront exécutées dans un certain ordre. Par exemple, étant donné que l'on a choisi que le père crée le segment partagé, il est important que les fils ne l'ouvrent qu'après; or, lorsque le 'fork' se produit, un processus est créé; alors deux processus existent et on est incapable de prévoir quel processus se poursuivra avant l'autre. Il faut donc attendre un 'certain temps' avant d'ouvrir la mémoire partagée. Evidemment, le mieux, pour gérer la synchronisation, est le système de sémaphores. Et si ton code ne marchait pas bien, c'était dû justement à un problème de synchro.
Bonne réflexion.