IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Programme Multi-Processus par fork()


Sujet :

C

  1. #1
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut Programme Multi-Processus par fork()
    Bonjour,

    Je suis en train d'essayer de paralléliser un programme qui traite séquentiellement un grand nombre de fichiers.

    Pour cela, je vais utiliser la commande fork().

    De plus, le nombre de taches tournant en même temps sera faible, en tout cas inférieur à 10.

    Dès qu'une tache se terminera, une autre sera lancée, jusqu'à traitement de tous les fichiers.

    J'ai commencé à écrire cela (cf. plus bas).
    Mais je crains plus que tout la prolifération de processus zombies.

    Quelqu'un peut-il me dire si ce code (ce n'est qu'un début) est correct ?
    Merci.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
     
    #define MAX_PROCESS 4
     
    /* Balayette à zombies */
    void
    sigchld_handler (int signal)
    {
        while (0 < waitpid (-1, NULL, WNOHANG))
            ;
    }
     
    /* Mise en place du gestionnaire de signaux */
    int
    install_sigaction (void)
    {
        struct sigaction sa;
     
        memset (&sa, 0, sizeof (sa));
        sa.sa_handler = sigchld_handler;
        sa.sa_flags = 0;
        sigemptyset (&(sa.sa_mask));
        if (0 != sigaction (SIGCHLD, &sa, NULL))
            return -1;
     
        return 0;
    }
     
    void
    tache_secondaire (int num)
    {
        printf ("%d:fils> Je suis le processus fils (père:%d)\n", getpid (), getppid ());
        sleep (10 * num + 5);
    }
     
    int
    main (int argc, char *argv[])
    {
        int pidfils;
        int i;
     
        /* Mise en place du gestionnaire de signaux */
        if (-1 == install_sigaction ())
        {
            fprintf (stderr, "Problème avec sigaction()\n");
            exit (EXIT_FAILURE);
        }
     
        for (i = 0; i < MAX_PROCESS; i++)
        {
            pidfils = fork ();
            switch (pidfils)
            {
                case -1:
                    fprintf (stderr, "fork() a échoué\n");
                    exit (EXIT_FAILURE);
                case 0:
                    tache_secondaire (i);
                    exit (EXIT_SUCCESS);
            }
        }
     
        /* Programme principal exécuté par le père */
        printf ("%d:pere> Je suis le processus père\n", getpid ());
     
        exit (EXIT_SUCCESS);
    }

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    1 515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 515
    Par défaut
    Ton code ne fait pas ce que tu as dit. Il créé 10 process fils puis s'arrête. Il manque la logique qui fork un nouveau fils quand un des fils s'est arrêté.

  3. #3
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut
    Oui je sais « matafan ».

    Je n'en suis pas encore là, et je ne sais pas encore comment je vais implémenter cette mécanique

    Je veux simplement savoir si ce début de code est correct

  4. #4
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    1 515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 515
    Par défaut
    Ben oui, enfin il n'y a pas de grosse erreur qui saute aux yeux. Mais comme de toute façon ton code final n'aura rien à voir avec ça, je ne vois pas trop l'intérêt de s'étendre sur ce code. La partie délicate c'est justement le contrôle du nombre de processus.

  5. #5
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut
    Je pensais à quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
     
    #define MAX_PROCESS 4
     
    /* Balayette à zombies */
    void
    sigchld_handler (int signal)
    {
        while (0 < waitpid (-1, NULL, WNOHANG))
            ;
    }
     
    /* Mise en place du gestionnaire de signaux */
    void
    install_sigaction (void)
    {
        struct sigaction sa;
     
        memset (&sa, 0, sizeof (sa));
        sa.sa_handler = sigchld_handler;
        sa.sa_flags = 0;
        sigemptyset (&(sa.sa_mask));
        if (sigaction (SIGCHLD, &sa, NULL) != 0)
        {
            perror ("sigaction");
            exit (EXIT_FAILURE);
        }
    }
     
    void
    tache_secondaire (int num)
    {
        int delay = 10 * num + 5;
        printf ("%d:fils> Je suis le processus fils #%d (pere:%d), sleeping for %d\n", getpid (), num, getppid (), delay);
        sleep (delay);
    }
     
    int
    main (int argc, char *argv[])
    {
        int pidfils;
        int i;
        int nb_files = 13;
        int nb_tasks;
     
        /* Mise en place du gestionnaire de signaux */
        install_sigaction ();
     
        /* Boucle sur les fichiers */
        nb_tasks = 0;
        for (i = 0; i < nb_files; i++)
        {
            pidfils = fork ();
            switch (pidfils)
            {
                case -1:
                    perror ("fork");
                    exit (EXIT_FAILURE);
                case 0:
                    tache_secondaire (i);
                    exit (EXIT_SUCCESS);
            }
     
            nb_tasks++;
            if (nb_tasks > MAX_PROCESS)
            {
                waitpid (-1, NULL, WUNTRACED);
                nb_tasks--;
            }
        }
     
        /* Programme principal exécuté par le père */
        printf ("%d:pere> Je suis le processus père\n", getpid ());
     
        exit (EXIT_SUCCESS);
    }

  6. #6
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    1 515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 515
    Par défaut
    Non, ça ne marchera pas. Après avoir atteint le max de fils, le père se met en attente sur un waitpid(). Seulement quand un fils termine, tu ne sais pas si c'est le waitpid() qui va se "débloquer" en premier, ou si c'est ton signal handler qui va être appelé. Si c'est le signal handler, tu feras un waitpid() sans décrémenter nb_tasks, et tu ne forkera pas de nouveau fils.

    Tu devrais soit ne pas installer de signal handler, soit décrémenter nb_tasks dans le signal handler. Le plus simple dans ton cas étant de ne pas installer de signal handler (sinon il faut bien penser à masquer SIGCHLD dans ton signal handler, et déclarer nb_tasks en global et en volatile).

  7. #7
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut
    Oui, merci matafan, je venais aussi de m'en rendre compte.

    Je l'ai vu en ajoutant dans l'attente de la terminaison d'un fils, le code ci-dessous.

    Et ça me ramenait -1 !!!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
     
    #define MAX_PROCESS 4
     
    void
    tache_secondaire (int num)
    {
        /* int delay = 5 * num + 3; */
        int delay = num + 5;
        printf ("%d:fils> Je suis le processus fils #%d (pere:%d), standby de %d secondes\n", getpid (), num, getppid (), delay);
        sleep (delay);
    }
     
    int
    main (int argc, char *argv[])
    {
        int pidfils;
        int i;
        int nb_files = 13;
        int nb_tasks;
     
        /* Boucle sur les fichiers */
        nb_tasks = 0;
        for (i = 0; i < nb_files; i++)
        {
            pidfils = fork ();
            switch (pidfils)
            {
                case -1:
                    perror ("fork");
                    exit (EXIT_FAILURE);
                case 0:
                    tache_secondaire (i);
                    exit (EXIT_SUCCESS);
            }
     
            nb_tasks++;
            if (nb_tasks > MAX_PROCESS)
            {
                int pid;
                int status;
     
                printf ("%d:pere> J'attends la terminaison d'un fils...\n", getpid ());
     
                pid = waitpid (-1, &status, WUNTRACED);
                if (WIFEXITED (status))
                    printf ("\t\tchild (%d) terminated with return code (%d)\n", pid, WEXITSTATUS (status));
                else
                if (WIFSIGNALED (status))
                    printf ("\t\tchild (%d) killed by signal (%d)\n", pid, WTERMSIG (status));
                else
                    printf ("\t\tchild (%d) terminated\n", pid);
     
                nb_tasks--;
                printf ("%d:pere> ...ok, je continue\n", getpid ());
            }
        }
     
        /* Programme principal exécuté par le père */
        printf ("%d:pere> Fin du processus père\n", getpid ());
     
        exit (EXIT_SUCCESS);
    }
    Maintenant, ça me retourne bien le PID du process qui vient de se terminer.

    Mais comment puis-être certain de ne pas créer de processus zombie ?
    que puis-je ajouter pour rendre ce programme robuste ?

    Merci.

  8. #8
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    remplace
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if (nb_tasks > MAX_PROCESS)
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    while (nb_tasks != 0)
    Mais de toute maniere, quand tu quittes les process deviennent fils de init qui s'occupe des zombies. Donc si tu fais un wait avant de lancer un nouveau process, tu n'auras jamais plus de MAX_PROCESS zombies et ce, uniquement entre le dernier wait et la fin du pere.

  9. #9
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut
    Un grand MERCI pour ton aide « matafan »
    Si tu vois autre chose qui peut rendre ce programme robuste, je suis intéressé.

    [quote=Jean-Marc.Bourguet;5719477]
    remplace
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if (nb_tasks > MAX_PROCESS)
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    while (nb_tasks != 0)
    Non
    Car sinon il n'y pas 4 taches parallèles dès le départ.
    Au lancement, le programme lance bien les 4 taches max en prallèles comme souhaité.
    Puis il en lance une nouvelle dès qu'une des taches se termine.
    De plus, si on regarde la sortie actuelle, il y a bien 13 fichiers traités (de 0 à 12)

    1925340:fils> Je suis le processus fils #0 (pere:6103238), standby de 5 secondes
    7262344:fils> Je suis le processus fils #1 (pere:6103238), standby de 6 secondes
    6377720:fils> Je suis le processus fils #2 (pere:6103238), standby de 7 secondes
    60977334:fils> Je suis le processus fils #3 (pere:6103238), standby de 8 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    4927722:fils> Je suis le processus fils #4 (pere:6103238), standby de 9 secondes
    child (1925340) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6103238:pere> J'attends la terminaison d'un fils...
    1683580:fils> Je suis le processus fils #5 (pere:6103238), standby de 10 secondes
    child (7262344) terminated with return code (0)
    6103238:pere> ...ok, je continue
    7262346:fils> Je suis le processus fils #6 (pere:6103238), standby de 11 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    child (6377720) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6377722:fils> Je suis le processus fils #7 (pere:6103238), standby de 12 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    child (60977334) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6103238:pere> J'attends la terminaison d'un fils...
    60977336:fils> Je suis le processus fils #8 (pere:6103238), standby de 13 secondes
    child (4927722) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6103238:pere> J'attends la terminaison d'un fils...
    4927724:fils> Je suis le processus fils #9 (pere:6103238), standby de 14 secondes
    child (1683580) terminated with return code (0)
    6103238:pere> ...ok, je continue
    1683582:fils> Je suis le processus fils #10 (pere:6103238), standby de 15 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    child (7262346) terminated with return code (0)
    6103238:pere> ...ok, je continue
    7262348:fils> Je suis le processus fils #11 (pere:6103238), standby de 16 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    child (6377722) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6377724:fils> Je suis le processus fils #12 (pere:6103238), standby de 17 secondes
    6103238:pere> J'attends la terminaison d'un fils...
    child (60977336) terminated with return code (0)
    6103238:pere> ...ok, je continue
    6103238:pere> Fin du processus père

  10. #10
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Pardon, j'ai lu trop vite ton code. Tu mets une boucle

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    while (nb_tasks != 0)
            {
                int pid;
                int status;
     
                printf ("%d:pere> J'attends la terminaison d'un fils...\n", getpid ());
     
                pid = waitpid (-1, &status, WUNTRACED);
                if (WIFEXITED (status))
                    printf ("\t\tchild (%d) terminated with return code (%d)\n", pid, WEXITSTATUS (status));
                else
                if (WIFSIGNALED (status))
                    printf ("\t\tchild (%d) killed by signal (%d)\n", pid, WTERMSIG (status));
                else
                    printf ("\t\tchild (%d) terminated\n", pid);
     
                nb_tasks--;
                printf ("%d:pere> ...ok, je continue\n", getpid ());
            }
    en dehors de ta boucle for. (Et de preference tu mets le contenu dans une fonction parce que c'est la meme chose que le contenu du if (nb_tasks > MAX_PROCESS)).

  11. #11
    Membre éclairé Avatar de domiq44
    Homme Profil pro
    Inscrit en
    Novembre 2005
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2005
    Messages : 302
    Par défaut
    J'ai tenu compte de ta remarque « Jean-Marc » pour les 4 processus résiduels.

    Du coup j'ai fait ça

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
     
    #define MAX_PROCESS 4
     
    void
    tache_secondaire (int num)
    {
        /* int delay = 5 * num + 3; */
        int delay = num + 5;
        printf ("%d:fils> Je suis le processus fils #%d (pere:%d), standby de %d secondes\n", getpid (), num, getppid (), delay);
        sleep (delay);
    }
     
    void
    wait_for_a_task (int *nb_tasks)
    {
        int pid;
        int status;
     
        printf ("%d:pere> Il y a actuellement %d processus qui tournent\n", getpid (), *nb_tasks - 1);
        printf ("%d:pere> J'attends la terminaison d'un fils...\n", getpid ());
     
        pid = waitpid (-1, &status, WUNTRACED);
        if (WIFEXITED (status))
            printf ("\t\tchild (%d) terminated with return code (%d)\n", pid, WEXITSTATUS (status));
        else
        if (WIFSIGNALED (status))
            printf ("\t\tchild (%d) killed by signal (%d)\n", pid, WTERMSIG (status));
        else
            printf ("\t\tchild (%d) terminated\n", pid);
     
        printf ("%d:pere> ...un process vient de se terminer\n", getpid ());
     
        /* un process de moins */
        (*nb_tasks)--;
    }
     
    int
    main (int argc, char *argv[])
    {
        int pidfils;
        int i;
        int nb_files = 13;
        int nb_tasks;
     
        /* Boucle sur les fichiers */
        nb_tasks = 0;
        for (i = 0; i < nb_files; i++)
        {
            pidfils = fork ();
            switch (pidfils)
            {
                case -1:
                    perror ("fork");
                    exit (EXIT_FAILURE);
                case 0:
                    tache_secondaire (i);
                    exit (EXIT_SUCCESS);
            }
     
            nb_tasks++;
     
            /* Si nombre MAX_PROCESS de processus atteint */
            if (nb_tasks > MAX_PROCESS)
            {
                wait_for_a_task (&nb_tasks);
            }
        }
     
        /* On attend que se terminent les MAX_PROCESS processus résiduels */
        while (nb_tasks != 0)
        {
            wait_for_a_task (&nb_tasks);
        }
     
        /* Programme principal exécuté par le père */
        printf ("%d:pere> Fin du processus père\n", getpid ());
     
        exit (EXIT_SUCCESS);
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [WD17] Multi-fichier par programmation
    Par v95kp5s dans le forum WinDev
    Réponses: 5
    Dernier message: 11/06/2014, 22h26
  2. Programme multi processus qui marche aléatoirement
    Par Anonymouse dans le forum Linux
    Réponses: 1
    Dernier message: 13/10/2007, 20h56
  3. Réponses: 4
    Dernier message: 30/08/2005, 12h59
  4. Programmer un automate par RS232
    Par wael khalil dans le forum Langage
    Réponses: 6
    Dernier message: 25/08/2005, 16h02
  5. Processus.. adoption d'un processus par init
    Par Francois Trazzi dans le forum Administration système
    Réponses: 15
    Dernier message: 17/01/2005, 13h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo