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 :

fork() wait () kill ()


Sujet :

C

  1. #1
    Membre habitué
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Points : 151
    Points
    151
    Par défaut fork() wait () kill ()
    Bonjour,

    Je me met au C sous Linux.

    J'ai des bonnes bases pour le C sous MS-DOS, mais je dois avouer que je bloque quelques peu sur les problèmes ci-dessus.

    Ma question est :

    Où peut-on lire un listing avec des processus père et fils ( fork ) , qui attendent un signal ( wait et kill ) et qui exécutent un handler.

    Ce que j'ai lu est très abstrait et il me manque des exemples pour approfondir ma compréhension.

    Mais peut-être, il y a t il un cours sur le site, dans ce cas je ne l'ai pas vu .

  2. #2
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    319
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 319
    Points : 346
    Points
    346
    Par défaut
    Salut !!

    J'ai fait à l'époque un petit programme servant à démontrer le fonctionnement de l'interception des signaux (kill) par un programme.
    Ca te donnera déjà une petite idée de la mise en place d'un "handler".

    Concernant le fork()... j'ai des applications qui l'utilisent mais je passe le plus souvent par les threads posix, voici le seul exemple que j'ai trouvé utilisant le fork().
    Fichiers attachés Fichiers attachés

  3. #3
    Membre habitué
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Points : 151
    Points
    151
    Par défaut Merci pour les exemples
    Je vais potasser tout ça.

    Mais quelle est vraiment l'utilité de créer un processus fils ?

    Est-ce pour le cas d'une commande qui demande un long temps d'exécution au fils qui permet ainsi au
    processus père de continuer de travailler ?

    A part cela, je ne vois pas.

  4. #4
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    319
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 319
    Points : 346
    Points
    346
    Par défaut
    Dans le cas où tu veuilles faire un "service" (un daemon) sous Linux, en général nous procédons de la sorte.

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 735
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 735
    Points : 31 060
    Points
    31 060
    Billets dans le blog
    1
    Par défaut
    Salut
    Citation Envoyé par frederic13870 Voir le message
    J'ai des bonnes bases pour le C sous MS-DOS
    Dommage, Unix (et son petit frère Linux) ayant été écrits en C, le C est particulièrement adapté sur ces OS. C'est même pour cela que le C est enseigné dans les écoles d'ingénieur. Ceci dit, tu as commencé par le plus difficile (C sur un OS qui n'est pas adapté) donc venir au C sous Linux devrait te sembler... rafraichissant.

    Citation Envoyé par frederic13870 Voir le message
    Mais quelle est vraiment l'utilité de créer un processus fils ?

    Est-ce pour le cas d'une commande qui demande un long temps d'exécution au fils qui permet ainsi au
    processus père de continuer de travailler ?

    A part cela, je ne vois pas.
    Déjà le shell fait cela par défaut. Quand tu appelles une commande (ex ls -l), le shell commence par lancer un fils. Le fils bascule ensuite sur le "ls" via la fonction execl() tandis que le père se met en attente de la fin du fils.
    A quoi cela sert ? Ben il te suffit de rajouter un "&" à la fin de ta commande (ex ls -l &) et là le père n'attend plus, il revient immédiatement à l'écoute d'une autre commande. C'est parce que le shell est déjà programmé pour générer un fils à chaque commande que lui rajouter la possibilité de mettre cette commande en "arrière plan" est facile (suffit juste que le père n'attende pas la fin du fils c'est à dire ne pas exécuter le wait(). Alors que s'il avait fallu faire le contraire (c'est à dire ne générer la commande dans un fils qu'en cas de demande en arrière plan) cela aurait été bien plus lourd à coder.

    Il y a aussi les serveurs, c'est à dire des programmes qui attendent une requête (au hasard serveur web, serveur bdd, etc) venant d'un client quelconque. Quand la requête arrive ils génèrent un fils chargé de s'en occuper tandis qu'eux reviennent à l'écoute de la requête suivante (client suivant).

    Citation Envoyé par frederic13870 Voir le message
    Où peut-on lire un listing avec des processus père et fils ( fork ) , qui attendent un signal ( wait et kill ) et qui exécutent un handler.
    Il y a quelques années j'avais tapé un petit exemple montrant un programme qu'on ne peut arrêter
    Code c : 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
    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
     
    void handler(int s) {
    	// signal(s, handler);		// A décommenter sur un OS plus vieux sur lequel le handler est désarmé à réception du signal => il faut alors le réarmer
    	printf("Je suis HIGHLANDER !!! Je suis INVULNERABLE au signal %d\n !!!", s);
    }
     
    int main() {
    	unsigned long i;
    	for (i=1; i <= SIGRTMAX; i++) signal(i, handler);
     
    	i=1;
    	while (1) {
    		printf("(i=%lu) - Je suis pid=%d - Essayez de me tuer...\n", i, getpid());
    		if ((i% 10) == 0) printf("... et ça commence à devenir long !!!\n");
    		i++;
    		sleep(1);
    	}
    }
    Bon manque le fork car tout se déroule dans le père mais ça reste compréhensible je pense. J'avais fait aussi un ping-pong entre deux processus mais je ne le retrouve pas. S'il t'intéresse je le réécrirai.

  6. #6
    Membre habitué
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Points : 151
    Points
    151
    Par défaut merci, c'est plus clair
    Et voici un exemple, plus sophistiqué que j'ai glané sur le man du raspberry pi Buster :

    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
     
    /* The following shell session demonstrates the use of the program:
     
               $ ./a.out &
               Child PID is 32360
               [1] 32359
               $ kill -STOP 32360
               stopped by signal 19
               $ kill -CONT 32360
               continued
               $ kill -TERM 32360
               killed by signal 15
               [1]+  Done                    ./a.out
               $
    */
     
           #include <sys/wait.h>
           #include <stdlib.h>
           #include <unistd.h>
           #include <stdio.h>
     
           int main(int argc, char *argv[])
           {
               pid_t cpid, w;
               int wstatus;
     
               cpid = fork();
               if (cpid == -1) {
                   perror("fork");
                   exit(EXIT_FAILURE);
               }
     
               if (cpid == 0) {            /* Code executed by child */
                   printf("Child PID is %ld\n", (long) getpid());
                   if (argc == 1)
                       pause();                    /* Wait for signals */
                   exit(atoi(argv[1]));
     
               } else {                    /* Code executed by parent */
                   do {
                       w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
                       if (w == -1) {
                           perror("waitpid");
                           exit(EXIT_FAILURE);
                       }
     
                       if (WIFEXITED(wstatus)) {
                           printf("exited, status=%d\n", WEXITSTATUS(wstatus));
                       } else if (WIFSIGNALED(wstatus)) {
                           printf("killed by signal %d\n", WTERMSIG(wstatus));
                       } else if (WIFSTOPPED(wstatus)) {
                           printf("stopped by signal %d\n", WSTOPSIG(wstatus));
                       } else if (WIFCONTINUED(wstatus)) {
                           printf("continued\n");
                       }
                   } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
                   exit(EXIT_SUCCESS);
               }
           }
    Compilé avec gcc sius DEBIAN, ce programme fonctionne parfaitement.

    Est-ce que quelqu'un se sent de m'expliquer le fonctionnement ?

    Surtout la condition du " do " que je ne comprends pas.

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 735
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 735
    Points : 31 060
    Points
    31 060
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par frederic13870 Voir le message
    Est-ce que quelqu'un se sent de m'expliquer le fonctionnement ?

    Surtout la condition du " do " que je ne comprends pas.
    Un père lance un fils, puis attend que le fils se termine, d'où ce "do" qui permet au père de boucler sur cette attente.
    Une fois le fils terminé, il récupère son état de sortie et affiche cet état.

    Et c'est là que ça devient intéressant car un processus (par exemple le fils comme ici) peut s'arrêter de deux façons
    • de lui-même via exit(n) (0 <= n <= 255)
    • par un signal "s" envoyé depuis l'extérieur via kill(s) (1 <= s <= 64). A quelques exceptions près comme 19 (SIGSTOP) qui suspend le processus et 18 (SIGCONT) qui le relance, et 9 (SIGKILL) qui s'adresse non pas au processus lui-même mais à l'ordonnanceur de tâche en lui demandant de virer le processus de la liste des processus à exécuter ce qui fait que ce signal là ne peut pas être "trappé" par ledit processus, les processus "meurent" quand ils reçoivent un signal

    Le père, quand il récupèrera cet êtat, doit pouvoir distinguer de quelle façon s'est arrêté le fils. Hé bien il récupère cet état dans un int (16 bits). Et si le fils a été arrêté par exit(n) la valeur "n" se trouve stockée dans les 8 premiers bits de l'int, et si c'est kill(s) la valeur "s" se trouve stockée dans les 8 derniers bits de l'int.
    Suffit donc de regarder ces 8 derniers bits et s'ils sont à 0 ("s" ne pouvant pas être égal à 0) c'est que c'était exit(n) et sinon c'était kill(s). Suffit ensuite d'extraire les bits adéquats pour récupérer "s" ou "n".
    Et comme cette procédure peut évoluer demain, elle a été encapsulée dans 4 macros
    • WIFEXITED() qui indique si c'était exit()
    • WIFSIGNALED() qui indique si c'était kill()
    • WEXITSTATUS() qui récupère le "n" de exit(n)
    • WTERMSIG() qui récupère le "s" de kill(s)

    Plus deux macros WIFSTOPPED() qui indique si un processus est stoppé (pas arrêté, juste mis en pause) et WIFCONTINUED() qui indique s'il est reparti.

    Donc la condition du "do", c'est "si pas de sortie par kill() ou exit()".

  8. #8
    Membre habitué
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Points : 151
    Points
    151
    Par défaut Merci
    Merci beaucoup pour cette claire et brillante explication.

    Mais, dans quels cas , est-il intéressant d'utiliser un tel schéma , à part comme précisé plus haut ,
    pour la gestion d'un serveur , web par exemple , ou bien une base de donnée ?

    Inutile de dire que dans ces deux cas , cet ingénieux processus est " transparent " pour l'utilisateur
    et je n'ai pas l'intention de " construire " de tels serveurs, mais d'utiliser l'existant.

    Avez-vous rencontrer dans votre expérience professionnelle des cas où il était nécessaire
    de coder de tels schémas ?

    Merci encore.

  9. #9
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 822
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 822
    Points : 44 114
    Points
    44 114
    Par défaut
    Sous Linux, le principe pour lancer un nouveau programme commence par un fork. fork va créer un processus identique à son père (le déclencheur du fork), à l’exception de certains éléments comme les verrous, les signaux en attente, il voir les fichiers ouverts comme son père. l'enfant est distinguable du père par le retour de la fonction fork : le père aura le numéro du processus créé, le fils aura 0. a partir de ce moment, chaque processus fera sa vie avec le même code, qui continue dans chaque processus par ce qu'il y a juste après le fork.
    C'est pour ça que dans les exemples, on voit tout de suite un if qui va exécuter un code différent selon qu'on soit dans le père ou le fils. L'étape va être ensuite pour chaque processus de mettre en place sa gestion de signaux. Dans le cas du lancement d'un nouveau programme, soit le père attend la fin de l’exécution du programme enfant, soit il fait sa vie (par défaut il fait sa vie). On peut aussi utiliser les signaux pour communiquer entre processus. Dans le cas du lancement d'un nouveau programme, le processus enfant va ensuite lancer une des fonctions de la famille execve, qui va écraser son espace mémoire par la projection de l’exécutable. A ce stade, le processus enfant n'a plus rien à voir avec son père mise à part sa relation de filiation.

    Pour la gestion de signaux, ça se fait chez le père et le fils. Le père doit envoyer un signal au fils, mais le fils doit traiter ce signal.

    Attention :
    On parle de killer un processus pour y mettre fin. Mais la fonction kill n'est pas une fonction pour killer un process, on lui envoi un numéro de signal, les plus connus étant SIGTERM, demandant au processus de se fermer (charge au processus de se fermer correctement dans sa gestion de ce signal), SIGKILL pour killer le process. Tu as les SIGUSR1 et SIGUSR2 que tu peux paramétrer comme je l'expliquais pour envoyer ou recevoir une info entre processus. Et il y a les signaux que l'on aime pas recevoir : SIGSEGV, appelé aussi SEGFAULT quand le process est killé pour violation d'accès mémoire (lecture/écriture non autorisée sur une adresse mémoire)

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 735
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 735
    Points : 31 060
    Points
    31 060
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par frederic13870 Voir le message
    Mais, dans quels cas , est-il intéressant d'utiliser un tel schéma , à part comme précisé plus haut ,
    pour la gestion d'un serveur , web par exemple , ou bien une base de donnée ?
    Avez-vous rencontrer dans votre expérience professionnelle des cas où il était nécessaire
    de coder de tels schémas ?
    Un très bel exemple d'un tel schéma est le service "cron". C'est un service Unix/Linux qui permet de planifier des tâches. On peut lui dire par exemple "chaque dimanche à 14h18 tu lanceras tel programme" etc (la gamme des possibilités de planification est assez étendue et a une granularité au niveau de la minute).
    Toutefois c'est un service qui tourne en permanence donc quand il se lance au démarrage de la machine il enregistre dans sa mémoire toutes les tâches prévues puis se charge ensuite de les lancer à l'heure dite. Mais comment faire quand un utilisateur rajoute une tâche c'est à dire modifie le fichier contenant la liste des tâches ? Faut-il relire le fichier à intervalles fixes pour se réactualiser ?
    Pas besoin. Le programme est prévu pour recharger le fichier à réception d'un signal SIGHUP (Hands-Up, qui vaut 1). Donc quand l'utilisateur rajoute une tâche, il lui suffit ensuite d'envoyer (kill) ce signal au cron qui recharge alors son fichier de config. Et cette action est intégrée dans le programme crontab qui a pour action d'éditer le fichier de config (option "-e") puis d'envoyer ledit signal.

  11. #11
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 822
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 822
    Points : 44 114
    Points
    44 114
    Par défaut
    Ou tout simplement un navigateur Web, chaque onglet va tourner dans un processus différent et autonome ayant pour père le premier processus crée par le dit navigateur à son ouverture en schématisant : la fenêtre principale. Bien sûr à la fermeture du navigateur, le processus parent va fermer proprement tous ses fils avant de se terminer lui-même. Si la fenêtre principale est tuée (par plantage ou par kill), ses enfants seront orphelins et ils seront adoptés par le premier processus du noyau qui les killera. Par contre si un des onglets (donc un processus) plante, les autres pourront continuer.

    le fonctionnement sera globalement le même sur tout système d'exploitation multitâche. Mais fork est spécifique Unix/Linux.

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

Discussions similaires

  1. Fork & wait(0)
    Par stoner dans le forum Débuter
    Réponses: 7
    Dernier message: 14/02/2013, 03h38
  2. [debutant] fork et wait
    Par nyakooza dans le forum Linux
    Réponses: 4
    Dernier message: 26/08/2008, 22h27
  3. fork() wait() waitpid() et companie
    Par guillaume_60 dans le forum POSIX
    Réponses: 3
    Dernier message: 23/05/2006, 13h03
  4. fork kill
    Par krusaf dans le forum Windows
    Réponses: 5
    Dernier message: 01/09/2005, 12h32
  5. fork wait
    Par lafracas dans le forum POSIX
    Réponses: 2
    Dernier message: 19/03/2005, 12h57

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