2026-06-18 19:03:42 +02:00
# TP : Simulateur d'Ordonnanceur - Gestion des Processus
2026-01-17 23:10:49 +01:00
> **Thème** : Processus, états, ordonnancement, interblocage
---
## Contexte
Votre système d'exploitation préféré gère en permanence des dizaines de processus : navigateur web, lecteur de musique, mises à jour en arrière-plan... Comment fait-il pour donner l'illusion que tout s'exécute en même temps ?
Dans ce TP, vous allez créer un **simulateur d'ordonnanceur** qui gère une file de processus et les exécute tour à tour, comme le fait un vrai système d'exploitation.
---
## Partie 1 : La classe Processus
### Exercice 1 : Modéliser un processus
Créez une classe `Processus` représentant un processus avec ses caractéristiques.
**Attributs :**
- `pid` : identifiant unique du processus (int)
- `nom` : nom du processus (str)
- `duree_totale` : durée d'exécution totale nécessaire (int, en unités de temps)
- `duree_restante` : durée restant à exécuter (int)
2026-06-18 19:03:42 +02:00
- `etat` : état actuel du processus (str) - "nouveau", "pret", "elu", "bloque", "termine"
2026-01-17 23:10:49 +01:00
- `priorite` : niveau de priorité (int, optionnel, par défaut 0)
**Méthodes :**
- `__init__` : constructeur
- `executer(quantum)` : exécute le processus pendant `quantum` unités de temps
- `est_termine()` : renvoie True si le processus est terminé
- `__repr__` : affichage du processus
```python
class Processus :
# Compteur de PID (variable de classe)
compteur_pid = 0
def __init__ ( self , nom , duree_totale , priorite = 0 ):
"""
Constructeur de la classe Processus.
:param nom: (str) Nom du processus
:param duree_totale: (int) Durée totale d'exécution
:param priorite: (int) Priorité (0 = normale)
"""
Processus . compteur_pid += 1
self . pid = Processus . compteur_pid
# À compléter
pass
def executer ( self , quantum ):
"""
Exécute le processus pendant quantum unités de temps.
Met à jour la durée restante.
:param quantum: (int) Temps d'exécution alloué
:return: (int) Temps réellement utilisé
"""
# À compléter
pass
def est_termine ( self ):
"""
Vérifie si le processus est terminé.
:return: (bool) True si terminé
"""
# À compléter
pass
def __repr__ ( self ):
"""Affichage du processus."""
return f "[PID { self . pid } ] { self . nom } ( { self . etat } ) - { self . duree_restante } / { self . duree_totale } "
```
**Tests :**
```python
>>> p = Processus ( "Firefox" , 10 )
>>> print ( p )
[ PID 1 ] Firefox ( nouveau ) - 10 / 10
>>> p . etat = "elu"
>>> p . executer ( 3 )
3
>>> print ( p )
[ PID 1 ] Firefox ( elu ) - 7 / 10
>>> p . est_termine ()
False
```
---
## Partie 2 : L'ordonnanceur FIFO
### Exercice 2 : Ordonnanceur simple (FIFO)
L'ordonnanceur **FIFO** (First In, First Out) exécute les processus dans l'ordre d'arrivée, chacun jusqu'à la fin.
```python
class OrdonnanceurFIFO :
def __init__ ( self ):
"""Initialise l'ordonnanceur avec une file vide."""
self . file_prets = [] # File des processus prêts
self . historique = [] # Historique d'exécution
self . temps = 0 # Temps courant
def ajouter_processus ( self , processus ):
"""
Ajoute un processus à la file des prêts.
:param processus: (Processus) Processus à ajouter
"""
processus . etat = "pret"
self . file_prets . append ( processus )
print ( f "[T= { self . temps } ] { processus . nom } ajouté à la file" )
def executer_suivant ( self ):
"""
Exécute le prochain processus de la file jusqu'à la fin.
"""
if len ( self . file_prets ) == 0 :
print ( "File vide, rien à exécuter" )
return
# Récupérer le premier processus
processus = self . file_prets . pop ( 0 )
processus . etat = "elu"
print ( f " \n [T= { self . temps } ] { processus . nom } commence son exécution" )
# Exécuter jusqu'à la fin
while not processus . est_termine ():
temps_utilise = processus . executer ( 1 )
self . temps += temps_utilise
self . historique . append (( self . temps , processus . pid , processus . nom ))
processus . etat = "termine"
print ( f "[T= { self . temps } ] { processus . nom } terminé" )
def executer_tous ( self ):
"""Exécute tous les processus de la file."""
print ( " \n " + "=" * 50 )
print ( "ORDONNANCEUR FIFO - Début de l'exécution" )
print ( "=" * 50 )
while len ( self . file_prets ) > 0 :
self . executer_suivant ()
print ( " \n " + "=" * 50 )
print ( f "Exécution terminée - Temps total : { self . temps } " )
print ( "=" * 50 )
def afficher_historique ( self ):
"""Affiche l'historique d'exécution."""
# À compléter
pass
```
**Tests :**
```python
>>> ordonnanceur = OrdonnanceurFIFO ()
>>> ordonnanceur . ajouter_processus ( Processus ( "Firefox" , 5 ))
>>> ordonnanceur . ajouter_processus ( Processus ( "VSCode" , 3 ))
>>> ordonnanceur . ajouter_processus ( Processus ( "Spotify" , 2 ))
>>> ordonnanceur . executer_tous ()
```
---
## Partie 3 : L'ordonnanceur Round Robin
### Exercice 3 : Ordonnanceur à temps partagé
L'ordonnanceur **Round Robin** (tourniquet) alloue un **quantum** de temps à chaque processus, puis passe au suivant. Si le processus n'est pas terminé, il retourne en fin de file.
```python
class OrdonnanceurRoundRobin :
def __init__ ( self , quantum = 2 ):
"""
Initialise l'ordonnanceur Round Robin.
:param quantum: (int) Temps alloué à chaque processus
"""
self . file_prets = []
self . quantum = quantum
self . temps = 0
self . historique = []
def ajouter_processus ( self , processus ):
"""Ajoute un processus à la file."""
processus . etat = "pret"
self . file_prets . append ( processus )
def executer_cycle ( self ):
"""
Exécute un cycle : le premier processus pendant quantum unités.
S'il n'est pas terminé, il retourne en fin de file.
:return: (bool) True si un processus a été exécuté
"""
if len ( self . file_prets ) == 0 :
return False
# Récupérer le premier processus
processus = self . file_prets . pop ( 0 )
processus . etat = "elu"
# Exécuter pendant le quantum
temps_utilise = processus . executer ( self . quantum )
self . temps += temps_utilise
# Enregistrer dans l'historique
for _ in range ( temps_utilise ):
self . historique . append ( processus . nom [ 0 ]) # Première lettre
if processus . est_termine ():
processus . etat = "termine"
print ( f "[T= { self . temps } ] { processus . nom } TERMINÉ" )
else :
# Remettre en fin de file
processus . etat = "pret"
self . file_prets . append ( processus )
print ( f "[T= { self . temps } ] { processus . nom } interrompu, retour en file ( { processus . duree_restante } restant)" )
return True
def executer_tous ( self ):
"""Exécute tous les processus jusqu'à la fin."""
print ( f " \n { '=' * 50 } " )
print ( f "ORDONNANCEUR ROUND ROBIN (quantum= { self . quantum } )" )
print ( f " { '=' * 50 } \n " )
while self . executer_cycle ():
pass
print ( f " \n { '=' * 50 } " )
print ( f "Temps total : { self . temps } " )
print ( f "Historique : { '' . join ( self . historique ) } " )
print ( f " { '=' * 50 } " )
```
**Tests :**
```python
>>> Processus . compteur_pid = 0 # Reset des PID
>>> rr = OrdonnanceurRoundRobin ( quantum = 2 )
>>> rr . ajouter_processus ( Processus ( "Firefox" , 6 ))
>>> rr . ajouter_processus ( Processus ( "VSCode" , 4 ))
>>> rr . ajouter_processus ( Processus ( "Spotify" , 2 ))
>>> rr . executer_tous ()
```
**Sortie attendue :**
```
==================================================
ORDONNANCEUR ROUND ROBIN (quantum=2)
==================================================
[T=2] Firefox interrompu, retour en file (4 restant)
[T=4] VSCode interrompu, retour en file (2 restant)
[T=6] Spotify TERMINÉ
[T=8] Firefox interrompu, retour en file (2 restant)
[T=10] VSCode TERMINÉ
[T=12] Firefox TERMINÉ
==================================================
Temps total : 12
Historique : FFVVSSFFVVFF
==================================================
```
---
## Partie 4 : Simulation d'interblocage
### Exercice 4 : Processus avec ressources
Modélisons des processus qui ont besoin de ressources partagées.
```python
class Ressource :
def __init__ ( self , nom ):
"""
:param nom: (str) Nom de la ressource
"""
self . nom = nom
self . proprietaire = None # PID du processus qui détient la ressource
def est_disponible ( self ):
"""Renvoie True si la ressource est libre."""
return self . proprietaire is None
def acquerir ( self , pid ):
"""
Tente d'acquérir la ressource.
:param pid: (int) PID du processus demandeur
:return: (bool) True si acquisition réussie
"""
if self . est_disponible ():
self . proprietaire = pid
return True
return False
def liberer ( self ):
"""Libère la ressource."""
self . proprietaire = None
def __repr__ ( self ):
if self . est_disponible ():
return f "Ressource { self . nom } : LIBRE"
else :
return f "Ressource { self . nom } : occupée par PID { self . proprietaire } "
```
### Exercice 5 : Détection d'interblocage
Créez une fonction qui détecte un interblocage potentiel.
```python
class ProcessusAvecRessources ( Processus ):
def __init__ ( self , nom , duree_totale , ressources_requises ):
"""
:param ressources_requises: (list) Liste des noms de ressources nécessaires
"""
super () . __init__ ( nom , duree_totale )
self . ressources_requises = ressources_requises
self . ressources_obtenues = []
def demander_ressources ( self , ressources_dispo ):
"""
Tente d'obtenir toutes les ressources nécessaires.
:param ressources_dispo: (dict) {nom: Ressource}
:return: (bool) True si toutes les ressources obtenues
"""
for nom_res in self . ressources_requises :
if nom_res not in self . ressources_obtenues :
ressource = ressources_dispo . get ( nom_res )
if ressource and ressource . acquerir ( self . pid ):
self . ressources_obtenues . append ( nom_res )
print ( f " { self . nom } obtient { nom_res } " )
else :
print ( f " { self . nom } BLOQUÉ sur { nom_res } " )
self . etat = "bloque"
return False
return True
def liberer_ressources ( self , ressources_dispo ):
"""Libère toutes les ressources détenues."""
for nom_res in self . ressources_obtenues :
ressources_dispo [ nom_res ] . liberer ()
print ( f " { self . nom } libère { nom_res } " )
self . ressources_obtenues = []
def detecter_interblocage ( processus_list , ressources ):
"""
Détecte si un interblocage existe.
:param processus_list: (list) Liste des processus
:param ressources: (dict) Dictionnaire des ressources
:return: (bool) True si interblocage détecté
"""
# Un interblocage existe si tous les processus non terminés sont bloqués
processus_actifs = [ p for p in processus_list if p . etat != "termine" ]
if len ( processus_actifs ) == 0 :
return False
tous_bloques = all ( p . etat == "bloque" for p in processus_actifs )
if tous_bloques :
print ( " \n *** INTERBLOCAGE DÉTECTÉ ! ***" )
print ( "Processus bloqués :" )
for p in processus_actifs :
print ( f " - { p . nom } : possède { p . ressources_obtenues } , attend { [ r for r in p . ressources_requises if r not in p . ressources_obtenues ] } " )
return True
return False
```
**Exemple de simulation d'interblocage :**
```python
# Ressources
ressources = {
"A" : Ressource ( "A" ),
"B" : Ressource ( "B" )
}
# Processus (scénario classique d'interblocage)
p1 = ProcessusAvecRessources ( "Programme1" , 5 , [ "A" , "B" ])
p2 = ProcessusAvecRessources ( "Programme2" , 5 , [ "B" , "A" ])
# Simulation manuelle
print ( "=== Simulation d'interblocage === \n " )
print ( "Tour 1 : P1 demande A" )
p1 . demander_ressources ( ressources )
print ( " \n Tour 2 : P2 demande B" )
p2 . demander_ressources ( ressources )
print ( " \n Tour 3 : P1 demande B (déjà pris par P2)" )
p1 . demander_ressources ( ressources )
print ( " \n Tour 4 : P2 demande A (déjà pris par P1)" )
p2 . demander_ressources ( ressources )
# Détection
detecter_interblocage ([ p1 , p2 ], ressources )
```
---
## Partie 5 : Statistiques et comparaison
### Exercice 6 : Calcul des métriques
Ajoutez des méthodes pour calculer les statistiques d'ordonnancement :
```python
def calculer_statistiques ( self ):
"""
Calcule les statistiques d'ordonnancement.
:return: (dict) Statistiques
"""
# Temps de réponse moyen
# Temps d'attente moyen
# Temps de rotation moyen
# À compléter
pass
```
**Métriques importantes :**
- **Temps de rotation** (turnaround) : temps total entre l'arrivée et la fin
- **Temps d'attente** : temps passé en file d'attente
- **Temps de réponse** : temps avant la première exécution
### Exercice 7 : Comparaison FIFO vs Round Robin
Créez un programme qui compare les deux algorithmes sur le même jeu de processus.
```python
def comparer_ordonnanceurs ():
"""Compare FIFO et Round Robin."""
processus_test = [
( "Navigateur" , 8 ),
( "Editeur" , 4 ),
( "Terminal" , 2 ),
( "Musique" , 6 )
]
print ( "=" * 60 )
print ( "COMPARAISON DES ORDONNANCEURS" )
print ( "=" * 60 )
# Test FIFO
Processus . compteur_pid = 0
fifo = OrdonnanceurFIFO ()
for nom , duree in processus_test :
fifo . ajouter_processus ( Processus ( nom , duree ))
fifo . executer_tous ()
# Test Round Robin
Processus . compteur_pid = 0
rr = OrdonnanceurRoundRobin ( quantum = 2 )
for nom , duree in processus_test :
rr . ajouter_processus ( Processus ( nom , duree ))
rr . executer_tous ()
# À compléter : afficher la comparaison des statistiques
```
---
## Résumé des algorithmes
| Algorithme | Principe | Avantages | Inconvénients |
|------------|----------|-----------|---------------|
| **FIFO** | Premier arrivé, premier servi | Simple | Temps d'attente long pour les petits processus |
| **Round Robin** | Quantum de temps pour chacun | Équitable, bon temps de réponse | Overhead des changements de contexte |
| **Priorité** | Processus prioritaire d'abord | Urgent traité en premier | Famine des processus peu prioritaires |
---
## Pour aller plus loin
- **Ordonnanceur à priorité** : Implémenter un ordonnanceur qui choisit le processus de plus haute priorité
- **Ordonnanceur SJF** (Shortest Job First) : Le processus le plus court d'abord
- **Vieillissement** (Aging) : Augmenter la priorité des processus qui attendent longtemps
- **Interface graphique** : Visualiser l'exécution avec une barre de Gantt
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.