Forum général.général Communication entre deux processus pere-fils bloquante

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
13
avr.
2015

Bonjour à tous,

J'ai pas mal cherché comment initier une communication entre deux processus et voici la configuration.

J'ai deux fichiers dont un se nomme acq.c et un autre interface.cpp.

Plusieurs méthodes peuvent être testé pour une communication entre deux processus :
1-Création de fichier texte -> marche moyennement
2-Tubes -> recouvrement du processus père par un autre -> Comment trouver le tube créé par le père?
3-Tubes nommés -> bien mais je n'ai aucun moyen de synchroniser le processus fils & père pour la lecture par le fils et lecture par le père.

Ainsi, je propose les tubes nommés.

interface.cpp :

pid_t pid_fils;
pid_fils = fork();

            if(pid_fils == -1)
            {
                fprintf(stderr, "Erreur de création du processus.\n");

            }

            if(pid_fils == 0)
            {
                execl("/home/jo-pc/Bureau/Acquisition","Acquisition",NULL);
            }

            else
            {
                wait(NULL); //attente de la fin du processus fils bloquante puisque le père ne peut pas lire

                //lecture du tube :
                double tmp2=0;

                int fd2;
                FILE *fp2;

                char *nomfich2="/home/jo-pc/Bureau/test2.txt", chaine2[50];

                fd2 = open(nomfich2, O_RDONLY); /* ouverture du tube */
                fp2=fdopen(fd2, "r"); /* ouverture du flot */
                fscanf(fp2, "%s", chaine2); /* lecture dans le flot */
                tmp2 = strtod (chaine2,NULL);
                cout << chaine2 << endl;
                //printf("%lf\n",tmp2); /* affichage */

                unlink(nomfich2); /* fermeture du flot */
            }

acq.c :

 //Ecriture du tube nommé :

            int fd;
            FILE *fp;

            char *nomfich2="/home/jo-pc/Bureau/test2.txt"; // nom du fichier 

            if(mkfifo(nomfich2, 0644) != 0) // création du fichier
            {
                    perror("Problème de création du noeud de tube");
                    exit(1);
            }

            fd = open(nomfich2, O_WRONLY); // ouverture en écriture
            fp=fdopen(fd, "w"); // ouverture du flot

             fprintf(fp,"%d\n",numberOfPixels); // écriture dans le flot

            unlink(nomfich2); // fermeture du tube

Je lance un fork depuis interface.cpp.
Pour le processus père je mets un wait(NULL) afin d'attendre la fin du processus fils. Le problème c'est que ceci est bloquant pour le processus père et fils. Le fils attend qu'un processus viennent lire le tube nommé… Comment faire?

Merci d'avance

  • # revoir l'enoncé et choisir ce qui va bien

    Posté par  . Évalué à 2.

    mais je n'ai aucun moyen de synchroniser le processus fils & père pour la lecture par le fils et lecture par le père.

    alors pourquoi ne pas faire qu'un process ecrive dans un fichier
    et que l'autre process lise dans ce fichier

    tu as alors 2 process independants, qui peuvent attendre silencieusement,
    et quand acq.c remplit le fichier, ca declenche une action du lecteur

    • [^] # Re: revoir l'enoncé et choisir ce qui va bien

      Posté par  . Évalué à 1. Dernière modification le 13 avril 2015 à 21:21.

      Interface.cpp doit envoyer des paramètres d'acquisition à acq.c afin de faire une acquisition sur un matériel.
      Acq doit retourner les valeurs du matériel.

      J'ai essayé de faire le lien avec de simple fichiers texte sans lancer les processus "en même temps". Ceci fonctionne mais ça n'est pas ce qui est voulu.
      Une interaction temps réel serait nécessaire.

      La commande execl permet de remplacer le processus fils par acq.

      Pour les fichiers textes, j'ai essayé mais le fichier acq.c ne créé par le fichier texte et le processus père continu. Apparement pour le fichier texte, la solution ne fonctionne pas.
      1-fichiers textes
      2-tubes nommés
      3-tubes classiques

      Les tubes nommés sont bloquants. Je m'en sors pas :/

      Si lorsqu'on remplace le processus fils par celui d'acq avec execl, le contenu déclaré depuis le début dans affichage.cpp est toujours valable, le processus fils saurait trouver le tube créé dans le père. Ca je ne sais pas.

      • [^] # Re: revoir l'enoncé et choisir ce qui va bien

        Posté par  . Évalué à 2.

        Interface.cpp doit envoyer des paramètres d'acquisition à acq.c

        et acq.c tout seul depuis la ligne de commande prend des parametres ?

        parce qu'à ce compte là, faut juste lancer acq.c avec les parametres dans interface.c et recuperer ce qui en sort,
        sans avoir besoin de creer des pipes, des sockets ou des fichiers;

        dans ton cas, si en ligne de commande tu peux faire :
        acq parametre1 parametre2
        ou
        acq --option1 parametre1 --option2 parametre2

        alors dans interface.c il doit suffire de faire un
        retour = execl("/chemin/vers/acq", "parametre1", "parametre2");

        • [^] # Re: revoir l'enoncé et choisir ce qui va bien

          Posté par  . Évalué à 1.

          Acq devrait prendre des parametres oui.

          Pour le moment je n'en ai pas mis pour simplifier le tout.

          Pour execl c'est ce que je fais avec un fork lorsque je suis dans le processus fils.

          Le souci c'est que je dois pouvoir appuyer sur un bouton via une interface graphique interface.cpp codé en C/C++ pour faire des acquisitions en continue.

          1-fichiers textes -> ne fonctionne pas car le fichier texte n'est pas créé par le fils
          2-tube nommé -> bloque le fils et le pere en etat de processus sommeil S
          3-tube classique -> si execl remplace le processus, je perds le descripteur du tube peut être

          Des threads peut être?

          • [^] # Re: revoir l'enoncé et choisir ce qui va bien

            Posté par  . Évalué à 2.

            1-fichiers textes -> ne fonctionne pas car le fichier texte n'est pas créé par le fils

            ben faur le creer avec le pere, le remplir avec le fils
            puisqu'il faut le remplir avec les parametres que le fils va lire.
            le fils lit ces parametres, fait l'acquisition, ecrit le resultat dans un 2e fichier.
            et le pere viendra lire le fichier quand le fils aura terminé donc apres le execl()

            • [^] # Re: revoir l'enoncé et choisir ce qui va bien

              Posté par  . Évalué à 1.

              C'est tout à fait le genre de méthode que je voulais utiliser. Je viens de tester avec un execl vers mon acq.c pour écrire le fichier texte. Celui-ci ne s'écrit pas.

              J'aurais tendance à croire que acq.c se termine sans même écrire le fichier texte alors que derrière interface.cpp attend la fin d'un processus fils!

              C'est assez étrange. On m'a dit que pour l'écriture d'un fichier texte, l'écriture se fait en priorité assez basse. Du coup d'autres choses dans l'OS se font avant.

              Une histoire de permission du processus fils pour écrire un fichier texte?

              Solution :
              -chercher une option pour forcer l'écriture sans attendre
              -permission plus élevé pour le programme

              • [^] # Re: revoir l'enoncé et choisir ce qui va bien

                Posté par  . Évalué à 1.

                Une solution à mon problème existe. La fonction popen, elle permet de créer un pipe entre deux fichiers et remplace les fonctions fork, execl, dup2, …

                pere.cpp :

                    int main()
                {
                        char buf [10];
                
                        FILE *pp;
                
                        pp = popen("fils", "r");
                
                        if (pp == NULL)
                        {
                                perror("popen");
                                exit(1);
                        }
                        while (fgets(buf, sizeof(buf), pp))
                                fputs(buf, stdout);
                        pclose(pp);
                
                return 0;
                }    
                

                fils.c :

                    int main()
                {
                        fprintf(stdout,"coucou\n");
                        fflush (stdout);
                    return 0;
                }    
                

                On obtient bien coucou de la part du fils sur la sortie standard (console)!
                L’inconvénient c'est que je ne maitrise pas ce que je fais avec cette fonction.

                Je ne sais pas si passer par la sortie standard c'est très approprié … ce n'est peut être pas très propre à la longue!
                Je vais chercher du côté des redirection de tube ou de la mémoire partagée pour deux processus afin de garder la déclaration du descripteur pour le fils une fois execl dans le père lancé.

                • [^] # Re: revoir l'enoncé et choisir ce qui va bien

                  Posté par  . Évalué à 2.

                  tu y es presque,

                  mais je penses que tu as l'impression que cela fonctionne car tu vois coucou sur stdout
                  mais c'est directement le fils qui ecrit sur stdout, et pas le pere.

                  pour etre sur que ce soit le pere, il faudrait ajouter une chaine dans le fputs(buf,stdout)

                  pour moi il faudrait maintenant que le fils ecrive dans le "pp" puisque c'est là que le pere va lire avec fgets(buf, sizeof(buf), pp)

                  • [^] # Re: revoir l'enoncé et choisir ce qui va bien

                    Posté par  . Évalué à 1.

                    J'ai remplacé mon code pere.cpp :

                            char buf [10];
                    
                            FILE *pp;
                    
                            pp = popen("/home/jo-pc/Bureau/STAGE/Projet_spectro/pipe/test_3/fils", "r");
                    
                            if (pp == NULL)
                            {
                                    perror("popen");
                                    exit(1);
                            }
                            printf("etape1");
                            while (fgets(buf, sizeof(buf), pp))
                                    fputs(buf, stdout);
                            pclose(pp);
                            printf("etape2");
                            printf("%s",buf);
                    

                    Ainsi, j'ai :
                    etape1coucou
                    etape2coucou

                    Je pense donc que c'est correcte. popen créé un pipe entre les deux fichiers et lit sur l'entrée standard du process fils.

                    Autre méthode qui marche pour le pere.cpp via ce lien en utilisant les E/S standard : Texte du lien

                    #include <stdio.h>
                    #include <sys/types.h>
                    #include <sys/wait.h>
                    #include <unistd.h>
                    
                    #include <stdio.h>
                    #include <stdlib.h>
                    #include <sys/types.h>
                    #include <sys/wait.h>
                    #include <unistd.h>
                    
                    #include <fcntl.h>
                    #include <sys/stat.h>
                    
                    #include <iostream>
                    #include <string>
                    #include <fstream>
                    
                    using namespace std;
                    
                    //#define BUFSIZ 20
                    
                    int main()
                    {
                     /* create the pipe */
                     int  fds[2];
                     if (pipe(fds) == -1)
                       {
                         printf("pipe failed\n");
                         return 1;
                       }
                    
                     /* create the child */
                     int  pid;
                     if ((pid = fork()) < 0)
                       {
                         printf("fork failed\n");
                         return 2;
                       }
                    
                     if (pid == 0)
                       {
                         /* child */
                         //char buffer[20];
                    
                         close(fds[0]);
                         dup2(fds[1], STDOUT_FILENO);
                         execl("/home/jo-pc/Bureau/STAGE/Projet_spectro/Projet_spectro_1/Test/Acquisition","Acquisition",NULL);
                         close(fds[1]);
                    
                    
                       }
                     else
                       {
                         /* parent */
                         waitpid(pid,NULL,0);
                         char buffer[1000];
                    
                         close(fds[1]);
                         dup2(fds[0], STDIN_FILENO);
                    
                         while (read(fds[0], buffer, 1000) != 0)
                           printf("%s\n", buffer);
                    
                         close(fds[0]);
                    
                           //printf("%s", buffer);       printf("%s", buffer);       printf("%s", buffer);
                        std::cout << buffer << std::endl;
                    
                       }
                    
                     return 0;
                    }
                    

                    Ca fonctionne bien pour le moment. Je vais continuer d'exploiter ceci. Je fairais un retour. Merci beaucoup

                    • [^] # Re: revoir l'enoncé et choisir ce qui va bien

                      Posté par  . Évalué à 1. Dernière modification le 16 avril 2015 à 15:40.

                      Salut,
                      sinon t'as les mqueue POSIX (librt) qui peuvent être soit synchrone soit asynchrones (
                      O_NONBLOCK ), soit uni ou bidriectionnelles, et plus facile pour balancer des objets, et comme ça te laisse tes entrées/sorties standards pour autre chose (par exmemple de l'ASCII Art, du debug, ou que sais-je).
                      Voila une petite explication des fonctions: ici
                      et un exemple ici

  • # Un exemple simple pere/fils

    Posté par  . Évalué à 1.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
        int pid = 0;
        int pipefd[2];
        char buf= 0;
        printf("Hello, Vador before the fork\n");
        if(pipe(pipefd)== -1){
            perror("Pipe");
            exit(-1);
        }
    
        pid = fork();
        if(pid == -1) {
            perror("Fork");
            exit(-1);
        }   
        if(pid){
            printf("I'm Vador\n");
            close(pipefd[0]);
            buf = 1;
            write(pipefd[1],&buf,1);
            close(pipefd[1]);
            wait(pid);
        }else{
            printf("I'm Skywalker\n");
            close(pipefd[1]);
            buf = 0;
            read(pipefd[0],&buf,1);
            if(buf){
                printf("I'm the force\n");
            }
            close(pipefd[0]);
        }
        exit(0);
    }

    La fonction "read" est bloquante par défaut. Tant qu'il n'y a rien dans le pipe, le processus fils attend.

    Apres, tu as un grand nombre de possibilité, mais le plus simple si tu veux que le fils attende pour faire une action c'est de jouer avec des sémaphores (http://linux.die.net/man/7/sem_overview). Ainsi quand le pere a écrit, il incrémente de 1 la sémaphore, le fils qui appele le sémaphore le décrémente de 1 et tu joues avec une mémoire partagée ou un pipe bidirectionnel.

    Ainsi tant que tu ne modifie pas la valeur du sémaphore, le fils dort. La sémaphore est un bon moyen de gérer la synchro entre père/fils.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.