Vous êtes sur la page 1sur 6

TD/TP no 5 : Processus

Correction
Simon Patarin
Novembre 2002

1 Que réalisent les programmes suivants ?


Combien de processus sont créés ? Indiquez le résultat à l’écran, et dessinez le graphe
des processus.
1. int main(int argc, char **argv) {
int p1, p2, p3;
p1 = fork();
p2 = getpid();
p3 = getppid();
printf("p1=%d - p2=%d - p3=%d\n",p1,p2,p3);
return 0;
}
Le père crée un fils et affiche le pid de son fils, son propre pid et celui de son père
(celui du shell à partir duquel il a été lancé). Le fils affiche 0 pour p1, son pid et celui
de son père.
2. int main(int argc, char **argv) {
int p1, p2, p3;
p2 = getpid();
p3 = getppid();
p1 = fork();
printf("p1=%d - p2=%d - p3=%d\n", p1, p2, p3);
return 0;
}
Les processus père et fils affichent tous deux le pid du père et celui du père du père
(celui du shell), ainsi que le pid du fils pour le père et 0 pour le fils.
3. int main(int argc, char **argv) {
int pid;
int x = 2;
printf("x=%d\n",x);
pid = fork();
x = x+1;
printf("x=%d\n",x);
if (pid != 0) {
waitpid(pid, 0, 0);
}
return 0;
}
Chaque processus incrémente sa variable x après le fork(). Le processus père (qui
a reçu le pid du fils) attend sa terminaison. Les paramètres du waitpid sont le

1
pid du fils à attendre, un pointeur sur l’état du fils (entier composé de constantes
et valeurs spécifiques), et une option (composée par un OU logique entre diverses
constantes possibles). Dans notre cas, aucune option n’est fournie et aucun résultat
n’est récupéré.
4. int main(int argc, char **argv) {
if (fork() != 0) {
fork();
}
printf("coucou\n");
return 0;
}
Le processus père crée un premier processus fils dans le test du if et en crée un
deuxième si c’est bien lui. Les trois processus affichent ”coucou”.
5. int main(int argc, char **argv) {
if (fork() == 0) {
execlp("xterm",0);
}
return 0;
}
L’appel système exec est en fait découplé en 6 appels systèmes distincts :
3 execv(rien ou p ou e) selon que les arguments (du nouveau programme – et donc
de sa fonction main(int argc, char *argv[], char *arge)) sont pris
dans un tableaux (et donc de taille fixe) – de manière similaire au passage d’argument
de int main(int argc, char **argv).
Exemple : Le recouvrement de la commande ls -lR / donne
execv("/bin/ls", argv), où argv est un tableau de char *, préalablement
initialisé avec les arguments ”ls”, ”-lR”, ”/” et NULL.
3 execl(rien ou p ou e) selon que les arguments sont pris dans une liste terminée
par NULL (et donc de taille non déterminée).
Exemple : execl("/bin/ls", "ls", "-lR", "/", NULL)
Ensuite on a 2 exec(l ou v)p selon que le chemin du programme à remplacer est à
rechercher dans la variable d’environnement PATH. Les autres versions recherchent
le fichier du programme relativement au répertoire de travail du processus.
2 exec(l ou v)e selon que le nouvel environnement courant est fourni en pa-
ramètres. Les autres versions ne modifient pas l’envrionnement courant, et utilisent
donc le même environnement courant que le programme exécuté précédent.
Dans notre cas, on va rechercher dans l’ensemble des chemins possibles définis par
la variable PATH le programme xterm, qui va lancer un xterm, en lui passant aucun
argument.
Remarque : On ne revient pas d’un appel à exec, sauf si l’appel s’est mal déroulé
(erreur).
6. int main(int argc, char **argv) {
int pid[3], i;
for (i = 0; i < 3; ++i) {
pid[i] = fork();
}
printf("%d %d %d\n", pid[0], pid[1], pid[2]);
return 0;
}
On crée ici 3 processus fils. Le père affiche le pid de chacun.
Le premier fils crée 2 processus fils et affiche donc sa valeur de fils ainsi que les pid
de chacun de ses 2 fils.

2
Le deuxième fils crée un processus fils du père A et affiche donc le pid du processus,
alors que chaque fils ne peut afficher que sa propre valeur de fils (0).

père -A-
pid(A1),pid(A2),pid(A3)

fils A1 fils A2 fils A3


0,pid(A1-1),pid(A1-2) pid(A1),0,pid(A2-1) pid(A1),pid(A2),0

fils A1-1 fils A1-2 fils A2-1


0,0,pid(A1-1-1) pid(A1),0,0
0,pid(A1-1),0

fils A1-1-1
0,0,0

2 Écrivez les programmes suivants


1. Un programme qui crée deux processus (en plus de lui-même). Le premier proces-
sus exécute le programme prog1, le deuxième processus exécute le programme
prog2.
int main(int argc, char **argv){
int fils1, fils2;
if (fils1 = fork()) {
printf("PID(pere): %d, PID(fils1): %d\n",
getpid(), fils1);
if (! (fils2 = fork())) {
printf("PID(fils2): %d\n", getpid());
execl("prog2", 0, 0);
} else {
printf("PID(pere): %d, PID(fils2): %d\n",
getpid(), fils2);
}
} else {
printf("PID(fils1): %d\n", getpid());
execl("prog1", 0, 0);
}
}
2. Un programme qui crée 3 processus (en plus de lui-même) ; un programme qui crée
4 processus (en plus de lui-même). Quelle différence y a-t-il entre les deux pro-
grammes ? Pourquoi ?
int main(int argc, char **argv){
int i, fils;

3
for (i = 0 ; i < 3 ; i++) {
if (fils = fork()) {
printf("PID(pere): %d, PID(fils%d): %d\n",
getpid(), i, fils);
} else {
printf("PID(fils%i): %d\n", i, getpid());
break;
}
}
}
int main(int argc, char **argv){
int i, fils;

for (i = 0 ; i < 2 ; i++) {


if (fils = fork()) {
printf("PID(pere): %d, PID(fils%d): %d\n",
getpid(), i, fils);
} else {
int petitfils;
if (petitfils = fork()) {
printf("PID(fils%d): %d, PID(petitfils%d): %d\n",
getpid(), i, petitfils, i);
} else {
printf("PID(petitfils%i): %d\n", i, getpid());
}
break;
}
}
}

3 Pipes
1. Que réalise le programme suivant ? Faire le schéma. Pourquoi mettre le chiffre 8
plutôt que 7 (ou 6) dans le write() ? Comment remplacer ce chiffre pour qu’on
puisse changer facilement la taille du message à envoyer ?
int main(int argc, char **argv) {
int p[2], pid;
char buffer[16];

if (pipe(p)<0) exit(1);

pid = fork();
if (pid<0) exit(1);
if (pid==0) {
close(p[0]);
strcpy(buffer,"coucou\n");
write(p[1],buffer,8);
}
else {
close(p[1]);
read(p[0],buffer,16);
printf("J’ai lu: %s\n",buffer);

4
waitpid(pid,0,0);
}
}
Le programme crée un pipe, puis crée un fils (le canal est donc partagé entre le père
et le fils). Le fils envoie ensuite un message au père (après avoir préalablement fermé
le canal adéquat). La taille de message (8) comprend le caractère de fin de chaı̂ne,
pour indiquer la fin du message.
int main(int argc, char **argv) {
int p[2], pid;
char buffer[16];
const int MAX= 16;

if (pipe(p) < 0) exit(1);

pid= fork();
if (pid < 0) exit(1);
if (pid == 0) {
close(p[0]);
strcpy(buffer,"coucou\n");
write(p[1],buffer,strlen(buffer)+1);
}
else {
close(p[1]);
read(p[0],buffer,MAX);
printf("J’ai lu: %s\n",buffer);
waitpid(pid,0,0);
}
}
Que se passerait-il si on mettait le fork() avant le pipe() ?
Le canal de communication serait créé par chacun, le père et le fils, de façon indépendante.
Il ne serait donc pas partagé, et ne serait pas communicant.
2. Écrire un programme qui crée un processus (en plus de lui-même). Le processus père
envoie des chaı̂nes de caractères à son fils, qui lui renvoie la taille de la chaı̂ne. Le
père affiche le résultat.
int main(int argc, char **argv) {
int p1[2], p2[2], pid;
char buffer[23];
const int MAX= 100;

if (pipe(p1) < 0) {
perror("creation du premier pipe\n");
exit(1); }
if (pipe(p2) < 0) {
perror("creation du deuxième pipe\n");
exit(1); }

pid= fork();
if (pid < 0) {
perror("creation du fils");
exit(1); }

5
if (pid > 0) {
/* je suis le pere */
close(p1[0]);
close(p2[1]);
strcpy(buffer, "c’est ton papa ...\n");
write(p1[1], buffer, strlen(buffer)+1);
read(p2[0], buffer, MAX);
printf("la taille de la chaine donnee par mon fils
est de : %s\n", buffer);
close(p1[1]);
close(p2[0]);
} else {
/* je suis le fils */
close(p1[1]);
close(p2[0]);
read(p1[0], buffer, MAX);
printf("le fils a recu :\n %s", buffer);
sprintf(buffer,"%d", strlen(buffer));
write(p2[1], buffer, strlen(buffer)+1);
close(p1[0]);
close(p2[1]);
}
}
Que se passe-t-il si le père envoie plusieurs chaı̂nes de caractères successivement
avant de lire tous les résultats d’un bloc ? Comment éviter le problème ?
Le père lit tout le contenu du canal en une seule fois (on peut le remarquer en re-
cupérant le résultat de la primitive read() qui indique le nombre de caractères lus).
Pour éviter le problème, il faut lire chaque résultat avant de faire une écriture. (At-
tention à n’envoyer un buffer que de la taille requise, sinon, si on envoie un buffer
d’une taille MAX, c’est cette taille de buffer qui sera lue ... !)

Vous aimerez peut-être aussi