Up Next

1  Clonage de processus


Exercice 1
 (Généalogie de processus)   Expliquez le comportement du programme de la figure suivante.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int
main (void)
{
    int i;
    pid_t pid;

    for (i=0; i<3; i++) {
        pid=fork();
        if (pid == -1) {        /* erreur */
            perror("erreur fork");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {  /* fils */
            fprintf(stderr, "fils : %d\n", i);
        } else {                /* pere */
            fprintf(stderr, "pere : %d\n", i);
        }
    }
    exit(EXIT_SUCCESS);
}
Figure 1: Que fait ce programme ?

Clonage interactif

(sur une idée de..., merci !)

Le nombre de processus pouvant être lancés par un utilisateur, mais aussi par le système sont limités.

Si un programme (erroné) crée des processus en boucle, on risque de ne plus pouvoir allouer de processus, même pour tuer ledit programme.

On peut se garder de telles erreurs en TP en utilisant la fonction suivante

pid_t
ifork()
{
    fprintf(stderr, "fork() %d ? (^C to abort) ", getpid());
    fflush(stderr);
    getchar();
    return fork();
}

La saisie d’un retour chariot suffit à accepter le fork().


Exercice 2
 (Quatre fils)  
Question 1   Donnez un programme qui affiche les entiers de 0 à 3 par 4 processus différents. L’exécution de ce programme se termine après l’affichage des 4 entiers.
Question 2   Assurez que les processus fils affichent les entiers dans l’ordre croissant.

Exercice 3
 (Tri fourche)   Écrivez une fonction
void trif (void(*f1)(void), void(*f2)(void), void(*f3)(void));
Le processus exécutant trif(f1, f2, f3) engendre des processus exécutant respectivement les fonctions f1(), f2(), et f3(), et attend la fin des processus engendrés pour terminer la fonction.

Cette fonction est par exemple utilisée dans le contexte suivant :

static void
f(int seconds, const char *fname)
{
    sleep(seconds) ;
    fprintf(stderr, "Fonction %s() executee par le processus %d\n",
            fname, getpid()) ;
}

static void fa(void) { f(4, "fa"); }
static void fb(void) { f(2, "fb"); }
static void fc(void) { f(3, "fc"); }

int
main(void)
{
  trif(fa, fb, fc);
  fprintf(stderr, "terminaison de main()\n");

  exit(EXIT_SUCCESS);
}

Exercice 4
 (Multi fourche)   La fonction multif() généralise la fonction trif() précédente.
typedef int (*func_t) (int);

int multif (func_t f[], int args[], int n);
Le type func_t est défini comme « pointeur sur une fonction à un paramètre entier retournant un entier ».

Les arguments de multif() sont un tableau de telles fonctions, un tableau des arguments à passer à ces fonctions, et la taille n de ces tableaux. Chacune des fonctions est exécutée par un processus différent. (La fonction f[i](arg[i] sera appelée avec arg[i] comme valeur de paramètre.) Ce processus se termine en retournant comme statut la valeur de la fonction.

La fonction multif() se termine elle-même en retournant la conjonction des valeurs retournées par les processus fils : elle ne retourne une valeur vraie que si chacun des processus fils a retourné une valeur vraie.


Exercice 5
 (Pas de zombis)   Un processus ne désirant pas interagir avec son fils ne peut l’abandonner. À sa terminaison, le fils passerait en l’état zombi.

Cependant, considérant qu’un processus orphelin est adopté par le processus init de PID 1, on peut utiliser la technique dite du double fork :

Question 1   Donnez le code d’une fonction
typedef void (*func_t) (void *);

void forkfork(func_t f, void *arg);
qui utilise la technique du double fork pour faire exécuter le fonction f() par un processus petit fils.
Question 2   Comment faire en sorte que le père récupère le PID de son petit-fils ?
Question 3   Que pensez, en terme de coût de copie mémoire, de cette technique du double fork ?
Question 4   Donnez le corps d’un programme illustrant l’utilisation de notre fonction forkfork()

Up Next