Table des matières Suivant

1  Clonage de processus


Exercice 1
 (Compréhension du clonage simple)   Observez le programme suivant.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    pid_t pid;

    printf("A: ce message s'affiche combien de fois ?\n");
    fflush(stdout);

    pid = fork();
    if (pid == 0) {
        /* qui suis-je, le pere ou le fils ? */
        printf("je suis le ...\n");
    } else {
        /* qui suis-je, le pere ou le fils ? */
        printf("je suis le ...\n");
    }

    printf("B: ce message s'affiche combien de fois ?\n");
    fflush(stdout);

    exit(EXIT_SUCCESS);
}
Question 1   Répondez aux questions posées dans le code.
Question 2   Pouvez-vous déterminer l’ordre d’apparition des messages ? Si oui donnez-le, sinon pourquoi ? Pouvez-vous alors déterminer un ordre partiel d’apparition des messages ?
Question 3   On ajoute dans le else, avant le printf l’instruction wait(NULL). Cela change-t-il quelque chose ?
Question 4   Pour garantir un fonctionnement plus sûr de ce code, que manque-t-il ?
Question 5   À quoi les instructions fflush(stdout) servent-elles ?

Exercice 2
 (Généalogie de processus)   On exécute le programme suivant.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    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);
}
Question 1   Déroulez son exécution et indiquez ce qui est affiché.
Question 2   Combien de processus sont lancés en tout (il pourra être utile de représenter sous forme arborescente les processus) ?
Question 3   Quels appels systèmes peut-on utiliser pour changer l’affichage et permettre un meilleur suivi des processus (savoir qui est fils de qui) ?

Clonage interactif

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 3
 (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 4
 (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() exécutée par le processus "
            "de pid %d\n", fname, getpid());
}

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

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

    exit(EXIT_SUCCESS);
}

Exercice 5
 (Multi-fourche)   La fonction multif() généralise la fonction trif() précédente.
typedef int (*func_t) (char *);
int multif (func_t f[], char *args[], int n);
Le type func_t est défini comme « pointeur sur une fonction à un paramètre chaîne de caractères et 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]() sera appelée avec arg[i] comme argument. 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 EXIT_SUCCESS que si chacun des processus fils a retourné EXIT_SUCCESS.

Pour tester en pratique cette multif(), on définira une fonction testfunc telle que :

On définira aussi d’autres comportements sur d’autres valeurs de l’argument (fonction qui prend du temps pour se terminer, par exemple en utilisant sleep, pour vérifier que les fonctions ne sont pas exécutées les unes après les autres mais de façon concurrente, etc.).

Enfin la commande multif prendra en argument les valeurs des arguments pour testfunc et se terminera avec la valeur de retour de multif() (./multif true false devra donc déclencher multif() avec n = 2, le tableau f contiendra 2 fois testfunc, le tableau args contiendra les chaînes "true" et "false" et se terminera en retournant EXIT_FAILURE).


Exercice 6
 (Pas de zombis)   Un processus ne désirant pas interagir avec son fils ne peut l’abandonner. À sa terminaison, le fils passerait à 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 penser, 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 forkfork().

Ce programme devra mettre en évidence le fonctionnement de forkfork() en rendant « visibles » les trois processus, l’adoption par init, etc.


Table des matières Suivant