Probleme de synchronisation ecriture tubes / multi threads fork

Probleme de synchronisation ecriture tubes / multi threads fork - C++ - Programmation

Marsh Posté le 06-06-2006 à 03:36:32    

Salutations,
 
J'ai un peu de mal à capter comment les tubes fonctionnent à cause des résultats aléatoires que je récupère... Je vous explique.
 
J'ai pour mission de récupérer la liste des fichiers d'un sous arbre à partir d'un point donné du disque.
 
Exemple : ./monprog repdepart
 
Lors du parcours de repdepart, le programme va, s'il tombe sur un repertoire à l'interieur de repdepart, lancer un processus fils avec l'appel fork();.
 
Les processus communiquent entre eux la liste des fichiers trouvés via UN tube.
 
Donc, le tube est crée par le programme principal. Premier fork() : si je suis le fils, je ferme le tube en lecture et je lance l'algoryhtme sur (repdepart,tube).
Si je suis le pere, je ferme le tube en ecriture et j'attends par un while (read ...) des informations de mon fiston.
 
Maintenant, si le fils, lorsqu'il parcoure la liste des fichiers, trouve un repertoire, il crée un fils avec (repdepart/reptrouvé,tube);
 
Ainsi de suite.
 
 
Le probleme est le suivant :
Considérons le cas où tout les fils ont écris dans le tube, puis se terminent un à un, sans que le processus pere principal n'ait eu le temps d'executer un read(pipe). (habituellement il a le temps d'en faire juste un dans mon programme, des fois deux).
Le pere lorsqu'il va prendre la main, va executer un read(tube...) qui va lui retourner 0 ! Et ce malgré le fait qu'il y ait des choses à lire dans le tube !
Et pourquoi? Simplement parceque le tube a été fermé en écriture par tout les fils, le read considère qu'il n'ya plus rien à lire et retourne 0.
 
Donc il me faudrait un moyen de faire en sorte à forcer le read à lire TOUT le contenu du tube, qu'il y ai des processus ecrivains ou non.
Ou alors : Forcer la fermeture du tube en ecriture du premier fils (dans l'ordre des choses, les fils creent des fils etc mais tout se termine dans le sens inverse, les parents attendant que les enfants finissent) uniquement quand le processus pere principal aura fini de lire tout le contenu du tube!
 
Des idées? Je suis bloqué depuis 2 jours la dessus ><

Reply

Marsh Posté le 06-06-2006 à 03:36:32   

Reply

Marsh Posté le 06-06-2006 à 09:07:07    

> l'algoryhtme
- l'algorihtme
 
> Le probleme est le suivant :
- je dirai que le problème est que tu va vite exploser le nombre de processus de ton système... Enfin, je suppose que c'est un exercice.
 
Tu as oublié de préciser ce qu'il se passe quand un fils reçoit une info d'un autre fils. Y a t-il plusieurs tubes de créés, ou est-ce-que le même tube est utilisé par tous les fils? Tu va aussi exploser le nombre de fichiers ouverts par le système.
 
En fait, la condition d'arrêt de lecture du père, devrait être à la terminaison de tous les fils. De même, chaque fils attends que tous ses fils s'arrêtent avant de s'arrêter. Voir les fonctions wait() et waitpid().

Reply

Marsh Posté le 06-06-2006 à 11:43:03    

Alors pour le nombre de processus, oui j'ai ai bien pensé mais bon comme c'est un exercice on nous en demande pas trop.
A la limite on donne une variable nombre max fils par fils. Disons 5 fils max par fils, après il dois wait l'un de ses fils.
 
Alors pour le tube, il n'y a qu'un seul tube.
Le processus main :
tube=pipe()
pid=fork
if (pid==0)
{ close(tube[0]). Appel de la fonction qui va s'occuper de parcourir le rep donné en parametre, et lancer des fils sur la meme fonction à chaque sous rep trouvé.
return 1;
} else {
close(tube[1]);
while (read(tube[0],buffer,sizeof(buffer))
{
on traite les resultats
}
}
 
 

Citation :


En fait, la condition d'arrêt de lecture du père, devrait être à la terminaison de tous les fils.


 
Et c'est déjà le cas ! Le read retournera 0 lorsequ'il n'y aura plus personne pour ecrire dans le tube.
Et c'est justement le probleme.
 
Si les processus fils ecrivent, que le main lis, puis les fils se terminent, tout est OK le main a tout recu.
Si le processus main a la main moins souvent que les fils, les fils ecrivent mais le main n'a pas encore eu le temps de tout lire lorsque ses fils se terminent : le read renvoie 0 car il n'y a plus d'ecrivain (ce qui est vrai) mais il y a toujorus des choses à lire dans le tube!

Reply

Marsh Posté le 06-06-2006 à 12:16:30    

C'est pas vraiment la question posée, mais j'ai quand même un doute sur ce type de fonctionnement : s'il n'y a qu'un seul tuyau et plusieurs fils écrivains (répartis sur plusieurs générations) qu'est-ce qui te garantit que les différents messages écrits par les fils ne se mélangent pas ?
 
Ne devrais tu pas 1) faire plusieurs tuyaux, ou 2) coordonner tes écritures (par exemple avec des signaux) ?
 
 
Pour ton problème de lecture dans le tuyau, je ne comprends pas d'où ça peut venir : normalement, si le fils fait une fermeture propre du tuyau avant de se terminer, les données ne devraient pas disparaître dans le vide intersidéral. Es-tu sûr que tous les fils se terminent proprement et ferment correctement leur tuyau ?


Message édité par franceso le 06-06-2006 à 12:16:50

---------------
TriScale innov
Reply

Marsh Posté le 06-06-2006 à 12:17:12    

Citation :


En fait, la condition d'arrêt de lecture du père, devrait être à la terminaison de tous les fils.


Je re-confirme, tu ne m'a pas compris. Je parle bien de terminaison de fils, et non de fermeture de tube. D'ailleurs je ne suis même pas sûr qu'il soit réellement important de fermer le tube.
 
Pour vérifier si un processus fils se termine, utilise l'une des fonctions suivantes:

  • wait()
  • waitpid()

Tu trouvera le manuel de ces fonctions avec la commande Unix:
man 2 waitpid
...ou sur le net et tapant "man waitpid" avec les guillemets dans un moteur de recherche.

Reply

Marsh Posté le 06-06-2006 à 12:23:32    

Pour répondre aux inquiétudes de francesco, oui, il y a un risque que les sorties des fils se mélangent.
 
Mais, le risque est minime si on fait un printf() car d'une part le chemin absolu d'un fichier est limité à 1024 octets, et d'autre part le buffer de printf() est bien souvent plus grand (4Ko).
 
Pour être cependant sûr que le code fonctionne sur des systèmes exotiques, il faut bien entendu utiliser une approche différente. Étant donné que créer plein de tubes peu poser problème, au niveau des ressources requises, et vaut mieux utiliser un <<cadenas>> sur le tube.
 
Pour celà, voir la fonction <<lockf>>, dans la section 3 du manuel Unix: man 3 lockf

Reply

Marsh Posté le 06-06-2006 à 12:58:35    

Je fait bien des wait(NULL)
 
Je vous inscris mon code, ce sera plus simple :
 

Code :
  1. int parcours(char * repertoire, int * tube)
  2. {
  3. cout<<"("<<getpid()<<" )" << "Fonction parcours sur : " << repertoire << endl;
  4. char cheminfich [1024];
  5. char liste[2048];
  6. close(tube[0]);
  7. struct dirent *lecture;
  8. DIR *rep;
  9. pid_t pid;
  10. rep = opendir(repertoire);
  11. char  temp[20];
  12. struct stat st;
  13. int childs=0;
  14.    if (rep) {
  15. while (lecture = readdir(rep))
  16. {
  17.  if ( (strcmp(lecture->d_name,".." )!=0 ) && (strcmp(lecture->d_name,"." )!=0) )
  18.  {
  19.   strcpy(cheminfich,repertoire);
  20.   strcat(cheminfich,"/" );
  21.   strcat(cheminfich,lecture->d_name);
  22.   lstat(cheminfich ,&st);
  23.   if ( ! S_ISDIR(st.st_mode) && ( st.st_mode & S_IFLNK ) != S_IFLNK )
  24.   {
  25.    strcat(liste,cheminfich);
  26.    sprintf(temp, "|%d", st.st_size);
  27.       strcat(liste,temp);
  28.    sprintf(temp, "|%d", st.st_ino);
  29.       strcat(liste,temp);
  30.    strcat(liste,"|" );
  31.   } else if (S_ISDIR(st.st_mode) && ( st.st_mode & S_IFLNK ) != S_IFLNK )
  32.   {
  33.    childs++;
  34.    pid=fork();
  35.    if (pid==0) {
  36.     return parcours(cheminfich,tube);
  37.    }
  38.   }
  39.  }
  40. }
  41. if (strlen(liste)>5) {
  42.  cout <<"("<<getpid()<<" )"<< "Ecriture dans le tube : " << liste << endl;
  43.  write(tube[1],liste,strlen(liste)+1);
  44.  }
  45.  closedir(rep);
  46.   }
  47. for (int i=0;i<childs;i++) { pid=wait(NULL); cout<<"("<<getpid()<<" ) A attendu une fois le fils : "<<pid<<endl; }
  48. close(tube[1]);
  49. cout<<"("<<getpid()<<" ) Je suis mort"<<endl;
  50. return 1;
  51. }
  52. int main (int argc, char * argv[])
  53. {
  54. Liste L;
  55. int tube[2];
  56. char chemin[512];
  57. char buffer[2048];
  58. int taille;
  59. int inode;
  60. pid_t pid;
  61. char * strsplit;
  62. if (argc!=2)
  63. {
  64.  cout<<"("<<getpid()<<" )"<<"Spécifiez un et un seul repertoire à analyser."<<endl;
  65.  exit(-1);
  66. }
  67. if (pipe(tube)<0)
  68. {
  69.  cout<<"("<<getpid()<<" )"<<"Impossible d'ouvrir un tube de communication"<<endl;
  70.  exit(-1);
  71. }
  72. pid=fork();
  73. if (pid==0)
  74. { parcours(argv[1],tube); return 1;}
  75. else {
  76.   close(tube[1]);
  77.  while (read(tube[0],buffer, sizeof(buffer)))
  78.   {
  79.    cout<<"("<<getpid()<<" )" << "Lu dans le tube : " << buffer << endl;
  80.    strsplit=strtok(buffer,"|" );
  81.    while (strsplit != NULL)
  82.    {
  83.     strcpy(chemin,strsplit);
  84.     strsplit=strtok(NULL,"|" );
  85.     taille=atoi(strsplit);
  86.     strsplit=strtok(NULL,"|" );
  87.     inode=atoi(strsplit);
  88.     L.ajoutTrieCroissant(chemin, taille, inode);
  89.     cout<<"("<<getpid()<<" )" << "Ajouté dans la liste : " << chemin << ":" << taille << ":" << inode << endl;
  90.     strsplit=strtok(NULL,"|" );
  91.    }
  92.   }
  93.  close(tube[0]);
  94.  cout <<"("<<getpid()<<" )"<< "Affichage de la liste des fichiers : " << endl;
  95.  L.affiche(cout);
  96. }
  97. wait(NULL);
  98. //suite qui n'a rien à voir...

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed