Vous êtes sur la page 1sur 15

UNIVERSITE DE CARTHAGE DEPARTEMENT

FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE

Classe : GLSI A et B A-U : 2019/2020


Matière : SYSTEME D’EXPLOITATION II
Resp. : R. Beltaifa

Travaux Pratiques 3

Les appels système

1. Appel système de création d’un nouveau processus : fork


La seule manière de créer un nouveau processus sous UNIX/Linux est d'utiliser l'appel système fork(). Seul
le processus systemd (init) (pid=1) est démarré de manière particulière par le kernel(noyau). Le processus courant
peut créer un processus fils en utilisant l’appel système fork . Cette primitive provoque la duplication (clonage)
du processus courant.

Prototype en C :

#include<unistd.h>

pid_t fork() // pid_t : ce type est un long int

Fonctionnement de fork() :
 crée un nouveau processus (processus fils) par duplication du processus courant (processus
père).
 copie les segments de données et de pile du processus père; le segment texte est partagé.
 les « pid » du père et du fils sont différents.
 le fils hérite en partie de l'environnement du père.
 retourne un entier:
• en cas de succès:
 0 dans le fils.
 pid du fils dans le père.
• en cas d'échec
 -1 dans le père.
 le fils n'est pas créé.
Suite à un fork, les processus père et fils poursuivent l'exécution du même programme. La valeur
retournée par fork permet de distinguer entre le père et le fils.

1
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE

Programme 1 : Utilisation des appels système fork, getpid et getppid.

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

int main()
{
pid_t pid;

switch(pid=fork())
{
case -1:
printf("erreur"); return -1;
case 0:
printf("je suis le fils, mon numéro est %d\n", getpid()); return 0;
default:
printf("je suis le père, le numéro de mon fils est %d\n",pid); return 0;
}
return 0;
}

Exécution
$gcc -o exfork exfork.c
$./exfork
Programme 2 : utilisation de la fonction system()
Il y a une façon de créer un sous-processus en Unix/Linux, en utilisant la commande system(), de la
bibliothèque standard de C <stdlib.h>. Comme arguments, elle reçoit le nom de la commande (et peut-
être une liste d'arguments) entre guillemets. Il faut retenir que system() n'est pas un appel système, mais
une fonction C. Ce qui rend l'utilisation de la fonction system() moins performante qu'un appel système
de création de processus.
Tapez le code suivant ensuite expliquer et analyser le résultat. Exécuter plus qu’une fois pour voir les
différents affichages. A partir des affichages dessiner l’arborescence créée.

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

int main()
{
int pid;
printf("Hello World \n");
2
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
pid = fork();
if (pid == 0) {printf ("je suis le fils mon pid est : %d, mon père est %d\n", getpid(), getppid());
system("ps") ;}
Else
{ printf("je suis le père, le numéro de mon fils est %d \n", pid);}
return 0;
}

Programme 3 : utilisation de la boucle ‘for’ pour créer une arborescence de


processus.

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

int main()
{
int pid, i;

for(i=0;i<3;i++)
{
pid = fork();
if(pid == 0) printf("je suis le fils mon pid est : %d, mon pere est %d\n",getpid(),getppid());
else
{ if (pid>0) printf("je suis le pere, mon numero est %d le numéro de mon fils est %d
\n",getpid(), pid);
else printf("erreur");}
}

return 0;
}

Exemple d’un résultat d’exécution.

3
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE

Exécuter le même programme plus qu’une fois. Dessiner l’arborescence des processus créés et analyser
l’affichage et son changement d’une exécution à l’autre.
Programme 4 : utilisation de &&
#include <stdio.h>
#include<unistd.h> // fork()
#include<sys/types.h> //getpid()
#include<stdlib.h> // exit()
#include<sys/wait.h> // wait()

int main()
{
fork() && fork() && fork();
printf("num %d, pere %d\n", getpid(), getppid());
while(wait(0)>0);

return 0;
}

Analyser le résultat d’exécution du programme 4. Ci-dessus un exemple de résultat.

Programme 5 : utilisation de ||
#include <stdio.h>
#include<unistd.h> // fork()
#include<sys/types.h> //getpid()
#include<stdlib.h> // exit()
#include<sys/wait.h> // wait()

4
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
int main()
{
fork() || fork() || fork();
printf("num %d, pere %d\n", getpid(), getppid());
while(wait(0)>0);

return 0;
}

Résultat possible :

Répondez aux mêmes questions.

2. Les appels systèmes wait, sleep et exit

 La primitive wait() permet à un processus d’attendre la fin d’un de ses fils :


• Si pas de fils ou erreur, retourne -1.
• Sinon, retourne le PID du fils qui s’est terminé.
• Doit être appelé avec wait(0)
 La primitive exit permet de terminer le processus qui l’appelle :
• void exit (int status)
• status, permet d’indiquer au processus père qu’une erreur s’est produite
• status=0 si pas d’erreur
 Similaire à la commande shell sleep
• int sleep (int seconds)
• le processus qui appelle sleep est bloqué pendant le nombre de secondes spécifié.
 Différence avec wait()
• wait() bloque jusqu’à la fin du fils, alors que sleep() bloque un temps spécifié
Exemple :

5
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE

 Un appel à la primitive exit() provoque la terminaison du processus effectuant l’appel avec


un code retour valeur.
 Un processus qui se termine passe dans l’état Zombie et reste dans cet état tant que son
père n’a pas pris en compte sa terminaison.
 Le processus père récupère la terminaison de ses fils par un appel à la primitive wait()
 Le processus père, par un wait(), récupère la mort de son fils, cumule les statistiques de celui-
ci avec les siennes et détruit l’entrée de la table des processus concernant son fils défunt.
Le processus fils disparaît complètement.
 Un processus fils défunt reste Zombie jusqu’à ce que son père ait pris connaissance de sa
mort.
 Un processus fils orphelin, suite au décès de son père (le processus père s’est terminé avant
son fils) est toujours adopté par le processus 1 (systemd).

Programme 6 : programme 3 + l’appel système wait()


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

int main()
{
6
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
int pid, i;

for(i=0;i<3;i++)
{
pid = fork();
if(pid == 0) printf("je suis le fils mon pid est : %d, mon pere est
%d\n",getpid(),getppid());
else
{ if (pid>0) printf("je suis le pere, mon numero est %d le numéro de mon fils est %d
\n",getpid(), pid);
else printf("erreur"); while(wait(0)>0);}
}

return 0;
}

While(wait(0)>0) : permet de n’avoir aucun processus orphelin. Donc par rapport au résultat
précédents aucun père de numéro 1 n’apparaitra. Car tous les pères doivent attendre leurs fils.

Programme 7 avec l’appel système exit()


Ecrire un programme C qui permet de créer 2 fils avec la boucle for. Chaque fils créé doit afficher un
message 3 fois. Dans chaque affichage, le processus indique son numéro, le numéro de père et le numéro
du message qu’il affiche. On ne veut pas avoir de processus orphelins.

Vous allez utiliser deux scénarios. Le premier sans exit(0) dans le fils et le second avec exit(0) dans le
fils. Exécutez les deux progarmmes et analyser les résultats en termes d’affichages et de l’arborescence
de processus créés.

Scénario1 : Programme sans exit()

#include <stdio.h>
7
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
#include<unistd.h> // fork()
#include<sys/types.h> //getpid()
#include<stdlib.h> // exit()
#include<sys/wait.h> // wait()

int main()
{
int i,j;
for(i=0;i<2;i++)
{
switch (fork())
{
case -1: printf("erreur");
case 0 : for(j=1;j<4;j++)
printf("je suis le processus num %d, mon affichage num %d. Mon pere est :
%d\n",getpid(),j, getppid());

default: ;
}
}

for(i=0;i<2;i++) wait(NULL);
return 0;
}

Résultat d’exécution :

Le père 8145 (votre prog) a créé deux fils 8146 et 8147. Et le fils 8146 a créé un fils
8148

Scénario 2 : Même programme avec exit()

#include <stdio.h>
#include<unistd.h> // fork()
#include<sys/types.h> //getpid()
#include<stdlib.h> // exit()
#include<sys/wait.h> // wait()

int main()
{
int i,j;
8
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
for(i=0;i<2;i++)
{
switch (fork())
{
case -1: printf("erreur");
case 0 : for(j=1;j<4;j++)
printf("je suis le processus num %d, mon affichage num %d. Mon pere est :
%d\n",getpid(),j, getppid());
exit(0);

default: ;
}
}

for(i=0;i<2;i++) wait(NULL); //le père attend tous ses fils avant de terminer
return 0;
}

Résultat :

Avec exit(0) aucun fils ne peut créer d’autres fils. Chacun des fils fait l’affichage et termine.
Donc avec exit(), seul le père qui est votre prog qui créé deux fils

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid, pid2;
int i;

pid =fork();
if(pid>0) {
printf("je suis le père\n");
pid2=fork();
if(pid2==0) {for (i=0;i<10;i++) printf("%d , ",i);} else while(wait(0)>0);
while(wait(0)>0); //printf("salut");

}
else
{ if (pid==0) { sleep(1); for (i=10;i<20;i++) printf("%d , ",i);}
9
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
else return -1;}
return 0;
}

Travail à faire
Exercice 1 :

Tapez le programme exercice1.c et vérifiez ses résultats

// ajouter les bib

int main() {
int v=0 ;
system(“ps –f”);
printf(“\n\n”); fork();
system(“ps -f”);
printf(“V=%d \n\n”,v);
return 0;
}

a- Qu’est ce que vous constatez ?


b- Proposez une solution afin de séparer l’affichage du résultat de l’exécution du processus
père de celui du fils.
c- Modifiez votre solution en joutant une pause de deux secondes avant le traitement du
processus fils.
d- Changez la valeur de la variable v afin qu’elle affiche 1 par le processus père et 2 par le
processus fils.
Réponse :
a- On constate qu’il y a 3 affichages qui sont les résultats d’exécution de la commande ps –f (ces
affichages sont de la linge 1 jusqu’à la ligne 6, de la ligne 8 jusqu’à la ligne 15 et de la ligne 18 jusqu’à
la ligne 23) et deux affichages de la valeur de v (ligne 16 et ligne 24). Mais, on ne peut pas distinguer
les affichages du processus père à ceux de son fils, créé par fork(). En effet, on ne peut pas savoir si
la valeur de v dans la ligne 16 (ou ligne 24) est celle du père ou du fils. De même, pour les résultats
du deuxième et troisième affichage de ps-f. Le premier affichage de ps-f est du père, pas de confusion.

Exemple d’un résultat d’exécution :

1 UID PID PPID C STIME TTY TIME CMD


2 14082 5567 0 0 15:01 pts/1 00:00:00 sh -c ./a.out
3 14082 5572 5567 0 15:01 pts/1 00:00:00 ./a.out
4 14082 5573 5572 0 15:01 pts/1 00:00:00 sh -c ps –f
5 14082 5574 5573 0 15:01 pts/1 00:00:00 ps –f
6
7
8 UID PID PPID C STIME TTY TIME CMD
9 14082 5567 0 2 15:01 pts/1 00:00:00 sh -c ./a.out
10 14082 5572 5567 0 15:01 pts/1 00:00:00 ./a.out
10
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
11 14082 5575 5572 0 15:01 pts/1 00:00:00 ./a.out
12 14082 5576 5572 0 15:01 pts/1 00:00:00 sh -c ps –f
13 14082 5577 5575 0 15:01 pts/1 00:00:00 sh -c ps –f
14 14082 5578 5577 0 15:01 pts/1 00:00:00 ps –f
15 14082 5579 5576 0 15:01 pts/1 00:00:00 ps –f
16 V=0
17
18 UID PID PPID C STIME TTY TIME CMD
19 14082 5567 0 2 15:01 pts/1 00:00:00 sh -c ./a.out
20 14082 5572 5567 0 15:01 pts/1 00:00:00 ./a.out
21 14082 5575 5572 0 15:01 pts/1 00:00:00 [a.out] <defunct>
22 14082 5576 5572 0 15:01 pts/1 00:00:00 sh -c ps –f
23 14082 5579 5576 0 15:01 pts/1 00:00:00 ps –f
24 V=0

b- Afin de séparer ou de distinguer l’affichage du père à celui du fils, on propose la nouvelle version
du programme.

#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>

int main()
{
int v=0,pid;
system("ps -f");
printf("\n\n");
pid=fork();
if (pid > 0) {
wait(NULL);
printf("je suis le père\n");
system("ps -f");
printf("je suis le père, V=%d \n\n",v); }
Else
{if (pid ==0) {printf("je suis le fils\n");
system("ps -f");
printf("je suis le fils, V=%d \n\n",v); }
else printf("erreur de création du fils");}
return 0;
}

Dans le programme initial, en premier lieu il faut ajouter la valeur de retour de fork pour distinguer le tt
du père de celui du fils(pid=fork()). Ceci n’est pas suffisant car il est possible qu’une instruction du bloc
d’instructions du père (ou le fils) soit exécutée ensuite une de l’autre processus soit exécutée. Par
conséquent, on ne pourra pas distinguer le résultat de ps-f du père à celui du fils. Donc, en plus des
messages qu’on ajoute pour préciser si c’est le père ou le fils, il faut ajouter le wait. Ceci, va obliger le
père de ne pas suivre son exécution que si son fils termine. Wait(NULL) : le père attend la fin d’exécution
de son fils pour qu’il puisse suivre son exécution. Faites attention, le changement de l’emplacement de
wait change tout. Donc, wait doit absolument être dans l’endroit où elle est placée dans la nouvelle
version. N’oubliez pas d’ajouter l’entête wait.h. Cette proposition, garantis la séparation demandée pour
11
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
n’importe qu’elle exécution. Ci-dessous, un résultat d’exécution.

UID PID PPID C STIME TTY TIME CMD


runner16 7797 0 0 15:37 pts/1 00:00:00 sh -c ./a.out
runner16 7802 7797 0 15:37 pts/1 00:00:00 ./a.out
runner16 7803 7802 0 15:37 pts/1 00:00:00 sh -c ps -f
runner16 7804 7803 0 15:37 pts/1 00:00:00 ps -f

je suis le fils
UID PID PPID C STIME TTY TIME CMD
runner16 7797 0 0 15:37 pts/1 00:00:00 sh -c ./a.out
runner16 7802 7797 0 15:37 pts/1 00:00:00 ./a.out
runner16 7805 7802 0 15:37 pts/1 00:00:00 ./a.out
runner16 7806 7805 0 15:37 pts/1 00:00:00 sh -c ps -f
runner16 7807 7806 0 15:37 pts/1 00:00:00 ps -f
je suis le fils, V=0

je suis le père
UID PID PPID C STIME TTY TIME CMD
runner16 7797 0 0 15:37 pts/1 00:00:00 sh -c ./a.out
runner16 7802 7797 0 15:37 pts/1 00:00:00 ./a.out
runner16 7808 7802 0 15:37 pts/1 00:00:00 sh -c ps -f
runner16 7809 7808 0 15:37 pts/1 00:00:00 ps -f
je suis le père, V=0

c. Ajouter l’instruction sleep(2); dans la section du fils.

d. On doit affecter à v la valeur 1 dans la section du père et lui affecter la valeur 2 dans celle du fils.

#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>

int main()
{
int v,pid;
system("ps -f");
printf("\n\n");
pid=fork();
if (pid > 0) {wait(NULL);printf("je suis le père\n");
system("ps -f");
v=1;
printf("je suis le père, V=%d \n\n",v); }
else
{if (pid ==0) {
sleep(2);
printf("je suis le fils\n");
system("ps -f");
v=2;
12
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
printf("je suis le fils, V=%d \n\n",v); }
else printf("erreur de création du fils");}
return 0;
}

Exercice 2 :
Ecrivez les programmes en C permettant de créer des processus selon les figures suivantes :

a-

P1 P2 P3

rép :
fork()&&fork()&&fork()

// d’autres solutions avec pid=fork sont aussi possibles !


b-

P1 P2 P3

P21 P31

Rép :

fork()&&(fork()||fork())&&(fork()||fork())

c-

P1 P2 P3

13

P11 P21 P22


UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE

Rép : plus qu’une solution sont possibles. Ci-dessous une proposition.


int main()
{
int pid;
pid=fork(); /* p1 est créé */
if (pid=0) {fork();exit(0);} /* dans p1, p11 est créé, p1 termine */
else // dans p0
{ pid=fork(); //p2 est créé
if (pid=0) {fork() && fork();exit(0);} //dans p2, p21 et p22 créés, p2 termine
else fork(); // p3 est créé
}
return 0;
}

Exercice 3 :
Ecrire un programme qui crée n processus fils. Avec n est passée en paramètre.

Rép : déjà déposé dans la plateforme de l’uvt.

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

int
main (int argc, char **argv) // pour le passage en paramètre
{
int i;

pid_t pid;

if (argc > 1) //faites cette vérification pour être sûr que vous avez saisi un argument (le nombre n)
{
for (i = 0; i < atoi (argv[1]); i++) //atoi(argv[1] : te donne la valeur du premier arg

{
pid = fork ();

if (pid == 0)

{
Printf ("Je suis le fils mon numéro est %d , mon père est %d\n", getpid (), getppid ());
exit (0);

14
UNIVERSITE DE CARTHAGE DEPARTEMENT
FACULTE DES SCIENCES DE BIZERTE INFORMATIQUE
}

else

printf ("je suis le père mon numéro %d\n", getpid ());

}
while (wait (0) > 0); //Ecrivez cette insctruction pour que le père attend tous ses fils

}
return 0;

15