Rechercher : dans
Par :

Synchronisation entre processus peres et fils

Dernière réponse le 16 fév 2009 à 18:25:54 moimeme, le 15 fév 2009 à 21:17:10 
 Signaler ce message aux modérateurs

Bonjour,
Je voudrais écrire un petit programme pour m'entrainer avec la communication entre 2 processus pere et fils en language C.
J'ai codé un programme pere (arnaud.c) qui demande à l'utilisateur de taper une chaine de caracteres.
Il trie cette chaine, et envoie les lettres à une de ses fils (bob.c) et les chiffre à l'autre (charly.c).
Chaque fils lit ce qui lui est envoyé, et le renvoie au père à la fin du processus.
La communication pere fils se fait avec un pipe
(je reconnais que mon programme ne sert à rien, mais c'est pour m'entrainer)

Mon programme compile, mais ne marche pas: il se bloque et attend (donc je pense un probleme de synchronisation pere-fils). De plus chaque fils n'a l'air de lire que le 1er caractère( a l'air car comme ca se bloque, je ne suis pas sur...)

Voilà le code de mes 3 programmes (je reconnais qu'ils sont un peu longs...). Si vous pouviez jeter un oeuil et me dire ce qui bloque dans mon programme (et surtout pourquoi ca bloque), ce serait vraiment sympa.


//-----------------------------------arnaud.c---------------­------------------------------
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<signal.h>

void erreur(char *mes);
char tab[10];

int main(void)
{
int pidb,pidc,p1;
int bip=0;
char car[10];

// creation du pipe
unlink("pipe");
mkfifo("pipe",0666);

// creation des 2 fils
pidb=fork();
if (pidb==0) execv("bob",NULL);
pidc=fork();
if (pidc==0) execv("charly",NULL);

//ouverture d'un pipe en ecriture
p1=open("pipe",O_WRONLY);
if (p1==-1) erreur("pb d'ouverture en ecriture");


printf("\ntaper une sequences de caracteres\n");
scanf("%s",&car);
// lecture des caracteres
int i=0;
while(car[i]!='\0') // pas de &car[i]
{

// tri+envoi d'un signal au fils approprié
usleep(500);
if (car[i]>='A' && car[i]<='z')
kill(pidb,SIGUSR1);

else if (car[i]>='1' && car[i]<='9')
kill(pidc,SIGUSR1);

else
{
printf("caractere non valide\n");
bip=1; // on n'ecrit rien dans le pipe
}

// écriture dans le pipe
if (bip==0) write(p1,&car[i],sizeof(car));

i++;

}

// on envoie le message de fin de l'ecriture qui est "!"
char fin;
fin='!';
write(p1,&fin,sizeof(car));

//on referme le pipe en écriture
usleep(500);
close(p1);

// on ouvre le pipe en lecture
p1=open("pipe",O_RDONLY);
if (p1==-1) erreur("pb d'ouverture en lecture");

//on lit
int j;
for(j=0;j=10;j++)
{
read(p1,&tab[j],sizeof(tab));
printf("%s",&tab[j]);
}

//on referme le pipe en lecture
usleep(500);
close(p1);

// on tue les processus
kill(pidb,SIGKILL);
kill(pidc,SIGKILL);

return(0);
}


void erreur(char *mes)
{
printf("%s\n",mes);
exit(-1);
}


//---------------------------------------------------------bob.c------------------------------------

#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<signal.h>

void erreur(char *mes);
void lect(int sig);
void envoyer();

// s'occupe des lettres

int p1;
int i=0;
char tab[10][10];

int main(void)
{
//ouverture d'un pipe en lecture
p1=open("pipe",O_RDONLY);
if (p1==-1) erreur("pb d'ouverture en lecture\n");

// on se prepare au signal
signal(SIGUSR1,lect);

// on verifie si le signal de fin a ete envoyé
char fin;
fin='!';
if (strcmp(&tab[10][i],&fin)==0)
envoyer();

// on attend le signal
for(;;)
{
pause();
}

exit(0);
}

void lect(int sig)
{
char lett;

// lecture du pipe
read(p1,&lett,sizeof(lett));

//on stocke la variable ds un tableau
tab[10][i]=lett;
i++;
// on affiche le resultat
printf("la lettre lue est %c\n", lett);

//on referme le pipe
close(p1);

}

void envoyer()
{
//on ouvre le pipe en éccriture cette fois ci
p1=open("pipe",O_WRONLY);
write(p1,tab,sizeof(tab));

close(p1);
}


void erreur(char *mes)
{
printf("%s\n",mes);
exit(-1);
}

//-----------------------------------------------------charly.c---------------------------------------------
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<signal.h>

void erreur(char *mes);
void lect(int sig);
void envoyer();

// s'occupe des chiffres

int p1;
int i=0;
char tab[10][10];

int main(void)
{
//ouverture d'un pipe en lecture
p1=open("pipe",O_RDONLY);
if (p1==-1) erreur("pb d'ouverture en lecture\n");

// on se prepare au signal
signal(SIGUSR1,lect);

// on verifie si le signal de fin a ete envoyé
char fin;
fin='!';
if (strcmp(&tab[10][i],&fin)==0)
envoyer();

// on attend le signal
for(;;)
{
pause();
}

exit(0);
}

void lect(int sig)
{
char chiff;

// lecture du pipe
read(p1,&chiff,sizeof(chiff));

//on stocke la variable ds un tableau
tab[10][i]=chiff;
i++;
// on affiche le resultat
//printf("la lettre lue est %c\n", lett);

//on referme le pipe
close(p1);

}

void envoyer()
{
//on ouvre le pipe en éccriture cette fois ci
p1=open("pipe",O_WRONLY);
write(p1,tab,sizeof(tab));

close(p1);
}


void erreur(char *mes)
{
printf("%s\n",mes);
exit(-1);
}


merci d'avance

Configuration: Linux Mandriva
Firefox 2.0.0.13

Meilleures réponses pour « synchronisation entre processus peres et fils » dans :
Que fait un fork() ? Voir...ou le petit fork() illustré.... Introduction Lancement du père Le fork Maîtriser le fil d'exécution du père et celui du fils Les variables et les descripteurs de fichiers La synchronisation La fin Notes et...
[Linux] Synchroniser l'horloge système avec un serveur de temps VoirLa commande ntpdate permet de synchroniser l'heure du système Linux avec un serveur de temps, par exemple : ntpdate serveur.de.temps.fr L'adresse suivante recense les serveurs de temps pour la France Si vous êtes dans un autre pays, il est...
Problème pour retirer un périphérique en toute sécurité VoirRetirer une clé USB en toute sécurité Parfois lorsque vous essayer de retirer un périphérique USB et lorsque vous utilisez le raccourci de la zone de notification, vous recevez un message vous disant que le périphérique ne peut pas être retiré car...
Management par la qualité VoirPrincipes de management par la qualité Les principes de management par la qualité introduits dans la norme ISO 9004:2000 définissent un cadre de référence (en anglais framework) permettant aux organisation d'améliorer leurs performances. Ces...
Qualité - Management par les processus VoirNotion de processus Le modèle de processus consiste à concevoir l'objectif de l'entreprise comme étant la fourniture de produits et/ou services conformes aux attentes des clients. Ainsi, l'entreprise est modélisée comme un ensemble de processus...
Oracle - Les processus VoirLes processus Le fonctionnement de la base Oracle est régi par un certain nombre de processus chargés en mémoire permettant d'assurer la gestion de la base de données. On distingue généralement deux types de processus : les processus utilisateurs...

1

kilian, le 16 fév 2009 à 00:37:37

Salut,

Fondamentalement, le gros problème c'est ta boucle infinie avec pause().
En fait les main() de Bob et Charlie ont un soucis:

int main(void)
{
    //ouverture d'un pipe en lecture
    p1=open("pipe",O_RDONLY);
    if (p1==-1) erreur("pb d'ouverture en lecture\n");

    // on se prepare au signal
    signal(SIGUSR1,lect);

    // on verifie si le signal de fin a ete envoyé
    char fin;
    fin='!';
    if (strcmp(&tab[10][i],&fin)==0)
          envoyer();

    // on attend le signal
    for(;;)
    {
         pause();
    }
}

Ca donnes: tu ouvres le fichier, installe ton handler de signal puis tu compares avec 0 une valeur d'un tableau qui n'a jamais été initialisé.
Enfin tu tournes dans une boucle infinie.
Dans cette boucle, tu dors une première fois. Tu reçois SIGUSR1, donc tu te réveilles et tu vas dans lect(), ici ton handler n'est plus attaché à ce signal (utilise sigaction pour installer un handler persistant), ensuite tu retournes en boucle dans un pause() continuel qui fera que tu dormiras, tu va te réveiller, tu dormiras.... etc.. d'ou l'interblocage.

Ya aussi des petits trucs dangeureux:
// on envoie le message de fin de l'ecriture qui est "!"
char fin;
fin='!';
write(p1,&fin,sizeof(car)); 

Là tu débordes largement dans la mémoire :-)
Ce message ne mange ni trop gras, ni trop salé, ni trop
sucré .Pour sa santé il bouge plus et mange 5 fruits et  légumes par jour.

Répondre à kilian

2

loupius, le 16 fév 2009 à 02:08:51

Simplement quelques remarques à ajouter:
1) Pourquoi 2 programmes 'bob' et 'charly'? Ils font la même chose (du moins je n'y vois pas de différence; il faut dire qu'avec leur programme non indenté, j'ai bien du mal à lire... quand ça ne me décourage pas!). De toute façon on peut, lors du lancement, envoyer un paramètre différent à l'un et à l'autre.
2) On traite les erreurs du style 'if (p1==-1) erreur("pb d'ouverture en ...ture\n");'... mais pas complètement puisqu'on va continuer à lire ou à écrire dans ce fichier .
3) Dans le traitement de la fonction 'lect', tu fermes le pipe à la fin, donc quand tu reviendras dans cette fonction pour un autre 'read'... problème assuré! Et puis il y a un autre problème caché qui va survenir dès que la fonction 'lect' aura été exécutée dans les 2 programmes (bot et charly)... suspense... quel est le problème caché? Et bien le pipe n'aura plus d'extrémité d'ouverte en lecture et, par conséquent, l'extrémité en écriture sera bloquée (à moins de l'ouvrir en mode O_NONBLOCK, ce qui n'est pas le cas) et 'arnaud' ne peut plus écrire.
4) Mais on en sort comment des programmes 'fils'; il faut lâchement les tuer! Pas très correct. Bon d'accord il s'agit d'un exercice.
5) Si la fonction 'envoyer' n'a pas été appelée dans les conditions douteuses indiquées par kilian, elle ne le sera plus jamais; en effet, une fois dans la boucle infinie, on ne reviendra pas en arrière.
Bon j'arrête et bon courage pour tout ce qu'il reste à faire.

Répondre à loupius

3

moimeme, le 16 fév 2009 à 16:19:13

Merci de vos conseils !

Effectivement la difference entre Bob et Charlie est minime... masi c'était juste pour essayer de travailler avec 2 processus "freres"

Je ne comprneds pas bien vos remarque sur la boucle infinie : est ce que la fonction signal(SIGUSR1,lect) n'est "valable" qu'une seule fois et qu'ensuite, quand je recois un deuxième signal je ne vais pas a la fonction lect() ? (mais alors si je ne suis plus préparé au signal ,, le fils devrait mourir ??) enfin bref, je n'ai pas tres vbien compris quel ets le probleme de la boucle infinie : est ce que je recois les signaux ou non ?

merci encore (et désolé pour l'indentation.... le copier/coller me l'a supprimé)

Répondre à moimeme

4

loupius, le 16 fév 2009 à 18:10:46

Non, ce n'est pas le copier-coller qui a supprimé les indentations, mais c'est que tu n'as pas utilisé les balises <codes> (il y a les balises Gras Italique Souligne Code Lien).
Une fois dans ta boucle infinie, à chaque signal reçu tu iras exécuter le sous-programme prévu par le signal (soit lect) mais ensuite tu reviendras dans ta boucle infinie.
Comme l'a dit kilian, le comportement classique de signal est qu'une fois exécuté, le handler original est remis, ce qui n'est pas vrai sous Linux; le comportement est donc différent suivant le système d'exploitation et c'est pour cela qu'il a indiqué qu'il valait mieux utiliser 'sigaction' qui garantit, dans la norme, que le handler restera installé.
Quant aux deux processus frères, on peut très bien lancer deux fois le même programme!

Répondre à loupius

5

 loupius, le 16 fév 2009 à 18:25:54

Heu, j'ai oublié de répondre à la question:
mais alors si je ne suis plus préparé au signal ,, le fils devrait mourir ??
Non il n'y a aucune raison; quand tu n'es plus préparé au signal comme tu dis, cela signifie que le signal qui te serait envoyé n'arriverait pas , c'est tout, et qu'en conséquence, ton programme continue normalement... dans ta boucle!
Ceci dit même après réception d'un signal, tu resteras préparé parce que tu es sous Linux mais ce n'est pas certain sous un autre SE (donc vaut mieux utiliser 'sigaction').

Répondre à loupius