ajout de tous les cours et TP préparés cet été

This commit is contained in:
2026-01-17 23:10:49 +01:00
parent ed9415bc81
commit 301cf5a98f
125 changed files with 21614 additions and 542 deletions

View File

@@ -1,16 +1,95 @@
# TD Gestion des processus et des ressources corrigé
# Corrigé des exercices — Gestion des processus
------
---
## 1. QCM
1. a.
2. a.
3. c.
4. c.
5. c.
| Question | Réponse | Explication |
|----------|---------|-------------|
| 1 | **a.** | Le terminal (interpréteur de commandes) permet d'exécuter des commandes système. |
| 2 | **a.** | La commande `ps` (process status) affiche les processus sous Linux. |
| 3 | **c.** | Un programme peut être exécuté par plusieurs processus simultanément (ex : ouvrir plusieurs fenêtres du même navigateur). |
| 4 | **c.** | L'ordonnanceur choisit le processus à exécuter parmi ceux en état "prêt". |
| 5 | **c.** | L'interblocage (deadlock) survient quand des processus s'attendent mutuellement pour des ressources. |
---
## 2. Questions
1. Grâce à l'ordonnancement et le découpage des programmes en processus.
2. Un processus élu est en cours d'exécution, un prêt est en file d'attente dans l'ordonnanceur.
### Question 1
**Expliquer comment il se fait que l'utilisateur d'un ordinateur a l'impression que les programmes sont exécutés en même temps.**
L'utilisateur a l'impression que les programmes s'exécutent simultanément grâce à deux mécanismes :
1. **Le découpage en processus** : Chaque programme est divisé en plusieurs processus (unités d'exécution).
2. **L'ordonnancement** : Le système d'exploitation utilise un ordonnanceur qui :
- Gère une file d'attente des processus prêts à s'exécuter
- Alloue le processeur à chaque processus pendant un court laps de temps (quantum)
- Effectue des changements de contexte très rapides entre les processus
3. **La rapidité du processeur** : Le processeur exécute des milliards d'instructions par seconde. Les changements de contexte sont si rapides (quelques millisecondes) que l'utilisateur ne perçoit pas les interruptions.
**Exemple** : Si le quantum est de 10 ms et qu'il y a 10 processus, chaque processus s'exécute 100 fois par seconde. L'œil humain ne peut pas percevoir ces alternances, d'où l'illusion de simultanéité.
---
### Question 2
**Quelles sont les différences entre les états prêt et élu ?**
| Critère | État Prêt | État Élu |
|---------|-----------|----------|
| **Définition** | Le processus attend son tour dans la file d'exécution | Le processus est en cours d'exécution sur le processeur |
| **Ressources** | Toutes les ressources nécessaires sont disponibles | Le processeur lui est alloué |
| **Position** | Dans la file d'attente de l'ordonnanceur | Sur le processeur (CPU) |
| **Nombre** | Plusieurs processus peuvent être prêts | Un seul processus élu par cœur de processeur |
| **Transition** | Devient élu quand l'ordonnanceur le choisit | Redevient prêt quand son quantum expire |
**Schéma des transitions** :
```
Ordonnanceur choisit
[PRÊT] ─────────────────────────────► [ÉLU]
▲ │
│ Quantum expiré ou │
└─────────── interruption ────────────┘
```
---
## Résumé des états d'un processus
```
Création
┌──────────┐
│ NOUVEAU │
└────┬─────┘
│ Admission
Ressource ┌──────────┐ Ordonnancement
disponible ◄────│ PRÊT │────────────────────► ┌──────────┐
│ └──────────┘ │ ÉLU │
│ ▲ └────┬─────┘
│ │ Interruption │
│ │ (quantum expiré) │
│ ┌────┴─────┐ │
└───────────►│ BLOQUÉ │◄──────────────────────────┘
└──────────┘ Attente ressource
│ Terminaison
┌──────────┐
│ TERMINÉ │
└──────────┘
```
---
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
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>.

View File

@@ -0,0 +1,390 @@
"""
Corrigé du TP Simulateur d'Ordonnanceur
"""
# =============================================================================
# PARTIE 1 : Classe Processus
# =============================================================================
class Processus:
"""Représente un processus système."""
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
self.nom = nom
self.duree_totale = duree_totale
self.duree_restante = duree_totale
self.etat = "nouveau"
self.priorite = priorite
self.temps_arrivee = 0
self.temps_debut = None
self.temps_fin = None
def executer(self, quantum):
"""
Exécute le processus pendant quantum unités de temps.
:param quantum: (int) Temps d'exécution alloué
:return: (int) Temps réellement utilisé
"""
temps_utilise = min(quantum, self.duree_restante)
self.duree_restante -= temps_utilise
return temps_utilise
def est_termine(self):
"""Vérifie si le processus est terminé."""
return self.duree_restante <= 0
def __repr__(self):
return f"[PID {self.pid}] {self.nom} ({self.etat}) - {self.duree_restante}/{self.duree_totale}"
# =============================================================================
# PARTIE 2 : Ordonnanceur FIFO
# =============================================================================
class OrdonnanceurFIFO:
"""Ordonnanceur First In First Out."""
def __init__(self):
self.file_prets = []
self.historique = []
self.temps = 0
self.processus_termines = []
def ajouter_processus(self, processus):
"""Ajoute un processus à la file."""
processus.etat = "pret"
processus.temps_arrivee = self.temps
self.file_prets.append(processus)
print(f"[T={self.temps}] {processus.nom} ajouté (durée: {processus.duree_totale})")
def executer_suivant(self):
"""Exécute le prochain processus jusqu'à la fin."""
if len(self.file_prets) == 0:
return False
processus = self.file_prets.pop(0)
processus.etat = "elu"
processus.temps_debut = self.temps
print(f"\n[T={self.temps}] >>> {processus.nom} commence")
while not processus.est_termine():
temps_utilise = processus.executer(1)
self.temps += temps_utilise
self.historique.append(processus.nom[0])
processus.etat = "termine"
processus.temps_fin = self.temps
self.processus_termines.append(processus)
print(f"[T={self.temps}] <<< {processus.nom} terminé")
return True
def executer_tous(self):
"""Exécute tous les processus."""
print(f"\n{'='*50}")
print("ORDONNANCEUR FIFO")
print(f"{'='*50}")
while self.executer_suivant():
pass
self.afficher_resultats()
def afficher_resultats(self):
"""Affiche les résultats de l'ordonnancement."""
print(f"\n{'='*50}")
print(f"Temps total : {self.temps}")
print(f"Diagramme : {''.join(self.historique)}")
print(f"{'='*50}")
print("\nStatistiques par processus :")
temps_attente_total = 0
temps_rotation_total = 0
for p in self.processus_termines:
rotation = p.temps_fin - p.temps_arrivee
attente = rotation - p.duree_totale
temps_attente_total += attente
temps_rotation_total += rotation
print(f" {p.nom}: rotation={rotation}, attente={attente}")
n = len(self.processus_termines)
print(f"\nMoyennes: rotation={temps_rotation_total/n:.1f}, attente={temps_attente_total/n:.1f}")
# =============================================================================
# PARTIE 3 : Ordonnanceur Round Robin
# =============================================================================
class OrdonnanceurRoundRobin:
"""Ordonnanceur Round Robin (tourniquet)."""
def __init__(self, quantum=2):
self.file_prets = []
self.quantum = quantum
self.temps = 0
self.historique = []
self.processus_termines = []
def ajouter_processus(self, processus):
"""Ajoute un processus à la file."""
processus.etat = "pret"
processus.temps_arrivee = self.temps
self.file_prets.append(processus)
def executer_cycle(self):
"""Exécute un cycle d'ordonnancement."""
if len(self.file_prets) == 0:
return False
processus = self.file_prets.pop(0)
processus.etat = "elu"
if processus.temps_debut is None:
processus.temps_debut = self.temps
temps_utilise = processus.executer(self.quantum)
self.temps += temps_utilise
for _ in range(temps_utilise):
self.historique.append(processus.nom[0])
if processus.est_termine():
processus.etat = "termine"
processus.temps_fin = self.temps
self.processus_termines.append(processus)
print(f"[T={self.temps}] {processus.nom} TERMINÉ")
else:
processus.etat = "pret"
self.file_prets.append(processus)
print(f"[T={self.temps}] {processus.nom} → file ({processus.duree_restante} restant)")
return True
def executer_tous(self):
"""Exécute tous les processus."""
print(f"\n{'='*50}")
print(f"ORDONNANCEUR ROUND ROBIN (quantum={self.quantum})")
print(f"{'='*50}\n")
while self.executer_cycle():
pass
self.afficher_resultats()
def afficher_resultats(self):
"""Affiche les résultats."""
print(f"\n{'='*50}")
print(f"Temps total : {self.temps}")
print(f"Diagramme : {''.join(self.historique)}")
print(f"{'='*50}")
print("\nStatistiques par processus :")
temps_attente_total = 0
temps_rotation_total = 0
temps_reponse_total = 0
for p in self.processus_termines:
rotation = p.temps_fin - p.temps_arrivee
attente = rotation - p.duree_totale
reponse = p.temps_debut - p.temps_arrivee
temps_attente_total += attente
temps_rotation_total += rotation
temps_reponse_total += reponse
print(f" {p.nom}: rotation={rotation}, attente={attente}, réponse={reponse}")
n = len(self.processus_termines)
print(f"\nMoyennes: rotation={temps_rotation_total/n:.1f}, attente={temps_attente_total/n:.1f}, réponse={temps_reponse_total/n:.1f}")
# =============================================================================
# PARTIE 4 : Ressources et Interblocage
# =============================================================================
class Ressource:
"""Représente une ressource partagée."""
def __init__(self, nom):
self.nom = nom
self.proprietaire = None
def est_disponible(self):
return self.proprietaire is None
def acquerir(self, pid):
if self.est_disponible():
self.proprietaire = pid
return True
return False
def liberer(self):
self.proprietaire = None
def __repr__(self):
if self.est_disponible():
return f"[{self.nom}] LIBRE"
return f"[{self.nom}] → PID {self.proprietaire}"
class ProcessusAvecRessources(Processus):
"""Processus qui nécessite des ressources."""
def __init__(self, nom, duree_totale, ressources_requises):
super().__init__(nom, duree_totale)
self.ressources_requises = ressources_requises
self.ressources_obtenues = []
def demander_ressources(self, ressources_dispo):
"""Tente d'obtenir les ressources nécessaires."""
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."""
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 un interblocage."""
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" + "!"*50)
print("!!! INTERBLOCAGE DÉTECTÉ !!!")
print("!"*50)
for p in processus_actifs:
attend = [r for r in p.ressources_requises if r not in p.ressources_obtenues]
print(f" {p.nom}: possède {p.ressources_obtenues}, attend {attend}")
return True
return False
# =============================================================================
# PARTIE 5 : Comparaison
# =============================================================================
def comparer_ordonnanceurs():
"""Compare FIFO et Round Robin."""
processus_test = [
("Navigateur", 8),
("Editeur", 4),
("Terminal", 2),
("Musique", 6)
]
print("\n" + "="*60)
print("COMPARAISON FIFO vs ROUND ROBIN")
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()
# =============================================================================
# TESTS
# =============================================================================
if __name__ == "__main__":
print("="*60)
print("TEST 1 : ORDONNANCEUR FIFO")
print("="*60)
Processus.compteur_pid = 0
fifo = OrdonnanceurFIFO()
fifo.ajouter_processus(Processus("Firefox", 5))
fifo.ajouter_processus(Processus("VSCode", 3))
fifo.ajouter_processus(Processus("Spotify", 2))
fifo.executer_tous()
print("\n" + "="*60)
print("TEST 2 : ORDONNANCEUR ROUND ROBIN")
print("="*60)
Processus.compteur_pid = 0
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()
print("\n" + "="*60)
print("TEST 3 : SIMULATION D'INTERBLOCAGE")
print("="*60)
ressources = {
"A": Ressource("A"),
"B": Ressource("B")
}
Processus.compteur_pid = 0
p1 = ProcessusAvecRessources("Prog1", 5, ["A", "B"])
p2 = ProcessusAvecRessources("Prog2", 5, ["B", "A"])
print("\nÉtat initial des ressources:")
for r in ressources.values():
print(f" {r}")
print("\nTour 1: Prog1 demande ses ressources")
p1.demander_ressources(ressources)
print("\nTour 2: Prog2 demande ses ressources")
p2.demander_ressources(ressources)
print("\nTour 3: Prog1 continue")
p1.demander_ressources(ressources)
print("\nTour 4: Prog2 continue")
p2.demander_ressources(ressources)
detecter_interblocage([p1, p2], ressources)
print("\n" + "="*60)
print("TEST 4 : COMPARAISON DES ALGORITHMES")
print("="*60)
comparer_ordonnanceurs()

View File

@@ -1,3 +1,5 @@
# Exercices — Gestion des processus
## 1. QCM
1. Le terminal, interpréteur de commande :
@@ -30,7 +32,7 @@
b. Changer l'état des processus
c. Choisi le processus à exécuter
c. Choisit le processus à exécuter
5. Un interblocage est :
@@ -42,5 +44,13 @@
## 2. Questions
1. Expliquer comment cela se fait que l'utilisateur d'un ordinateur à l'impression que les programmes sont exécuté en même temps.
2. Quels sont les différences entre les états prêt et élu.
1. Expliquer comment il se fait que l'utilisateur d'un ordinateur a l'impression que les programmes sont exécutés en même temps.
2. Quelles sont les différences entre les états prêt et élu ?
---
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
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>.

View File

@@ -1,58 +1,56 @@
## Gestion des processus et des ressources
# Gestion des processus et des ressources
### Le programme
## Le programme
![bo.png](assets/bo.png)
------
Nous le savons, un ordinateur possède une architecture de Von Neumann, il y a donc différents composants tels que la mémoire vive, la mémoire 'dure' (Disque dur, SSD...), un processeur...C'est cet élèment ci qui nous intéresse aujourd'hui.
Nous le savons, un ordinateur possède une architecture de Von Neumann, il y a donc différents composants tels que la mémoire vive, la mémoire 'dure' (Disque dur, SSD...), un processeur... C'est cet élément-ci qui nous intéresse aujourd'hui.
Le processeur est le cerveau de l'ordinateur et c'est lui qui exécute les tâches de la machine.
Comment expliquer que notre processeur arrive à ouvrir un navigateur web, un lecteur de musique, télécharger un jeu ou encore envoyer des messages le tout simultanément. (Sans parler des tâches de fond de l'ordinateur)
Tout ces programmes sont en réalité des lignes immenses de code et le processeur lui agit seul. Il exécute une à une les instructions de chaque programme, mais donne la sensation de tout gérer en même temps.
Tous ces programmes sont en réalité des lignes immenses de code et le processeur, lui, agit seul. Il exécute une à une les instructions de chaque programme, mais donne la sensation de tout gérer en même temps.
> Les processeurs multi-cœurs fonctionnent de la même façon à l'exception près qu'il n'y a pas 1 cœur, mais 4 voir 8 cœurs. Mais au vu de la multitude de programmes tournant en même temps sur la machine, le nombre de cœurs est nettement inferieur.
> Les processeurs multi-cœurs fonctionnent de la même façon à l'exception près qu'il n'y a pas 1 cœur, mais 4 voire 8 cœurs. Mais au vu de la multitude de programmes tournant en même temps sur la machine, le nombre de cœurs est nettement inférieur.
## 1. Notion de processus
Lorsqu'un programme est exécuté, il créé plusieurs processus. En effet, un programme est composé de diverses instructions (lignes de code) qui elles mêmes forment diverses parties (processus) de celui-ci.
Lorsqu'un programme est exécuté, il crée plusieurs processus. En effet, un programme est composé de diverses instructions (lignes de code) qui elles-mêmes forment diverses parties (processus) de celui-ci.
Ces processus sont donc stocker dans une file (tout cela est géré par l'OS [Rappel 1ère]) et sont exécuté un à un.
Ces processus sont donc stockés dans une file (tout cela est géré par l'OS [Rappel 1ère]) et sont exécutés un à un.
*Exemple de répartition de deux programmes par l'OS :*
<img src="assets/file.png" alt="File" style="width:50%;" />
Chaque processus possèdent différentes informations stockées en mémoire (dans le PCB (*Process Control Block*)) comme :
Chaque processus possède différentes informations stockées en mémoire (dans le PCB (*Process Control Block*)) comme :
| Nom | Description |
| ---------- | ----------------------------------------------------- |
| PID | Process ID, identifiant du processus |
| Etat | Etat du processus |
| Registre | Valeur des registres lors de la dernière interruption |
| Mémoire | Emplacement mémoire, allouée par le processeur |
| Mémoire | Emplacement mémoire alloué par le système |
| Ressources | Ressources utilisées par le processus. |
## 2. Gestion des processus
Nous l'avons vu chaque processus possèdent plusieurs informations. **L'état** d'un processus permet de comprendre comment la file d'exécution est créée et comment celle-ci perdure.
Nous l'avons vu, chaque processus possède plusieurs informations. **L'état** d'un processus permet de comprendre comment la file d'exécution est créée et comment celle-ci perdure.
Il existe 3 états différents :
- Prêt : Le processus attend d'être exécuté. Il est dans la file d'exécution.
- Elu : En cours d'exécution
- Bloqué/En attente : Le processus nécessite une ressource non disponible. Tel qu'un emplacement mémoire, une entré/sortie.
- Bloqué/En attente : Le processus nécessite une ressource non disponible, tel qu'un emplacement mémoire ou une entrée/sortie.
Lorsque la ressource sera disponible, le processus repassera en état prêt.
- On peut imaginer que le processus attend l'intervention de l'utilisateur, ou le chargement d'une ressources (donc d'autres processus), etc.
- On peut imaginer que le processus attend l'intervention de l'utilisateur, ou le chargement d'une ressource (donc d'autres processus), etc.
Il existe aussi deux autres états :
- Nouveau : le processus vient d'être crée, il n'est pas encore dans la file d'exécution
- Nouveau : le processus vient d'être créé, il n'est pas encore dans la file d'exécution
- Terminé : l'exécution du processus est finie.
<u>Voici un schéma des différents états :</u>
@@ -61,8 +59,8 @@ Il existe aussi deux autres états :
## 3. Interblocage :
L'interblocage intervient lorsque plusieurs processus sont bloqués les un aux autres.
Imaginons deux programme.
L'interblocage intervient lorsque plusieurs processus sont bloqués les uns par les autres.
Imaginons deux programmes.
```
# Programme 1 :
@@ -90,7 +88,7 @@ Imaginons deux programme.
Accès à la ressource B
```
- Le processus 1 à commencé, via un premier processus. Afin de faire tourner tous les programmes 'en même temps' il repasse dans la file d'exécution
- Le processus 1 a commencé, via un premier processus. Afin de faire tourner tous les programmes 'en même temps' il repasse dans la file d'exécution
```
# Programme 2 :
@@ -127,14 +125,14 @@ Imaginons deux programme.
Accès à la ressource A
```
- Le programme 1 créer un premier processus, qui appelle la ressource A
- Le programme 1 crée un premier processus, qui appelle la ressource A
```
# Programme 2 :
Accès à la ressource B
```
- le programme 2 créer un premier processus, qui appelle la ressource B
- Le programme 2 crée un premier processus, qui appelle la ressource B
```
# Programme 1 :
@@ -150,13 +148,13 @@ Imaginons deux programme.
- Le programme 2 attend la ressource A utilisée par le programme 1 et passe en état bloqué.
Ici les deux programmes sont en état bloqué car chacun attend la ressource de l'autre. Il y a interblocage et donc aucuns des programmes ne peut s'exécuter.
Ici les deux programmes sont en état bloqué car chacun attend la ressource de l'autre. Il y a interblocage et donc aucun des programmes ne peut s'exécuter.
## 4. En pratique
### 4. 1. Windows
Sous windows il est possible de voir les processus en cours d'exécution grâce au **gestionnaire de tâche**. Celui-ci est accessible grâce aux touches Ctrl+ Maj+Echap.
Sous Windows il est possible de voir les processus en cours d'exécution grâce au **gestionnaire de tâches**. Celui-ci est accessible grâce aux touches Ctrl+Maj+Echap.
Il suffit ensuite d'aller dans l'onglet détail et on obtient tous les processus.
<img src="assets/liste_processus_windows.png" style="width:60%;" />
@@ -169,16 +167,16 @@ Sous linux, il suffit d'accéder au terminal. Et d'y écrire la commande **ps**,
![ps_aux](assets/ps_aux.webp)
### 4.3 MacOs
### 4. 3. macOS
MacOs étant un système Unix, (dérivé de BSD ici) on retrouve les mêmes commandes que sous linux en grande partie.
macOS étant un système Unix (dérivé de BSD), on retrouve les mêmes commandes que sous Linux en grande partie.
Pour voir les processus, ici, on utilisera plutôt **TOP** (dispo sous linux également donc).
<img src="assets/top.png" alt="top" style="zoom:50%;" />
---------
Auteurs : Florian Mathieu, Timothée Decoster, Enzo Frémaux
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
Licence CC BY NC

View File

@@ -0,0 +1,503 @@
# TP : Simulateur d'Ordonnanceur — Gestion des Processus
> **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)
- `etat` : état actuel du processus (str) — "nouveau", "pret", "elu", "bloque", "termine"
- `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("\nTour 2 : P2 demande B")
p2.demander_ressources(ressources)
print("\nTour 3 : P1 demande B (déjà pris par P2)")
p1.demander_ressources(ressources)
print("\nTour 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>.