Les processus sous Unix
© D. Mathieu mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 20/09/2000 -
Dernière mise à jour : 10/10/2001

Remarques préliminaires :
-
Prendre connaissance du document :
Les
processus sous Unix
-
Créer le répertoire tpsys/dirprocess, qui sera considéré
comme répertoire courant, jusqu'à une nouvelle indication.
-
Comme précédemment, ne testez pas avant que cela vous
soit explicitement demandé dans l'énoncé,
-
Pensez à consulter les
objectifs
pédagogiques
Sommaire
Quelques outils nécessaires à la manipulation des processus
Compréhension de
la fonction fork()
Identification des
processus père et fils
Processus démons
Héritage des propriétés
Constructeurs/destructeurs
et fork()
Fonction kill()
Compréhension
de la fonction kill()
Relations fils --> père : fonctions wait...()
Lancement de commandes
Quelques
outils nécessaires à la manipulation des processus
Wrapper de fork()
-
Ajouter à l'espace de noms nsSysteme le wrapper
de la fonction système
fork().
Wrapper de
kill()
-
Ajouter à l'espace de noms nsSysteme le wrapper de la fonction système
kill().
Corrigés :
nsSysteme.h
-
nsSysteme.hxx
Sommaire
Compréhension
de la fonction fork()
-
Héritage du tampon du clavier
-
Liste des fichiers : fork_a.cxx, fork_b.cxx, fork_c.cxx,
fork_d.cxx,
fork_e.cxx,
fork_f.cxx
Les programmes fork_*.cxx suivants sont dans le répertoire
dirprocess
des corrigés.
Recopiez-les dans votre répertoire courant.
Analyser le code et, avant de les lancer, essayez de prévoir ce que va donner chaque exécution.
Chaque programme doit être lancé au moins 10 fois.
Comparer avec le résultat effectif et, en cas de divergence, essayer de l'expliquer.
En cas de symptômes prolongés, consulter un spécialiste.
fork_a.cxx
// fork_a.cxx
#include <iostream>
#include <string>
#include <exception>
#include <sys/types.h> // pid_t
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);
cout << "Avant le fork()" << endl;
const ::pid_t pid = Fork();
cout << "Apres le fork() : pid = " << pid << endl;
return 0;
} // ppal()
| |
Corrigé
fork_b.cxx
// fork_b.cxx
#include <iostream>
#include <string>
#include <exception>
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);
cout << "Avant le fork()" << endl;
cout << "Apres le fork() pid = ";
cout << Fork() << endl;
return 0;
} // ppal()
|
Corrigé
fork_c.cxx
// fork_c.cxx
#include <iostream>
#include <string>
#include <exception>
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);
cout << "Avant le fork()" << endl;
cout << "Apres le fork() pid = " << endl;
cout << Fork() << endl;
return 0;
} // ppal()
|
Corrigé
fork_d.cxx
// fork_d.cxx
#include <iostream>
#include <string>
#include <exception>
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);
cout << "Avant le fork()" << endl;
cout << "Apres le fork() pid = \n";
cout << Fork() << endl;
return 0;
} // ppal()
|
Corrigé
fork_e.cxx
// fork_e.cxx
#include <iostream>
#include <string>
#include <exception>
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);
cout << "Avant le fork()" << endl;
cout << "Apres le fork() pid = " << Fork() << endl;
return 0;
} // ppal()
|
Corrigé
fork_f.cxx
// fork_f.cxx
#include <iostream>
#include <string>
#include <exception>
#include <stdio.h> // printf()
#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil;
using namespace nsSysteme;
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc) throw CExc (string ("Usage : ") + argv [0]);
cout << "Avant le fork()" << endl;
printf ("Apres le fork() pid = %d\n", Fork());
return 0;
} // ppal() |
Corrigé
Sommaire
Identification des processus père et fils
L'objectif de ces exercices est de reconnaître, dans un processus qui crée un ou plusieurs fils, quelles parties de code sont exécutées respectivement par le père et par le(s) fils.
Gnôthi seauton (connais-toi toi-même)
[1]
exo_01
-
Notion de numéro de processus (process identification)
-
Fonctions
getpid(),
getppid()
-
Liste des fichiers : exo_01.cxx
Dans le fichier exo_01.cxx, écrire la fonction ppal() qui crée un fils.
Le père affiche son propre numéro de pid et celui de son fils, le fils affiche son numéro de pid et celui de son père.
Compiler et tester.
Que se passe-t-il lorsque le fils commence par dormir une seconde ?
Rq1
Corrigé :
exo_01.cxx
Sommaire
Contrôle des naissances ou petits
lapins ?
exo_02
Liste des fichiers : exo_02.cxx
Dans le fichier exo_02.cxx, écrire un processus père qui crée 5 fils dans une boucle.
Immédiatement après chaque création, le père et le fils créé font un compte-rendu de l'opération en s'identifiant :
-
le père donne son pid et celui de son fils créé,
-
le fils donne son numéro (1, 2, etc.), son pid et celui de son père,
puis le fils se termine, pendant que le père passe à la création du fils suivant.
Compiler et tester.
Vérifier que les cinq fils ont été créés
Corrigé :
exo_02.cxx
Sommaire
Y a-t-il de l'écho ?
ah_que
-
Liste des fichiers : ah_que.cxx
Recopier dans le répertoire courant le fichier
ah_que.cxx
qui est dans le répertoire
dirprocess
des corrigés.
Analyser le code et prévoir à l'avance le résultat
de son exécution.
Comparer aver le résultat effectif, etc.
// ah_que.cxx
#include <iostream>
#include <exception>
#include <string>
#include "CException.h"
#include "nsSysteme.h" // Fork()
#include "CstCodErr.h"
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc)
throw CException (string ("Usage : ") + argv [0], CstExcArg);
for (int i = 4; --i; ) if (!Fork()) cout << "Coucou" << endl;
return 0;
} // ppal() |
Corrigé
Processus démons
Mise en évidence des démons existant
Par la commande ps -Al, vérifier l'existence du processus init et de nombreux autres démons.
Si on suppose que les premiers processus créés par le système ont des numéros de pid croissants, vérifier qu'il n'en a survécu que très peu de la phase initiale de lancement du système.
Sommaire
Création d'un démon
exo_03
-
Liste des fichiers : exo_03.cxx
Dans le fichier exo_03.cxx, écrire la fonction ppal() qui crée un démon dont le corps consiste à :
-
dormir une seconde (sleep()),
-
afficher les processus (commande ps lx effectuée par la fonction
system()
: voir l'utilisation de cette fonction dans
l'exo_05 du TP sur les fichiers),
-
entrer dans une boucle infinie.
Compiler et tester.
Vérifier que le processus fils existe bien toujours et est un démon.
Pourquoi est-il demandé de faire dormir le démon une seconde ? Le démon existe-t-il après que vous vous êtes délogé ?
Rq2
Corrigé :
exo_03.cxx
Sommaire
Héritage des propriétés
Héritage du traitement
des signaux
exo_04
-
Liste des fichiers créés ou modifiés :
exo_04.cxx,
Makefile,
INCLUDE_H,
MakeUtil
Recopier les fichiers SaveSig.h et SaveSig.cxx du répertoire dirsignal dans les répertoires include et util.
Modifier en conséquence les fichiers MakeUtil, INCLUDE_H et Makefile.
Dans le fichier exo_04.cxx, écrire la fonction ppal() qui :
-
déroute tous les signaux déroutables (utiliser la fonction nsSysteme::SaveSig()) vers une fonction qui indique le numéro du signal reçu et le pid du processus qui l'a reçu
-
affiche les messages suivants à l'écran :
Le père est arrêté par SIGINT
(Ctrl C)
Le fils est arrêté par SIGQUIT (Ctrl \) |
-
crée un fils.
Chaque processus annonce quel est son pid.
Le père et le fils modifient les traitants de signaux pour pouvoir être arrêtés respectivement par les signaux SIGINT ou SIGQUIT.
Chaque processus part ensuite dans une boucle infinie.
Dans la fonction de déroutement, afficher un message sous la forme suivante (par exemple) :
Signal 15 reçu par le père de pid 12345 |
ou
Signal 15 reçu par le fils de pid 12346 |
Compilez et testez en faisant les essais suivants :
-
en vous plaçant dans une autre fenêtre, envoyez aux deux processus les signaux SIGIOT, SIGUSR1, SIGALRM par exemple, en utilisant la commande :
-
défoulez-vous, et tuez enfin le père par Ctrl C! Que constatez-vous ? Envoyez encore quelques signaux au fils par la commande killall, puis tuez le fils par le signal prévu à cet effet.
Vérifiez qu'il est bien mort,
-
relancez l'ensemble puis arrêtez le fils par Ctrl \.
Expliquez ce que vous constatez.
Rq3
Corrigé :
exo_04.cxx
-
Makefile
-
INCLUDE_H
-
MakeUtil
Sommaire
Héritage des descripteurs de fichiers ouverts
exo_05
-
Liste des fichiers :
exo_05.cxx,
TestFdOuverts.h,
TestFdOuverts.cxx,
MakeUtil,
INCLUDE_H,
Makefile
Dans l'espace de noms nsSysteme (fichiers include/TestFdOuverts.h et util/TestFdOuverts.cxx ), écrire la fonction TestFdOuverts() qui parcourt la table des descripteurs et, pour chaque descripteur correspondant à un fichier ouvert, affiche, dans le flux qui lui est passé en paramètre (cout par défaut), le numéro du processus suivi de la valeur du descripteur.
La fonction
getdtablesize()
permet de connaître le nombre maximal de fichiers que peut ouvrir un processus, donc la taille maximale de la table des file descriptors.
Pour tester si un descripteur correspond à un fichier ouvert, appeler une fonction système qui doit échouer sur un fichier fermé, et récupérer l'exception.
Attention, si l'opération réussit (le fichier est ouvert), il ne faut pas qu'elle modifie l'état du fichier (donc pas de read() ou de write() ... ou de close()!!!).
Si nécessaire, vous pouvez utiliser une fonction concernant les fichiers que vous n'auriez pas encore mise en oeuvre (nous avons utilisé
lseek()).
Dans ce cas, ajoutez-la aux fichiers nsSysteme.*.
Mettre à jour les fichiers MakeUtil et INCLUDE_H en conséquence.
Dans le fichier exo_05.cxx, écrire la fonction ppal() qui ouvre plusieurs fichiers (ou plusieurs fois le même fichier !), en referme certains (pour faire des "trous" dans la table des file descriptors), puis appelle la fonction TestFdOuverts().
Mettre à jour le fichier Makefile.
Compiler et tester.
Lorsqu'elle a été validée, modifier la fonction ppal() pour créer un processus fils APRES avoir appelé la fonction TestFdOuverts().
Faites alors afficher les fichiers ouverts dans le processus fils.
Vérifier que ce sont bien les mêmes descripteurs qui correspondent à des fichiers ouverts dans les deux processus.
Rq4
Corrigé :
exo_05.cxx
-
TestFdOuverts.h
-
TestFdOuverts.cxx
-
MakeUtil
-
INCLUDE_H
-
Makefile
Sommaire
Partage du pointeur de fichier
[2]
exo_06
Liste des fichiers créés ou modifiés :
exo_06.cxx,
exo_06.txt,
nsSysteme.h,
nsSysteme.hxx
Ajouter, si ce n'est pas déjà fait, le wrapper de la fonction système
lseek()
à l'espace des noms nsSysteme.
Les fichiers exo_06.cxx et exo_06.txt sont dans le répertoire
dirprocess des corrigé.
Recopiez-les dans votre répertoire courant.
Analyser le code correspondant ci-dessous et, avant de lancer le programme, essayer de prévoir (par écrit) ce que peut donner chaque exécution.
/**
* @File : exo_06.cxx
**/
#include <string>
#include <iostream>
#include <exception>
#include <unistd.h>
// SEEK_SET
#include <fcntl.h>
// O_RDWR
#include "nsSysteme.h" // Open(),
Close(), Read(), Write(), Lseek()
#include "CException.h"
#include "CstCodErr.h"
using namespace std;
using namespace nsUtil; // CException
using namespace nsSysteme; // Open(), Close(), Read(),
Write(), Lseek()
int std::ppal (int argc, char * argv []) throw (exception)
{
if (1 != argc)
throw CException (string
("Usage : ") + argv [0], CstExcArg);
char c;
const int fd = Open ("exo_06.txt", O_RDWR);
if (Fork ())
/* 01 */
{
Lseek (fd, 10, SEEK_SET);
/* 02 */
Read (fd, &c,
1);
/* 03 */
cout << "lu par
le pere : " << c << endl;
/* 04 */
Read (fd, &c,
1);
/* 05 */
cout << "lu par
le pere : " << c << endl;
/* 06 */
}
else
{
Lseek (fd, 3, SEEK_SET);
/* 07 */
Read (fd, &c,
1);
/* 08 */
Write (fd, "ABCDE",
3);
/* 09 */
cout << "lu par
le fils : " << c << endl;
/* 10 */
}
Close (fd);
return 0;
} // ppal() |
Compiler et tester.
En analysant les affichages obtenus et en visualisant le fichier résultant exo_06.txt, retrouver le déroulement du programme, en utilisant les numéros des instructions indiquées dans le listing.
Exécuter plusieurs fois le programme, car les résultats peuvent être différents.
Pouvez-vous en conclure quelque chose concernant la localisation du pointeur de fichier ?
Est-ce en accord avec ce que vous avez (peut-être ...) montré au cours des
exercices correspondants
concernant les fichiers ?
Corrigé
Sommaire
Constructeurs/destructeurs
et fork()
exo_07
-
Liste des fichiers : exo_07.cxx
Dans le fichier exo_07.cxx, écrire dans l'espace de noms anonyme la struct CX qui comporte :
-
un constructeur sans paramètre qui affiche qu'il est appelé,
-
un destructeur qui affiche qu'il est appelé.
Dans la fonction ppal() définir un objet de la classe CX, puis créer un processus fils.
C'est tout.
Compiler, tester plusieurs fois et interpréter les résultats.
Rq5
Corrigé :
exo_07.cxx
Sommaire
Fonction kill()
Compréhension de la fonction
kill()
-
Liste des fichiers : TuQuoqueFili_a.cxx, TuQuoqueFili_b.cxx
Les programmes TuQuoqueFili_*.cxx suivants sont dans le répertoire ~mathieu/PARTAGE/src/tp/tpsys/tpprocess
dirprocess
des corrigés.
Recopiez-les dans votre répertoire courant.
Avant de les lancer, essayez de prévoir ce que va donner chaque exécution.
Comparer avec le résultat effectif et, en cas de divergence, essayer de l'expliquer (vous serez sans doute obligé de lire attentivement le man de la fonction système
kill()).
En cas de symptômes prolongés, consulter un spécialiste.
TuQuoqueFili_a
/**
* @File : TuQuoqueFili_a.cxx
**/
#include <string>
#include <exception>
#include <csignal>
// SIGKILL
#include <unistd.h>
// getppid(), sleep()
#include <sys/types.h> // pid_t
#include "nsSysteme.h" // Fork(),
Kill()
#include "CException.h"
#include "CstCodErr.h"
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork(), Kill()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc)
throw CException (string ("Usage : ") + argv [0], CstExcArg);
::pid_t n;
if (!(n = Fork ()))
{
::sleep (3);
Kill (n, SIGKILL);
}
else
Kill (::getppid (), SIGKILL);
return 0;
} // ppal()
|
Corrigé
Sommaire
TuQuoqueFili_b
/**
* @File : TuQuoqueFili_b.cxx
**/
#include <string>
#include <exception>
#include <csignal>
// SIGKILL
#include <unistd.h>
// getppid(), sleep()
#include <sys/types.h> // pid_t
#include "nsSysteme.h" // Fork(),
Kill()
#include "CException.h"
#include "CstCodErr.h"
using namespace nsUtil; // CException
using namespace nsSysteme; // Fork(), Kill()
int std::ppal (int argc, char * argv[]) throw (exception)
{
if (1 != argc)
throw CException (string
("Usage : ") + argv [0], CstExcArg);
::pid_t n;
if (!(n = Fork ()))
Kill (n, SIGKILL);
else
{
::sleep (3);
Kill (::getppid (),
SIGKILL);
}
return 0;
} // ppal()
|
Corrigé
Sommaire
Communication par signaux (facultatif)
exo_08
-
Liste des fichiers : exo_08.cxx
En utilisant aussi les fonctions Fork(), getpid(),
getppid(),
pause()
et sleep(), écrire deux processus père et fils qui
s'envoient mutuellement et périodiquement (de l'ordre de quelques
secondes) un signal (SIGUSR1 par exemple)
Compiler et tester.
Corrigé :
exo_08.cxx
Sommaire
Relations fils --> père
: fonctions wait...()
Wrappers des fonctions
système wait(), waitpid(), wait3() et wait4()
Ajouter à l'espace des noms nsSysteme les wrappers des fonctions système
wait()
,
waitpid()
,
wait3()
et
wait4()
Donner :
-
la valeur par défaut 0 au paramètre status de ces différentes fonctions,
-
la valeur par défaut 0 au paramètre option de la fonction waitpid(),
-
la valeur par défaut
WNOHANG
au paramètre option des fonctions wait3() et wait4(),
-
la valeur par défaut 0 au paramètre rusage des fonctions wait3() et wait4(),
Corrigé :
nsSysteme.h,
-
nsSysteme.hxx
Sommaire
status d'un fils mort
exo_09
-
Analyse des causes de la mort d'un processus fils
-
Utilisation de la macro standard WEXITSTATUS() et de la fonction système wait()
-
Liste des fichiers : exo_09.cxx
Sachant que la valeur de retour du fils est contenue dans le deuxième octet de plus faible poids du paramètre status de la fonction wait(), écrire dans le fichier exo_09.cxx une macro-instruction GETSTATUS qui renvoie cette valeur sous forme d'un entier non signé.
Attention les entiers peuvent être codés sur plus de 2 octets et seul le second octet doit être récupéré.
Dans la fonction ppal(), écrire un processus qui crée deux fils :
-
le premier se termine par un retour normal (utilisation de la valeur de retour de la fonction main()), la valeur renvoyée étant un argument de la commande,
-
le second se termine par appel à la fonction exit(), la valeur renvoyée étant un autre argument de la commande,
-
APRES avoir créé les deux fils, le processus père attend (au moyen de la fonction Wait()) la mort de chacun d'eux, dont il affiche le pid et la valeur du status en utilisant cette macro-instruction.
Montrer que la fonction wait() n'est pas interrompue par le signal SIGCHLD (en utilisant un bloc try-catch), mais que le signal SIGCHLD est cependant bien reçu par le processus père (en déroutant sur une fonction qui affiche son interception).
Rq6
Corrigé :
exo_09.cxx
Sommaire
status d'un fils stoppé
exo_10
-
Détection et analyse des causes de la suspension d'un processus fils
-
Utilisation des macros standard WIFSTOPPED(), WSTOPSIG(), WUNTRACED et de la fonction système wait3()
-
Liste des fichiers : exo_10.cxx
Afin de montrer que le père d'un processus peut être informé (par un signal SIGCHLD) qu'un de ses processus fils a été stoppé par un signal (grâce à la macro
WSTOPSIG(),
écrire dans le fichier exo_10.cxx la fonction ppal() qui effectue les opérations suivantes :
-
crée un fils qui part dans une boucle infinie,
-
attend dans une boucle un événement en provenance de son fils.
Si celui-ci a été stoppé par un signal (connu grâce à
WIFSTOPPED(),
qui nécessite l'utilisation de wait3() et de
WUNTRACED),
le père indique par un message le pid du fils stoppé et le numéro du signal qui l'a stoppé, puis relance le fils et recommence la boucle.
Si le fils est mort, le père sort de la boucle.
-
en sortie de boucle, le père indique si le fils s'est terminé normalement (exit() ou return) ou s'il a été tué par réception d'un signal non intercepté (utiliser WIFSIGNALED()).
Rq7
Compiler et tester, en envoyant au fils les deux signaux qui peuvent le stopper (SIGSTOP et SIGTSTP).
Les identifier grâce aux tableaux des signaux
(tableau 1
ou
tableau 2).
Il est intéressant d'effectuer le test suivant : envoyer au père et au fils le même signal SIGTSTP, en utilisant la commande Unix killall, puis relancer le fils.
Relancer ensuite le père.
Garde-t-il la trace de l'arrêt de son fils ?
Rq8
Corrigé :
exo_10.cxx
Sommaire
Mort de plusieurs fils
L'objectif des deux exercices suivants est de montrer que la fonction wait(), même utilisée sans en exploiter le paramètre status, ne peut pas être considérée seulement comme une simple fonction pause().
Version 1 - exo_11a
-
Liste des fichiers : exo_11a.cxx
Dans le fichier exo_11a.cxx,
-
dérouter le signal SIGCHLD vers une fonction qui affiche qu'un fils est mort,
-
écrire la fonction ppal() constituée de deux boucles :
-
dans la première boucle répétée NbreFils fois, (NbreFils passé en argument de la commande), le processus crée NbreFils fils.
Le ième fils dort i secondes puis se termine,
-
dans une deuxième boucle répétée NbreFils, le père attend successivement la terminaison de chacun de ses fils en appelant la fonction pause()
-
le processus père se termine lui-même après avoir affiché un message.
Compiler et tester.
Ajouter un argument facultatif à la commande (n'importe quoi).
Modifier le programme pour que, si cet argument est présent, tous les fils dorment le même temps (par exemple NbreFils secondes).
Compiler et tester (plusieurs essais seront peut-être nécessaires pour que vous obteniez le comportement attendu).
Rq9
Corrigé :
exo_11a.cxx
Sommaire
Version 2 - exo_11b
-
Liste des fichiers : exo_11b.cxx
Recopiez le fichier exo_11a.cxx (dernière version) dans le fichier exo_11b.cxx.
Remplacez l'appel de la fonction pause() par celui de la fonction Wait().
Compiler et tester.
Comparer le comportement par rapport à celui de l'exo_11a.
Rq10
Corrigé :
exo_11b.cxx
Sommaire
Autopsie des processus (facultatif)
exo_12
-
Analyse exhaustive du comportement du processus père par rapport à son fils.
-
Liste des fichiers créés ou modifiés :
exo_12.cxx
Makefile,
INCLUDE_H et
MakeUtil.
Cet exercice a pour objectifs d'illustrer les différentes possibilités de retour de l'appel à la fonction wait() par un processus père et d'analyser toutes les causes de retour possibles d'un processus fils.
Recopier à partir des répertoires include et util des
corrigés
les fichiers
Autopsie.h
et
Autopsie.cxx.
Ils contiennent la fonction Autopsie() de l'espace des noms nsSysteme qui injecte dans un flux de sortie la cause de la mort d'un processus.
Modifier en conséquence les fichiers Makefile, INCLUDE_H et MakeUtil.
Dans le fichier exo_12.cxx, montrer ce que provoque l'appel de la fonction Wait() en l'absence de fils.
Compiler et tester.
A l'espace de noms anonyme, ajouter une fonction de déroutement vide, et les trois fonctions Fils1(), Fils2() et Fils3(), dont les comportements sont les suivants :
-
le premier fils s'identifie en affichant son pid, dort pendant un délai qui lui est passé en paramètre, puis se termine par exit (1),
-
le deuxième fils s'identifie en affichant son pid, rétablit le traitement par défaut pour le signal SIGINT, puis entre dans une boucle indéfinie jusqu'à réception de ce signal.
-
le troisième fils est un (mauvais) farceur :
-
il s'identifie en affichant son pid,
-
dort pendant un délai passé en paramètre,
-
envoie à son père le signal SIGCHLD,
-
dort encore pendant un autre délai passé lui aussi en paramètre,
-
enfin se termine par l'instruction return 3;.
Dans la fonction ppal(), le père :
-
s'identifie avec son pid,
-
ignore le signal SIGINT,
-
déroute tous les signaux possibles vers la fonction Derout() (en utilisant la fonction SaveSig()),
-
lance ses trois fils,
-
boucle jusqu'à ce que tous ses fils soient morts, en affichant pour chacun l'analyse de leur mort.
-
affiche que ses trois fils sont morts.
Compiler et tester.
Rq11
Corrigés :
exo_12.cxx
-  
Makefile
-  
INCLUDE_H
-  
MakeUtil
Sommaire
Nettoyage des zombies : fonction wait3()
exo_13
-
Liste des fichiers : exo_13.cxx
Dans l'espace de noms anonyme, écrire
un "nettoyeur de zombies" qui autopsie chaque fils mort (boucle
for dans laquelle est appelée la fonction wait3()
non bloquante).
Ecrire la fonction ppal() qui :
-
crée huit fils, dont quatre dorment 3 secondes et les quatre autres
5 secondes avant de mourir,
-
effectue la commande ps lx,
-
part dans une boucle infinie.
Au bout d'un laps de temps supérieur à 5 secondes, vérifiez qu'il n'y a plus de zombies.
Corrigé :
exo_13.cxx
Sommaire
Tri sélectif : fonction waitpid()
exo_14
-
Liste des fichiers : exo_14.cxx
Ecrire un processus père qui crée 5 fils (le ième fils "dort" i secondes avant de se terminer) et attend la fin du fils dont le rang (entre 0 et 4) est passé en argument de la commande (waitpid()).
Corrigé :
exo_14.cxx
Sommaire
Lancement de commandes
exo_15
-
Lancement d'une commande à partir d'un processus (sans utiliser la fonction system())
-
Utilisation de la fonction execl()
-
Liste des fichiers :
exo_15.cxx,
exo_15a.cxx
Dans le fichier exo_15a.cxx, écrire un programme qui affiche le nombre et la liste des arguments passés lors de son lancement (argc et argv), ainsi que la liste des descripteurs de fichiers ouverts (au moyen de la fonction TestFdOuverts()).
Compiler et tester.
Essayer en tapant des arguments "bizarres" (contenant par exemple des caractères | * ; etc.) et trouver la solution pour qu'ils apparaissent dans la liste des arguments.
Rq12
On rappelle que les fonctions de la famille
exec...()
remplacent le code du processus qui appelle cette fonction par celui du programme qui est passé en paramètre à la fonction.
Si l'appel se déroule normalement, on ne revient donc jamais des fonctions exec...().
Donc tout retour signifie une erreur.
Il est donc inutile d'écrire un wrapper, mais il est indispensable de vérifier que le programme appelant ne revient pas de la fonction, en plaçant un traitement d'erreur juste après l'appel.
Dans le fichier exo_15.cxx, écrire un programme qui ouvre ou crée quelques fichiers puis lance le programme exo_15a (avec des paramètres quelconques) au moyen de la fonction execl().
Compiler et tester.
Corrigé :
exo_15a.cxx
-
exo_15.cxx
Sommaire
Fermeture des fichiers par exec
exo_16
-
Fichier "fermé-en-exec"
-
Fonctions système fcntl()
-
Liste des fichiers : exo_16.cxx
Ajouter à l'espace de noms nsSysteme les 3 wrappers des fonctions système
fcntl()
Recopier le fichier exo_15.cxx dans exo_16.cxx.
Dans la fonction ppal() :
-
ouvrir ou créer quelques fichiers,
-
positionner le flag "fermé-en-exec" de ces fichiers (voir le flag
F_SETFD
de la fonction fcntl() et la valeur FD_CLOEXEC),
-
appeler la fonction TestFdOuverts(),
-
au moyen de la fonction execl(), lancer le programme exo_15a avec des paramètres.
Compiler et tester.
Rq13
Corrigé :
exo_16.cxx
Sommaire
Emulation de la fonction system()
exo_17
-
Emulation de la fonction system()
-
Utilisation de la fonction execv()
-
Liste des fichiers créés ou modifiés :
exo_17.cxx,
Chrono.cxx
Récupérer le fichier Chrono.cxx dans le répertoire
dirprocess des corrigés.
Le compiler et le tester.
Dans la fonction ppal() du fichier exo_17.cxx, lancer au moyen de la fonction ::system() l'exécutable Chrono avec les paramètres désirés.
Compiler et tester.
Dans l'espace de noms anonyme, écrire la fonction System (const char * const Commande); qui émule la fonction système system().
Remplacer l'une par l'autre.
Compiler et tester.
Modifier la fonction ppal() pour lui permettre les possibilités suivantes :
-
si exo_17 est lancé sans argument (1 == argc), son comportement n'est pas modifié,
-
sinon, ses arguments représentent la commande à lancer.
Compiler et tester.
Corrigé :
exo_17.cxx
Sommaire
Téléchargement des corrigés :
tpprocess.zip
Chemins d'accès aux sources des corrigés :
~mathieu/PARTAGE/src/tp/tpsys/tpprocess/dirprocess
~mathieu/PARTAGE/src/tp/tpsys/tpprocess/util
~mathieu/PARTAGE/src/tp/tpsys/tpprocess/include
[1] devise de Socrate ...
[2] inspiré de "La programmation sous UNIX" de Rifflet
© D. Mathieu mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique