0% ont trouvé ce document utile (0 vote)
83 vues9 pages

TD1 Correction Avec Question

Le document présente une série d'exercices sur les systèmes d'exploitation, en particulier sur la gestion des processus en C. Chaque exercice aborde des concepts tels que la création de processus, l'utilisation de fork(), et la synchronisation des processus. Les solutions incluent des explications détaillées et des exemples de code pour illustrer les concepts abordés.

Transféré par

Noobe jzm
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
83 vues9 pages

TD1 Correction Avec Question

Le document présente une série d'exercices sur les systèmes d'exploitation, en particulier sur la gestion des processus en C. Chaque exercice aborde des concepts tels que la création de processus, l'utilisation de fork(), et la synchronisation des processus. Les solutions incluent des explications détaillées et des exemples de code pour illustrer les concepts abordés.

Transféré par

Noobe jzm
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Université Sultan Moulay Slimane

Faculté Polydisciplinaire Beni Mellal 2019/2020


Département de Mathématiques et Informatique SMI, S4
Système d’exploitation II

Série 1: TD
Exercice 1
1) Répondre par OUI ou NON en justifiant vos réponses.
a. Un processus est une entité produite après compilation
b. Un processus est une entité produite après chargement d’un binaire en mémoire
c. Le pseudo-parallélisme impose aux processus de se connaître mutuellement
2) Dans le système Unix: Est-ce que tout processus a un père ? Que se passe-t-il lorsqu’un
processus devient orphelin ? Quand est-ce un processus passe à l’état Zambie ?
3) Ecrire un programme C qui affiche : L’identifiant du processus, l’identifiant du père du
processus. le propriétaire réel, le propriétaire effectif, le groupe propriétaire réel, le groupe
propriétaire effectif.

Solution
Déjà fait dans la classe
Exercice 2
Préciser le nombre de processus créé par les programmes ci-dessous :
Code1 Code2 Code3
int main(){ int main(){ int main(){
fork(); if(fork() > 0) int cpt=0;
fork(); fork(); while (cpt < 3){
} } if (fork() > 0) cpt++;
else cpt=3;}

Solution
Code 1: 4 processus sont créés:
– L'exécution du programme crée un processus P1.
– A la lecture de la première instruction fork(), P1 se duplique et crée alors P2. Les deux processus
continuent l'exécution à partir de la ligne incluse ;
– A la lecture de la seconde instruction fork(), P1 se duplique et crée P3 tandis que P2 crée P4

Code 2 : 3 processus sont créés:


– L'exécution du programme crée un processus P1.
– A la lecture de la première instruction fork(), P1 se duplique et crée alors P2. P1 est le processus
parent, P2 le processus enfant.
– Les deux processus continuent l'exécution à partir de la ligne incluse ; Le résultat de l'appel
précèdent est supérieur à 0 pour P1. Ce dernier rentre donc dans la suite d'instructions conditionnées
et exécute l'instruction fork().
– P1 se duplique et crée donc P3.
– En revanche, le résultat de l'appel précèdent est égale à 0 pour P2, qui ne rentre donc pas dans la
suite d'instructions conditionnées

Code 3 : 4 processus sont créés:


– L'exécution du programme crée un processus P1, qui initialise la variable cpt à 0.
– P1 rentre dans la boucle while() et se duplique lors de l'exécution de fork(). Il crée alors P2.
– Le résultat de l'appel précèdent est supérieur à 0 pour P1. Ce dernier rentre donc dans la suite
d'instructions conditionnées par "if" et incrémente son compteur cpt qui passe à 1.
– En revanche, le résultat de l'appel précèdent est égale à 0 pour P2, qui rentre donc dans la suite
d'instructions conditionnées par else et affecte cpt à 3. Dès lors P2 sort de la boucle et n’exécutera
plus d'instruction.
– Seul P1 re-exécute la séquence d'instruction de la boucle while(), et le même schema se reproduit:
à chaque entrée dans la boucle, P1 se duplique, tandis que le processus duplique n'exécute aucune
instruction.
– P1 aura ainsi exécuté 3 fois l'instruction fork() jusqu'à ce que sa variable cpt atteigne 3.
– Il aura donc engendrée P2, P3 et P4

Exercice 3
On considère le programme suivant :
#include <stdio.h>
#include <unistd.h>
int main(){
fork(); printf("fork1\n");
fork(); printf("fork2\n");
fork(); printf("fork3\n");
return(0);}
1) Expliquez l’exécution de ce programme ? Précisez en particulier le nombre total de processus
engendrés par cette exécution ?
2) Combien d’occurrences de chaque type de messages fork i sont affichées ?

Solution
1) 8 processus sont créés (explication : voir l’exercice précèdent)
2)
P1: P5 :
fork1 fork3
fork2
fork3
P6 :
P2 :
fork3
fork1
fork2
fork3
P7 :
P3 :
fork3
fork2
fork3
P8 :
P4 :
fork3
fork2
fork3
Exercice 4
1) Ecrire un programme C qui crée deux fils, l'un affichant les entiers de 1 à 50, l'autre de 51 à 100.
2) Modifier le programme précédent pour que l'affichage soit 1 2 3 ...100.
Solution
1)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
int i;
if ((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){ /* fils1 */
for (i = 1; i <= 50; i++)
printf("%d\n", i);
return 0;
}
if ((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){ /* fils2 */
for (i = 51; i <= 100; i++)
printf("%d\n", i);
return 0;
}
return 0;
}
2) Pour synchroniser l'affichage, il suffit d'ajouter wait() avant la création du second fils.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int i;
if ((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){ /* fils1 */
for (i = 1; i <= 50; i++)
printf("%d\n", i);
return 0;
}

wait(NULL);

if ((pid = fork()) == -1){


perror("fork");
exit(1);
}
if (pid == 0){ /* fils2 */
for (i = 51; i <= 100; i++)
printf("%d\n", i);
return 0;
}
return 0;
}

Exercice 5
On considère le programme suivant et son schéma d'exécution.
int main(){
fork();
printf("hello\n");
exit(0);}
Question: Illustrer l'exécution de ce programme :
int main(){
fork();
fork();
fork();
printf("hello\n");
exit(0);}

Solution

Exercice 6
On considère les deux structures de filiation (chaîne et arbre) représentées ci-après.

Question: Ecrire un programme C qui réalise une chaîne de n processus, où n est passé en
paramètre de l'exécution de la commande (par exemple, n = 3 sur la figure ci-dessus). Faire
imprimer le numéro de chaque processus et celui de son père. Même question avec la structure en
arbre.
Solution
#include <stdlib.h> /* exit, atoi */
#include <stdio.h> /* printf */
#include <unistd.h> /* getpid, fork */
#include <wait.h> /* wait */

/* création de la structure de filiation chaîne */

void chaine(int n){


int i;
printf("Lance %d processus en chaine\n\n",n);
printf("proc %d fils de %d (shell)\n",getpid(),getppid());
for (i=0; i<n; i++) {
if (fork() == 0) { /* fils */
printf("proc %d fils de %d\n",getpid(),getppid());
exit(0);
} else
wait(NULL); /* RQ1: Si on le met pas, on n’a pas le bon schéma */
}
}

/* création de la structure de filiation arbre */

void arbre(int n) {
int i;
printf("Lance %d processus en arbre\n\n",n);
printf("proc %d fils de %d (shell)\n",getpid(),getppid());
for (i=0; i<n; i++) {
if (fork() == 0) { /* fils */
printf("proc %d fils de %d\n",getpid(),getppid());
} else
/* wait(NULL); RQ2: Si on le met, on n’a pas le bon schéma */
exit(0); /* RQ3: Si on l'enlève, on a pas le bon schéma */
}
}

int main(int argc, char *argv[ ]) {


if (argc<2) {
printf("Usage:\n%s 1 <n> pour lancer <n> processus en arbre\n%s 2
<n> pour lancer <n> processus en chaine\n",argv[0], argv[0]);
exit(1);
}
switch(atoi(argv[1])) {
case 1: arbre(atoi(argv[2])); break;
case 2: chaine(atoi(argv[2])); break;
}
return 0;
}

Y'a trois erreurs classiques, aux trois lignes marquées RQ1, RQ2 et RQ3.
RQ1 : Si on ne met pas ce wait, on change le schéma d'exécution wait est donc une primitive de
syncronisation (en plus de nettoyer les zombies).
RQ2 : Si on ajoute un wait ici, on change le schéma :
Les pères attendent leur fils respectif.
La question suivante est de savoir ce qui se passe si on ne met pas ce wait. Que deviennent les
zombies dont le père est mort ? ? La réponse tient en deux éléments :
- Tout processus dont le père est mort est adopté par le processus 1 (init). Donc getppid=1
pour les orphelins.
- init est surtout utilisé au lancement de la machine (il est chargé de lancer tous les processus
nécessaires dans l'espace utilisateur, comme par exemple login) et à l'arrêt de la machine
(c'est lui qui fait du propre parmi les processus utilisateurs). Entre temps, il passe son temps
dans une boucle while(1) {wait(NULL)}. Il attend donc la fin des processus orphelins.
RQ3 : Si on enlève cet exit, on fait beaucoup trop de fils (car chaque père recrée des fils à chaque
tours de boucle). On fait donc 2n processus au lieu de n.
Exercice 7
La famille de primitives exec permet de créer un processus pour exécuter un programme déterminé.
On utilise en particulier execvp pour exécuter un programme en lui passant un tableau d'arguments.
Le paramètre filename pointe vers le nom (absolu ou relatif) du fichier exécutable, argv vers le
tableau contenant les arguments (terminé par NULL) qui sera passé à la fonction main du
programme lancé. Par convention, le paramètre argv[0] contient le nom du fichier exécutable, les
arguments suivants étant les paramètres successifs de la commande.
#include <unistd.h>
int execvp(char *filename, char *argv[]);

Noter que les primitives exec provoquent le recouvrement de la mémoire du processus appelant par
le nouveau fichier exécutable. Il n'y a donc pas normalement de retour (sauf en cas d'erreur – fichier
inconnu ou permission - auquel cas la primitive renvoie -1).

1) Que fait le programme suivant ?


#include<stdio.h>
#include<unistd.h>
#define MAX 5
int main() {
char *argv[MAX];
argv[0] = "ls"; argv[1] = "-lR"; argv[2] = "/"; argv[3] = NULL;
execvp("ls", argv);}

2) Ecrire un programme C nommé doit qui exécute une commande Unix qu'on lui passe en
paramètre. Exemple: doit ls –lt /

Solution
1) Il exécute la commande shell " ls -lR / " (ls récursif, avec affichage long)
2)
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc,char *argv[]) {
if (argc < 2) {
printf("Necessite au moins un argument\n");
exit(1);
}
execvp(argv[1],argv+1);
perror("execvp a échoué\n");
return 1;
}
Exercice 8
1) Ecrire un programme C équivalent à la commande shell suivante : who & ps & ls -l
2) Ecrire un programme C équivalent à la commande shell suivante : who ; ps ; ls –l

Solution
1) Les commandes séparées par & s'exécutent simultanément.
#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){
execlp("who", "who", NULL);
perror("execlp");
exit(1);
}
if ((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){
execlp("ps", "ps", NULL);
perror("execlp");
exit(1);
}
execlp("ls", "ls", "-l", NULL);
perror("execlp");
exit(1);
}

2) Les commandes séparées par ; s'exécutent successivement.

#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){
execlp("who", "who", NULL);
perror("execlp");
exit(1);
}
wait(NULL);
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if (pid == 0){
execlp("ps", "ps", NULL);
perror("execlp");
exit(1);
}
wait(NULL);
execlp("ls", "ls", "-l", NULL);
perror("execlp");
exit(1);
}

Vous aimerez peut-être aussi