Ordonnanceur
L'objectif de ce TP est d'établir de manière automatique des chronogrammes, étant donné une liste de processus et un algorithme d'ordonnancement. Ceci nous permettra de calculer ensuite des mesures statistiques des différents algorithmes d'ordonnancement afin de pouvoir les comparer entre eux.
Représentation des processus¶
Les processus sont représentés à l'aide d'instances de la classe Processus. Ils sont munis de quatre attributs :
- leur
pid(pour process identifier, c'est un entier unique au processus en question) ; - leur
dureetotale d'exécution nécessaire (exprimée en nombres de cycles du processeur) ; - leur instant d'
arrivee(exprimé en nombre de cycles du processeur après l'instant initial \(t = 0\)) ; - leur
duree_restante(exprimée en nombre de cycles du processeur) qui représente le temps d'exécution nécessaire avant la fin du processus. Initialement, la durée restante d'exécution d'un processus est égale à sa durée totale d'exécution.
- Écrire une classe
Processuspermettant d'instancier des variables munies de ces attributs. -
Instancier des variables
p1,p2, etp3, représentant les processus dont on donne la description dans le tableau ci-dessous :Processus Durée d'exécution Instant d'arrivée P110 0 P24 3 P38 2
class Processus:bksl-nl """Représente un processus"""bksl-nl def py-undpy-undinitpy-undpy-und(self, pid, arrivee, duree):bksl-nl """Processus, int, int, int -> None"""bksl-nl passbksl-nlbksl-nl def py-undpy-undreprpy-undpy-und(self):bksl-nl """ Processus -> strbksl-nl Renvoie une chaine de caractère représentant le processus self """bksl-nl passbksl-nl bksl-nl def avancer(self, d, t):bksl-nl """ Processus, int, int -> Nonebksl-nl Le processus (re)prend son exécution à la date t pour une durée d """bksl-nl passbksl-nl bksl-nl def estpy-undtermine(self):bksl-nl """ Processus -> boolbksl-nl Détermine si le processus p a terminé son exécution """bksl-nl passbksl-nl bksl-nl def py-undpy-undinitpy-undpy-und(self, pid, arrivee, duree):bksl-nl """ Initialise un processus """bksl-nl passbksl-nl bksl-nl def avancer(self, d, t):bksl-nl """ Processus, int, int -> Nonebksl-nl Avance le processus """bksl-nl passbksl-nl bksl-nl def sejour(self):bksl-nl """ Renvoie le temps de séjour de self """bksl-nl passbksl-nl bksl-nl def execution(self):bksl-nl """ Renvoie le temps d'exécution de self """bksl-nl passbksl-nl bksl-nl def attente(self):bksl-nl """ Renvoie le temps d'attente de self """bksl-nl passbksl-nl bksl-nl def latence(self):bksl-nl """ Renvoie le temps de latence de self """bksl-nl passbksl-nlbksl-nl
Représentation d'un processus¶
Écrire une méthode __repr__ de la classe Processus qui affiche une chaine de caractère représentant un processus à l'aide de son pid selon le schéma de l'énoncé.
1 2 3 4 | |
Chronogramme¶
On représente un chronogramme à l'aide d'une instance de la classe Chronogramme. Une telle instance permet d'obtenir le temps écoulé t (exprimé en nombre de cycles du processeur) depuis le démarrage du premier processus et est munie d'un attribut table de type list qui stocke la suite des processus ayant été exécutés par le processeur à chacun de ses cycles jusqu'à l'instant t. On remarquera que t est donc égal au nombre d'éléments de table.
Par exemple, si p1, p2, et p3 sont trois objets de type Processus et table est la liste [p1, p1, p2, p3], cela veut dire que le processus p1 a été exécuté pendant deux cycles du processeur, puis que p2 et p3 ont tous les deux été exécutés pendant un cycle. Ainsi, 4 cycles du processeur se sont écoulés depuis l'instant initial.
Écrire une classe Chronogramme permettant d'instancier une variable munie d'un unique attribut table.
class Chronogramme:bksl-nl """Représente un chronogramme"""bksl-nl def py-undpy-undinitpy-undpy-und(self):bksl-nl """Chronogramme -> None"""bksl-nl passbksl-nlbksl-nl def tempspy-undecoule(self):bksl-nl """ Chronogramme -> intbksl-nl Renvoie le nombre de cycles écoulés depuis l'instant initial """bksl-nl passbksl-nl bksl-nl def enregistrer(self, p, q):bksl-nl """ int, int -> Nonebksl-nl Enregistre l'information : le processus p est exécuté pendant q cycles """bksl-nl passbksl-nl bksl-nl def py-undpy-undreprpy-undpy-und(self):bksl-nl """ self -> strbksl-nl Renvoie une chaine de caractère représentant le chronogramme self """bksl-nl passbksl-nlbksl-nl
Méthode temps_ecoule¶
Écrire une méthode temps_ecoule de la classe Chronogramme qui renvoie le nombre de cycles écoulés depuis l'instant initial.
1 2 3 4 | |
Ordonnanceur FIFO¶
On s'intéresse dans cette partie à un ordonnanceur de type FIFO préemptif : celui-ci suit la logique du premier arrivé, premier servi. Ainsi, les différents processus sont exécutés suivant leur ordre d'arrivée. L'ordonnanceur interrompt les processus à intervalle régulier, définit par un nombre de cycles appelé quantum. Si le dernier processus exécuté n'est pas terminé, alors son exécution reprend là où elle s'était arrêtée. On modélise un tel ordonnanceur à l'aide d'une classe OrdonnanceurFIFO.
Celui-ci sera muni des attributs :
chrono: un chronogramme qui sera mis à jour à chaque cycle du processeur. Cet attribut sera initialisé avec un chronogramme vide.file_processus: une file contenant les processus à exécuter. Cet attribut sera initialisé par un objet de typeFile, vide.quantum: le nombre de cycles du processeur pendant lesquels les processus s'exécutent avant d'être interrompus par le système. À chaque interruption, l'ordonnanceur sélectionne un processus pour exécution. Cet attribut sera initialisé avec une valeur par défaut de1, qui pourra être modifiée en passant au constructeur de la classe un paramètre optionnel.
On utilisera le code suivant pour représenter et manipuler les objets de type File.
class Maillon:bksl-nl def py-undpy-undinitpy-undpy-und(self, v, n):bksl-nl self.valeur = vbksl-nl self.suivant = nbksl-nlbksl-nlclass File:bksl-nl """Une file d'entiers"""bksl-nl def py-undpy-undinitpy-undpy-und(self):bksl-nl """File -> Nonetype"""bksl-nl self.debut = Nonebksl-nl self.fin = Nonebksl-nlbksl-nl def estpy-undvide(self):bksl-nl """ File -> boolbksl-nl Détermine si la file self est vide """bksl-nl return self.debut is None and self.fin is Nonebksl-nl bksl-nl def enfiler(self, v):bksl-nl """ File, int -> Nonetypebksl-nl Ajoute l'élément v à la file self """bksl-nl if self.estpy-undvide():bksl-nl m = Maillon(v,None)bksl-nl self.debut = mbksl-nl self.fin = mbksl-nl else:bksl-nl nouveau = Maillon(v,None)bksl-nl maillonpy-undcourant = self.debutbksl-nl self.fin.suivant = nouveaubksl-nl self.fin = nouveaubksl-nl bksl-nl def defiler(self):bksl-nl """ File -> intbksl-nl Renvoie le premier élément de la file en le supprimant de celle-ci """bksl-nl if self.estpy-undvide():bksl-nl raise IndexError("la file est vide")bksl-nl valeur=self.debut.valeurbksl-nl self.debut=self.debut.suivantbksl-nl if self.debut is None:bksl-nl self.fin = Nonebksl-nl return valeurbksl-nlbksl-nl def examine(self):bksl-nl if self.estpy-undvide():bksl-nl raise IndexError("la file est vide")bksl-nl return self.debut.valeurbksl-nl bksl-nl bksl-nl def py-undpy-undstrpy-undpy-und(self):bksl-nl """ self -> strbksl-nl Construit la chaîne de caractères représentant la file self """bksl-nl s = ["[début]"]bksl-nl maillonpy-undcourant=self.debutbksl-nl while maillonpy-undcourant != None:bksl-nl # s=s+f' {maillonpy-undcourant.valeur}'bksl-nl s.append(f' {maillonpy-undcourant.valeur}')bksl-nl maillonpy-undcourant=maillonpy-undcourant.suivantbksl-nl s.append(" [fin]")bksl-nl return "".join(s)bksl-nlbksl-nl
Écrire une classe OrdonnaceurFIFO permettant d'instancier une variable munie de ces attributs. On importera pour cela la classe File depuis le module File sont munis d'une méthode examine renvoie l'élément présent au début de la file sans le supprimer de la file.
class OrdonnanceurFIFO:bksl-nl """Représente un ordonnanceur"""bksl-nl def py-undpy-undinitpy-undpy-und(self, q=1):bksl-nl """OrdonnanceurFIFO, int -> None"""bksl-nl passbksl-nlbksl-nl def ajoutepy-undprocessus(self, ppy-undlist):bksl-nl """ OrdonnanceurFIFO, [Processus] -> Nonebksl-nl Ajoute à self.filepy-undprocessus les processus de ppy-undlist par ordre d'arrivée """bksl-nl passbksl-nl bksl-nl def execpy-undquantum(self):bksl-nl """ OrdonnanceurFIFO -> Nonebksl-nl Simule l'exécution d'un quantum """bksl-nl passbksl-nl bksl-nl def exec(self):bksl-nl """ OrdonnanceurFIFO -> Chronogrammebksl-nl Simule l'exécution des processus de la file et en renvoie le chronogramme """bksl-nl passbksl-nlbksl-nl
Ajout des processus¶
L'ordonnanceur FIFO exécute les processus dans leur ordre d'arrivée. On souhaite écrire une méthode ajoute_processus de la classe OrdonnanceurFIFO qui prend en argument une liste de processus p_list et qui les ajoute à la file self.file_processus de l'ordonnanceur self par ordre croissant d'instant d'arrivée. Pour cela, tant que p_list n'est pas vide, on cherche dans p_list le processus dont l'attribut arrivee est le plus petit, on le supprime de p_list et on l'enfile dans la file des processus de l'ordonnanceur.
On procède en deux étapes, détaillées ci-après.
Sélection du premier processus arrivé¶
Écrire une fonction renvoie_et_supprime_premier qui étant donné une liste (supposée non vide) de processus p_list renvoie le processus ayant le plus petit temps d'arrivée parmi les processus de la liste, en le supprimant de celle-ci. On écrira donc un algorithme de recherche de la valeur du minimum ainsi que de son indice d'occurrence, puis et utiliser une instruction du type l.pop(i) (supprime de la liste l l'élément d'indice i en le renvoyant).
def renvoiepy-undetpy-undsupprimepy-undpremier(ppy-undlist):bksl-nl """ [Processus] -> Processusbksl-nl Renvoie et supprime de ppy-undlist le processus dont l'attribut arrivee est le plus petit """bksl-nl passbksl-nlbksl-nl
Ajout des processus à la file¶
Écrire une méthode ajoute_processus de la classe OrdonnanceurFIFO qui prend en argument une liste de processus p_list et qui les ajoute à la file self.file_processus par ordre croissant d'instant d'arrivée.
1 2 3 4 | |
Exécution d'un quantum¶
On souhaite écrire une méthode exec_quantum qui simule la sélection puis l'exécution sur le processeur d'un processus pendant self.quantum cycles. Dans le cas d'un ordonnanceur FIFO l'algorithme est très simple :
- récupérer le premier processus de la file des processus ;
- enregistrer dans le chronogramme l'information de l'exécution du processus ;
- avancer l'exécution du processus de
self.quantumunités de temps ; - si le processus est terminé à l'issue de cette étape, le retirer de la file des processus à exécuter.
Méthode enregistrer¶
Écrire une méthode enregistrer de la classe Chronogramme qui enregistre dans le chronogramme self l'information selon laquelle le processus p a été exécuté pendant q cycles du processeur. Ainsi, cette méthode ajoute q fois le processus p la fin du tableau self.table.
1 2 3 4 | |
Méthode avancer¶
Écrire une méthode avancer de la classe Processus qui diminue la durée d'exécution restante du processus self de d si l'instant t est supérieur à l'instant d'arrivée du processus.
1 2 3 4 | |
Méthode est_termine¶
Écrire une méthode est_termine de la classe Processus qui renvoie True si et seulement si le processus self a terminé son exécution. On effectuera pour cela un test utilisant l'attribut duree_restante du processus self.
1 2 3 4 | |
Méthode exec_quantum¶
Écrire une méthode exec_quantum qui implémente l'algorithme d'ordonnanceur FIFO décrit dans l'énoncé.
1 2 3 4 | |
Représentation d'un chronogramme et conclusion¶
Méthode __repr__¶
Écrire une méthode __repr__ de la classe Chronogramme qui affiche la chaine de caractère de votre choix qui permet de représenter un chronogramme. On pourra utiliser l'instruction str(p) où p est un processus pour obtenir une chaine de caractère représentant le processus p.
1 2 3 4 | |
Chronogramme complet¶
Écrire une méthode exec de la classe OrdonnanceurFIFO qui étant donné un ordonnanceur FIFO self simule son exécutition jusqu'à ce que la file des processus à exécuter soit vide. Cette méthode renverra le chronogramme de l'exécution.
Comparer avec les résultats vus en exercice.
1 2 3 4 | |
Étude des processus¶
On souhaite étudier "l'efficacité" d'un algorithme d'ordonnancement. Pour cela, on étudie le temps de séjour, le temps d'attente, et le temps de latence moyen des processus gérés. On rappelle les définitions suivantes :
- temps de termaison: instant \(t\) auquel le processus termine.
- temps d'exécution: c'est le temps pendant lequel le processus a utilisé le processeur.
- temps de séjour: c'est la différence entre le temps de terminaison et le temps d'arrivée du processus. C'est la durée pendant laquelle l'algorithme d'ordonnancement doit gérer le processus.
- temps d'attente: c'est la différence entre le temps d'exécution et le temps de séjour. C'est la durée pendant laquelle le processus a attendu un accès au processeur.
- temps de latence: c'est la différence entre le début de l'exécution du processus sur le processeur et l'arrivée du processus dans la file des processus en cours d'exécution. C'est la durée pendant lequel le processus "attend" son premier accès au processeur.
Modification des attributs des Processus¶
Ajouter à la classe Processus deux attributs initialisés avec la valeur -1 :
- l'attribut
debut: représente l'instanttde la première exécution du processus par le processeur. Dans ce contexte,-1signifie que le processus n'a pas encore accédé au processeur. - l'attribut
fin: représente l'instanttde la fin de la dernière exécution du processus par le processeur. Dans ce contexte,-1signifie que le processus n'est pas encore terminé.
1 2 3 | |
Modification de la méthode avance¶
Modifier la méthode avancer de la classe Processus : p.avancer(d, t) diminue la durée restante d'exécution du processus p et met à jour les attributs debut et fin de la manière suivante :
- s'il s'agit de la première exécution du processus, alors on sauvegarde l'instant
tde l'exécution du processus ; - si après diminution de la durée restante d'exécution le processus est terminé : on sauvegarde l'instant
d + tde fin du processus.
1 2 3 4 | |
Mesurer l'exécution d'un processus¶
Ajouter à la classe Processus les méthodes sejour, attente, latence qui renvoient respectivement les temps de séjour, d'attente et de latence du processus self, lorsque le processus a terminé son exécution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Statistiques d'un algorithme d'ordonnancement¶
-
Déduire des questions précédentes une méthode
statistiquesde la classeOrdonnanceurFIFOqui affiche sur la sortie standard les temps de séjour moyen, d'attente moyen et de latence moyen de l'ordonnancement généré par l'ordonnanceurselfpour les processus ayant été exécutés.Cette méthode sera toujours appelée après la méthode
exec. On pourra modifier la méthodeajoute_processusde telle sorte qu'en plus de remplir lafile_processuselle sauvegarde également la listeliste_processusdes processus exécutés (on pourra pour cela ajouter un attribut à la classeOrdonnanceurFIFO). On prendra garde aux effets de bords de la fonctionrenvoie_et_supprime_premier. -
-
Adapter le code de la classe
OrdonnanceurFIFOpour écrire une classeOrdonnanceurSJFreprésentant un ordonnanceur préemptif appliquant la politique du plus court travail d'abord (Shortest Job First) : à chaque intervalle de temps, l'ordonnanceur choisit dans la file d'attente le processus avec la durée d'exécution restante la plus courte. -
Comparer les statistiques d'un ordonnanceur FIFO avec celles d'un ordonnanceur SJF.
-