Table des matières
Exercice 1: Le pont en travaux
Pour cet exercice vous devez récupérer sur moodle le fichier: ``examq1.c``
On veut écrire un programme qui simule le passage de voitures sur un pont en travaux ne contenant qu’1 seule voie de circulation. De plus, pour éviter une surcharge de poids, 1 seule voiture à la fois doit passer sur ce pont.
Question 1
On vous donne le programme examq1.c qui génère n fils, n étant passé en paramètre de commande du programme. Compiler le programme examq1.c puis exécutez: ./examq1 4
Combien de processus (au total) sont créés par ce programme? Pourquoi? Justifiez votre réponse que vous écrirez en commentaires tout au début de votre fichier source examq1.c puis modifiez le programme pour le rendre correct.
Question 2
Dans cette question on part du principe que toutes les voitures circulent dans le même sens. Recopier votre programme précédent dans le fichier de nom ``examq2.c`` et ajouter un objet IPC pour permettre la synchronisation des voitures sur le pont car il ne peut y avoir au même moment qu’1 seule voiture qui passe le pont. Le passage sur le pont sera simulé par une attente de 1 seconde. Vous vous inspirerez de la trace d’exécution suivante pour mettre des affichages opportuns!
Exemple d’exécution:
>$ ./examq2 4 # 4 correspond au nombre de voitures qui vont passer le pont voiture 1 (1358) : je veux passer sur le pont voiture 2 (1359) : je veux passer sur le pont voiture 1 (1358) passe le pont voiture 3 (1360) : je veux passer sur le pont voiture 4 (1361) : je veux passer sur le pont voiture 1 (1358): j'ai fini de passer le pont ! voiture 2 (1359) passe le pont voiture 2 (1359): j'ai fini de passer le pont ! voiture 3 (1360) passe le pont voiture 3 (1360): j'ai fini de passer le pont ! voiture 4 (1361) passe le pont voiture 4 (1361): j'ai fini de passer le pont !
Question 3
Recopiez votre programme précédent examq2.c dans un autre fichier de nom ``examq3.c``
Ce pont doit pouvoir maintenant être utilisé par des voitures circulant dans les 2 sens. Pour simplifier on considèrera que le nombre de voitures circulant dans le sens A vers B sur le pont est identique au nombre de voitures circulant dans le sens contraire B vers A. On passera en paramètre du programme le nombre de voitures circulant dans le sens A vers B (qui est aussi égal à celui des voitures circulant dans le sens B vers A). De plus, on supposera que c’est forcément une voiture qui circule dans le sens A vers B qui passera le pont en premier, et qu’ensuite il y aura une alternance du sens de circulation, donc ce sera une voiture qui circule dans le sens B vers A qui passera le pont, puis une voiture dans le sens A vers B, etc... jusqu’à ce que toutes les voitures aient franchi le pont. On ne prendra pas en compte l’ordre d’arrivée des voitures. Rajouter un 2ème sémaphore à votre programme précédent pour assurer cette alternance.
Ecrivez le programme examq3.c afin qu’il réponde aux exigences précédentes.
Exemple d’exécution :
>$ ./examq3 3 # 3 correspond au nombre de voitures qui vont passer dans chaque sens NbVoit dans chaque sens =3 voiture 1 (1032) : je veux passer sur le pont de A vers B voiture 2 (1033) : je veux passer sur le pont de A vers B voiture 2 (1033) passe le pont de A vers B voiture 3 (1034) : je veux passer sur le pont de A vers B voiture 4 (1035) : je veux passer sur le pont de B vers A voiture 5 (1036) : je veux passer sur le pont de B vers A voiture 6 (1037) : je veux passer sur le pont de B vers A voiture 2 (1033): j'ai fini de passer le pont ! voiture 4 (1035) passe le pont de B vers A voiture 4 (1035): j'ai fini de passer le pont ! voiture 1 (1032) passe le pont de A vers B voiture 1 (1032): j'ai fini de passer le pont ! voiture 5 (1036) passe le pont de B vers A voiture 5 (1036): j'ai fini de passer le pont ! voiture 3 (1034) passe le pont de A vers B voiture 3 (1034): j'ai fini de passer le pont ! voiture 6 (1037) passe le pont de B vers A voiture 6 (1037): j'ai fini de passer le pont !
Exercice 2: Ordonnancement "simple"
L’objectif de cet exercice est de réaliser une sorte de simulation d’ordonnancement de processus avec un processus ordonnanceur, un processus créateur et des processus créés.
L’ordonnancement final sera une variante d’un tourniquet multiniveaux à priorité flottante. La progression se fera étape par étape et de manière incrémentale. Même si vous ne pouvez pas répondre à une question, indiquer le code qu’il faudrait ajouter pour la question courante en supposant le reste fonctionnel.
Question 1 - Pour s’échauffer
Dans cette première question nous allons simplement mettre en place la “runqueue” contenant la liste des processus prêts à être exécutés. Ceci va également mettre en place le protocole de communication entre le processus utilisateur et l’ordonnanceur.
Le principe est la suivant:
- Le processus père joue le rôle “d’ordonnanceur”,
- Le processus fils va jouer le rôle “d’utilisateur” (il n’y a qu’un seul processus utilisateur (fils)),
- La runqueue est modélisée par un tube non-nommé entre le père et le fils,
- Le processus utilisateur va créer des processus consommateurs (petit-fils),
- Le nombre de processus “consommateurs” à créer est donné en paramètre du programme.
Le travail du processus ordonnanceur est le suivant:
- Attend 1 seconde,
- Pour chaque pid dans la runqueue (tube) afficher ce pid,
- Se terminer quand le processus utilisateur (fils) est fini.
Le travail du processus utilisateur (fils) est le suivant:
- Créer N processus consommateurs (petit-fils),
- Mettre les pids de ces processus dans la runqueue (tube),
- Attendre la fin de tous ses fils.
Le travail de chaque consommateur (petit-fils) sera une simple pause d’une seconde puis se termine.
Ecrivez le programme ``tourn1.c`` répondant à cette question.
Chacun des processus devra donner des affichages comme dans la trace d’exécution suivante:
>$ ./tourn1 2 Fils utilisateur de pid 12997: creation de 2 fils Petit-Fils 0 de pid 12998 cree Petit-Fils 1 de pid 12999 cree Petit-Fils 0 de pid 12998: Fin Pere: processus de pid 12998 pris dans la runqueue Pere: processus de pid 12999 pris dans la runqueue Petit-Fils 1 de pid 12999: Fin Fils utilisateur de pid 12997: Fin Pere: fin, suite a fin du fils utilisateur 12997
Question 2 - Tourniquet à un niveau
Dans cette question nous allons introduire l’ordonnancement qui n’était pas présent dans la question précédente. Pour effectuer la synchronisation nous allons nous appuyer sur l’envoi de signaux entre le processus ordonnanceur (père) et les processus consommateurs (petit-fils) qui se trouvent dans la runqueue.
Le travail du processus ordonnanceur est le suivant:
- Attend 1 seconde,
- Pour chaque pid dans la runqueue (tube) afficher ce pid,
- Envoie le signal SIGUSR1 au processus pris dans la runqueue pour le réveiller,
- S’endort pour 2 secondes (équivalent d’un quantum de 2 s),
- Envoie le signal SIGUSR2 au même processus pour le suspendre
- Se terminer quand le processus utilisateur (fils) est fini.
Le travail du processus utilisateur est le même que précédemment.
Le travail de chaque processus consommateur (petit-fils) sera:
- Se mettre en pause,
- Se réveiller après avoir reçu un signal SIGUSR1,
- Se mettre en boucle infinie
- Se mettre en pause après avoir reçu un signal SIGUSR2,
- Se terminer une fois qu’il aura été réveillé 3 fois (défini par une constante dans votre programme).
Copier le fichier précédent en un nouveau fichier nommé ``tourn2.c``, et modifier celui-ci afin d’implanter cette amélioration.
Indications: vous pouvez utiliser des variables globales; pensez à man 2 kill; c’est le processus père qui remet en file.
Chacun des processus devra donner des affichages comme dans la trace d’exécution suivante:
>$ ./tourn2 2 Petit-Fils 0 de pid 13930 cree Petit-Fils 1 de pid 13931 cree Pere: processus de pid 13930 pris dans la runqueue Petit-Fils de pid 13930: réveil numero 1 Pere: processus de pid 13930 remis dans la runqueue Petit-Fils de pid 13930: stop Pere: processus de pid 13931 pris dans la runqueue Petit-Fils de pid 13931: réveil numero 1 Pere: processus de pid 13931 remis dans la runqueue Petit-Fils de pid 13931: stop Pere: processus de pid 13930 pris dans la runqueue Petit-Fils de pid 13930: réveil numero 2 Pere: processus de pid 13930 remis dans la runqueue Petit-Fils de pid 13930: stop Pere: processus de pid 13931 pris dans la runqueue Petit-Fils de pid 13931: réveil numero 2 Pere: processus de pid 13931 remis dans la runqueue Petit-Fils de pid 13931: stop Pere: processus de pid 13930 pris dans la runqueue Petit-Fils de pid 13930: réveil numero 3 Petit-Fils 0 de pid 13930: fin Pere: processus de pid 13930 disparu Pere: processus de pid 13931 pris dans la runqueue Petit-Fils de pid 13931: réveil numero 3 Petit-Fils 1 de pid 13931: fin Fils utilisateur de pid 13929: Fin Pere: processus de pid 13931 disparu Pere: fin, suite a fin du fils createur 13929
Question 3 - On commence à s’amuser: tourniquet multiniveaux
Dans cette question, nous allons simuler un ordonnancement multiniveaux à l’aide de plusieurs runqueue, une par niveau de priorité. On supposera 3 niveaux pour commencer mais ce paramètre N devra être défini par une constante dans le programme. Pour simplifier, nous supposerons que les priorités sont fixes, c’est-à-dire qu’un processus dans une file restera toujours dans la même file.
Afin de savoir si une file est vide ou pas, une variable supplémentaire est introduite afin d’indiquer la taille de chaque file. Le test se fera donc à partir d’un tableau de taille N indiquant le nombre de processus dans la file. Ce tableau étant partagé entre le processus fils utilisateur et le processus père ordonnanceur, vous utiliserez une zone de mémoire partagée pour ce tableau.
La priorité des processus consommateurs créé par l’utilisateur est tirée au hasard entre 0 et N-1, N étant le nombre de priorité et 0 étant la plus prioritaire.
Le travail du processus ordonnanceur est le suivant:
- Attend 1 seconde,
- Pour chaque pid dans les runqueues, par ordre de priorité,
afficher ce pid,
- Envoie le signal SIGUSR1 au processus pris dans la runqueue pour le réveiller,
- S’endort pour 2 secondes (équivalent d’un quantum de 2 s),
- Envoie le signal SIGUSR2 au même processus pour le suspendre
- Se terminer quand le processus utilisateur (fils) est fini.
Le travail du processus utilisateur est le suivant:
- Créer N processus fils,
- Tirer au hasard leur priorité
- Mettre les pids de ces processus dans la runqueue correspondant à leur priorité,
- Attendre la fin de tous ses fils.
Le travail de chaque fils consommateur sera le même que précédemment.
Pour rappel, on ne traite une file que si les files plus prioritaires sont vides.
Copier le fichier précédent en un nouveau fichier nommé ``tourn3.c``, et modifier le pour prendre en compte cette nouvelle version multiniveaux.
Chacun des processus devra donner des affichages comme dans la trace d’exécution suivante:
>$ ./tourn3 2 Fils utilisateur de pid 14985: creation de 2 fils Fils utilisateur: creation du fils 14986 de priorite 1 Petit-Fils 0 de pid 14986 cree Petit-Fils 1 de pid 14987 cree Pere: processus de pid 14986 pris dans la runqueue 1 Petit-Fils de pid 14986: réveil numero 1 Fils utilisateur: creation du fils 14987 de priorite 0 Pere: processus de pid 14986 remis dans la runqueue 1 Petit-Fils de pid 14986: stop Pere: processus de pid 14987 pris dans la runqueue 0 Petit-Fils de pid 14987: réveil numero 1 Pere: processus de pid 14987 remis dans la runqueue 0 Petit-Fils de pid 14987: stop Pere: processus de pid 14987 pris dans la runqueue 0 Petit-Fils de pid 14987: réveil numero 2 Pere: processus de pid 14987 remis dans la runqueue 0 Petit-Fils de pid 14987: stop Pere: processus de pid 14987 pris dans la runqueue 0 Petit-Fils de pid 14987: réveil numero 3 Petit-Fils 1 de pid 14987 fin Pere: processus de pid 14987 disparu Pere: processus de pid 14986 pris dans la runqueue 1 Petit-Fils de pid 14986: réveil numero 2 Pere: processus de pid 14986 remis dans la runqueue 1 Petit-Fils de pid 14986: stop Pere: processus de pid 14986 pris dans la runqueue 1 Petit-Fils de pid 14986: réveil numero 3 Petit-Fils 0 de pid 14986 fin Fils utilisateur de pid 14985: Fin Pere: processus de pid 14986 disparu Pere: fin, suite a fin du fils createur 14985
Question 4 - On s’amuse pour de bon: tourniquet multiniveaux à priorité flottante
Dans cette question, l’ordonnanceur a le droit de baisser la priorité de processus ayant trop consommé de CPU (uniquement baisser la priorité).
Si après avoir obtenu le CPU un processus consommateur l’a déjà obtenu 4 fois, alors dans ce cas, sa priorité diminue de 1 et donc il change de file de runqueue. Le nombre 4 sera défini par une constante dans votre programme. Lorsqu’un processus change de file, son nombre de tick consommé est remis à zéro.
Dans cette question, un processus consommateur se terminera après 10 réveils et le processus ordonnanceur (père) n’attendra qu’une seconde avant de suspendre le processus consommateur.
Le travail du processus ordonnanceur est le suivant:
- Attend 2 seconde,
- Pour chaque pid dans les runqueues, par ordre de priorité,
afficher ce pid,
- Envoie le signal SIGUSR1 au processus pris dans la runqueue pour le réveiller,
- S’endort pour 1 secondes (équivalent d’un quantum de 1 s),
- Envoie le signal SIGUSR2 au même processus pour le suspendre
- Test sa consommation CPU (nombre de ticks)
- Si moins de 4 alors priorité identique
- Sinon, diminution de priorité et changement de file
- Se terminer quand le processus utilisateur (fils) est fini.
Le travail du processus utilisateur est le suivant:
- Créer N processus consommateurs (petit-fils),
- Tirer au hasard leur priorité,
- Attendre aléatoirement entre 0 et 3 secondes,
- Mettre les pids de ces processus dans la runqueue correspondant à leur priorité,
- Attendre la fin de tous ses fils.
Le travail de chaque consommateur (petits-fils) sera le même que précédemment sans oublier de prendre en compte le nombre ticks (un passage sur le CPU = 1 tick).
Copier le fichier précédent en un nouveau fichier nommé ``tourn4.c``, et modifier le pour prendre en compte cette nouvelle version multiniveaux à priorité flottante.
Chacun des processus devra donner des affichages comme dans la trace d’exécution suivante (les affichages ont volontairement été simplifiés pour plus de lisibilité: réveils, pause, remise en runqueue):
>$ ./tourn4 2 Fils utilisateur de pid 4036: creation de 2 fils Petit-Fils 0 de pid 4037 cree Fils utilisateur: creation du fils 4037 de priorite 2 Petit-Fils 1 de pid 4038 cree Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Fils utilisateur: creation du fils 4038 de priorite 1 Pere: processus de pid 4038 pris dans la runqueue 1 Pere: processus de pid 4038 pris dans la runqueue 1 Pere: processus de pid 4038 pris dans la runqueue 1 Pere: processus de pid 4038 pris dans la runqueue 1 Pere: diminution de priorite de 4038: 1 -> 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4038 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: diminution de priorite de 4037: 2 -> 2 Pere: processus de pid 4038 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4038 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4038 pris dans la runqueue 2 Pere: diminution de priorite de 4038: 2 -> 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4038 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Pere: diminution de priorite de 4037: 2 -> 2 Pere: processus de pid 4038 pris dans la runqueue 2 Petit-Fils 1 de pid 4038 fin Pere: process 4038 disparu Pere: processus de pid 4037 pris dans la runqueue 2 Pere: processus de pid 4037 pris dans la runqueue 2 Petit-Fils 0 de pid 4037 fin Fils utilisateur de pid 4036: Fin Pere: process 4037 disparu Pere: fin, suite a fin du fils createur 4036