Chapitre 3
Le système d'exploitation
Lecture : 31 minQuiz de 5 questions
# Le système d'exploitation
## Objectifs et prérequis
**Objectifs** : À la fin de ce chapitre, l’élève sera capable de :
- Définir précisément ce qu’est un système d’exploitation (SE) et en citer les fonctions essentielles.
- Décrire l’architecture interne d’un SE (noyau, appels système, shell).
- Expliquer les mécanismes de gestion des processus (états, ordonnancement, synchronisation).
- Présenter les principes de gestion de la mémoire (allocation contiguë, pagination, mémoire virtuelle).
- Décrire l’organisation et la manipulation des fichiers dans un système de fichiers.
- Identifier les principaux concepts de sécurité et de protection mis en œuvre par un SE.
- Comparer les interfaces utilisateur en ligne de commande (CLI) et graphique (GUI).
- Résoudre des exercices d’application typiques du Probatoire (calcul de temps d’attente, de temps de retournement, choix d’algorithme d’allocation, etc.).
**Prérequis** : L’élève doit déjà maîtriser :
- Les notions de base de l’architecture d’un ordinateur (CPU, mémoire RAM, périphériques d’entrée/sortie).
- La représentation binaire de l’information et les opérations logiques de base.
- Les concepts d’algorithme et de complexité temporelle (O‑notation).
- L’utilisation d’un terminal ou d’une invite de commandes (notions de commandes simples comme `ls`, `cd`, `mkdir`).
---
## Introduction contextualisée
Depuis les premiers ordinateurs des années 1940, où chaque programme devait gérer directement le matériel, l’informatique a connu une évolution décisive avec l’apparition des **systèmes d’exploitation**. Un SE constitue une couche logicielle intermédiaire entre le matériel nu et les applications utilisateur. Il permet d’abstraire les détails matériels, de partager les ressources de façon équitable et sécurisée, et d’offrir une interface cohérente aux développeurs et aux utilisateurs.
Au Cameroun, comme dans le reste du monde, les systèmes d’exploitation sont omniprésents : les ordinateurs de laboratoire utilisent souvent des distributions Linux (Ubuntu, Debian), les postes administratifs tournent sous Windows, tandis que les smartphones intègrent Android ou iOS, tous dérivés d’un noyau UNIX‑like. Comprendre le fonctionnement d’un SE est donc indispensable pour tout élève préparant le Probatoire, car il constitue la base sur laquelle reposent les cours de programmation, de réseaux et de sécurité qui suivent.
---
## I. Définition, fonctions et évolution du système d’exploitation
### A. Qu’est-ce qu’un système d’exploitation ?
> **Définition** : Un système d’exploitation est un ensemble de programmes qui gère les ressources matérielles d’un ordinateur (processeur, mémoire, périphériques d’entrée/sortie) et fournit une interface abstraite permettant aux applications et aux utilisateurs d’exécuter leurs tâches de manière efficace et sécurisée.
Le SE joue trois rôles fondamentaux :
1. **Gestionnaire de ressources** : il alloue le temps CPU, la mémoire RAM et les périphériques aux différents processus en cours.
2. **Fournisseur de services** : il offre des appels système (system calls) tels que `open`, `read`, `write`, `fork`, `exec` que les programmes peuvent invoquer via une interface normale.
3. **Interface utilisateur** : il propose soit une ligne de commande (CLI), soit une interface graphique (GUI) permettant à l’utilisateur d’interagir avec la machine.
### B. Fonctions principales
| Fonction | Description | Exemple d’appel système |
|----------|-------------|--------------------------|
| **Gestion des processus** | Création, ordonnancement, suspension et terminaison des processus. | `fork()`, `exec()`, `wait()` |
| **Gestion de la mémoire** | Allocation et libération de la mémoire virtuelle et physique. | `brk()`, `mmap()`, `munmap()` |
| **Gestion des fichiers** | Création, lecture, écriture, suppression et organisation des données sur disque. | `open()`, `read()`, `write()`, `close()` |
| **Gestion des périphériques** | Abstraction des périphériques d’entrée/sortie via des pilotes. | `ioctl()` |
| **Protection et sécurité** | Contrôle d’accès aux ressources (authentification, autorisation, chiffrement). | `chmod()`, `setuid()` |
| **Interface utilisateur** | Fourniture d’un moyen d’interaction (CLI ou GUI). | `execve()` (exécution de programmes) |
### C. Évolution historique
- **Années 1950‑1960** : Les premiers systèmes étaient des *moniteurs* simples (ex. : GM‑NAA I/O pour IBM 704).
- **1969** : Naissance de **UNIX** chez Bell Labs (Ken Thompson, Dennis Ritchie). UNIX introduit le concept de **noyau monolithique**, de pipes et de la philosophie « tout est fichier ».
- **1970‑1980** : Développement de **BSD** (Berkeley Software Distribution) et de **System V**, deux variantes influentes d’UNIX.
- **1981** : Sortie de **MS‑DOS** (Microsoft), système d’exploitation à tâche unique destiné aux PC IBM.
- **1985** : Lancement de **Windows 1.0**, première interface graphique de Microsoft basée sur MS‑DOS.
- **1991** : Linus Torvalds publie le noyau **Linux**, noyau monolithique libre qui deviendra la base de nombreuses distributions (Debian, Red Hat, Ubuntu).
- **2000‑présent** : Émergence de systèmes d’exploitation mobiles (iOS, Android) basés sur des noyaux UNIX‑like, et de systèmes temps réel (FreeRTOS, VxWorks) pour l’embarqué.
> **À retenir** : Le noyau Linux, sous licence GPLv2, est aujourd’hui le cœur de plus de 70 % des serveurs web et de la majorité des smartphones Android.
---
## II. Architecture d’un système d’exploitation
### A. Noyau (kernel) et ses types
Le noyau est la partie du SE qui s’exécute en mode privilégié (mode noyau) et qui a un accès direct au matériel. Trois grandes familles existent :
1. **Noyau monolithique** : Toutes les fonctions du noyau (gestion des processus, mémoire, périphériques, systèmes de fichiers) sont implémentées dans un même espace d’adressage. Avantages : performances élevées (moins de commutations de contexte). Inconvénients : moindre modularité, un bug peut crasher tout le noyau. Exemple : Linux traditionnel, MS‑DOS.
2. **Micro-noyau** : Seul le strict minimum (ordonnancement, gestion des interruptions, communication inter-processus) réside en mode noyau. Les autres services (systèmes de fichiers, pilotes) tournent en tant que processus en mode utilisateur. Avantages : meilleure fiabilité et modularité. Inconvénients : surcoût de performance dû aux nombreuses commutations. Exemple : MINIX, QNX.
3. **Noyau hybride** : Combine les avantages des deux approches en gardant en mode noyau les services les plus critiques (ordonnancement, gestion de la mémoire) tout en déplaçant certains pilotes ou systèmes de fichiers en mode utilisateur. Exemple : Windows NT, macOS (XNU).
> **Propriété** : Dans un noyau monolithique, l’appel système entraîne une seule transition du mode utilisateur vers le mode noyau, tandis qu’un micro-noyau peut nécessiter deux transitions (utilisateur → noyau → service en mode utilisateur → noyau).
### B. Appels système (system calls)
Les appels système sont l’interface officielle entre un programme en mode utilisateur et le noyau. Ils sont invoqués via une instruction logicielle spécifique (ex. : `int 0x80` sur x86 Linux, `syscall` sur x86‑64).
Un appel système typique suit les étapes suivantes :
1. **Passage des paramètres** : les arguments sont placés dans des registres ou sur la pile selon la convention d’appel (ABI).
2. **Déclenchement de l’interruption logicielle** : le processeur passe en mode noyau, sauvegarde le contexte utilisateur.
3. **Exécution du service noyau** : le noyau vérifie les droits, effectue l’opération demandée.
4. **Retour au mode utilisateur** : le résultat est placé dans un registre (généralement `eax`/`rax`) et le contexte utilisateur est restauré.
> **Exemple** : L’appel système `write(fd, buf, count)` sur Linux x86‑64 utilise le registre `rax` pour le numéro d’appel (1 pour `write`), `rdi` pour le descripteur de fichier, `rsi` pour le pointeur vers le tampon, et `rdx` pour le nombre d’octets.
### C. Shell et interfaces utilisateur
Le **shell** est un programme qui interprète les commandes entrées par l’utilisateur (CLI) et les traduit en appels système ou en lancement de programmes. Les shells les plus répandus sont **bash** (Bourne Again SHell), **zsh**, **sh** (Bourne shell) et **powershell** sous Windows.
L’interface graphique (GUI) repose sur un **gestionnaire de fenêtres** (ex. : GNOME, KDE, Xfce sous Linux ; Explorer.exe sous Windows) qui utilise les services du noyau pour dessiner sur l’écran, gérer les périphériques d’entrée (clavier, souris) et lancer des applications graphiques.
> **À retenir** : Un utilisateur peut travailler exclusivement en CLI (ex. : administration de serveurs) ou exclusivement en GUI (ex. : bureautique), mais les deux interfaces s’appuient sur le même noyau et les mêmes appels système.
---
## III. Gestion des processus
### A. Concept de processus, états et PCB
Un **processus** est un programme en cours d’exécution accompagné de son contexte d’exécution (registres, compteur d’instruction, pile, tas, descripteurs de fichiers, etc.). Le noyau représente chaque processus par un **PCB** (Process Control Block) contenant :
- **Identifiant du processus (PID)**
- **État du processus** (nouveau, prêt, exécutant, bloqué, terminé)
- **Compteur d’instruction (PC)**
- **Registres CPU**
- **Information de gestion de la mémoire** (base et limite, table des pages)
- **Descripteurs de fichiers ouverts**
- **Priorité**
> **Définition** : Un processus est dit **prêt** lorsqu’il possède toutes les ressources nécessaires sauf le CPU ; il est **exécutant** lorsqu’il occupe le CPU ; il est **bloqué** lorsqu’il attend un événement (ex. : fin d’E/S).
### B. Ordonnancement (scheduling)
L’ordonnancement décide quel processus prêt obtient le CPU ensuite. Plusieurs algorithmes sont étudiés au niveau du Probatoire :
1. **FCFS (First‑Come, First‑Served)** : file d’attente simple, non préemptif.
2. **SJF (Shortest Job First)** : choisit le processus ayant le temps d’exécution restant le plus court ; peut être préemptif (SRTF).
3. **Round Robin (RR)** : chaque processus reçoit un **quantum** de temps fixe ; préemptif par défaut.
4. **Priorité** : chaque processus possède une priorité numérique ; le planificateur choisit le processus de priorité la plus élevée (peut être préemptif ou non).
#### Métriques d’évaluation
- **Temps d’attente (WT)** : somme des périodes durant lesquelles le processus est prêt mais n’exécute pas.
- **Temps de retournement (TAT)** : durée entre l’arrivée du processus et sa terminaison.
- **Temps de réponse (RT)** : délai entre l’arrivée et le premier démarrage d’exécution.
Formules (pour un ensemble de *n* processus) :
$$
WT_i = TAT_i - BT_i
$$
$$
TAT_i = CT_i - AT_i
$$
où
- $BT_i$ = temps d’exécution (burst time) du processus *i*
- $CT_i$ = temps de completion (completion time)
- $AT_i$ = temps d’arrivée (arrival time)
> **À retenir** : Dans un système RR avec quantum $q$, le nombre de context switches approximatif pour un processus de burst $BT$ est $\lceil BT/q \rceil - 1$.
#### Exemple 1 – FCFS (non préemptif)
Trois processus arrivent à l’instant 0 avec les burst times suivants :
| Processus | BT (ms) |
|-----------|---------|
| P1 | 6 |
| P2 | 8 |
| P3 | 3 |
**Solution** :
- P1 s’exécute de 0 à 6 → $CT_{P1}=6$
- P2 de 6 à 14 → $CT_{P2}=14$
- P3 de 14 à 17 → $CT_{P3}=17$
Calcul du TAT et WT :
| Processus | AT | CT | TAT = CT‑AT | BT | WT = TAT‑BT |
|-----------|----|----|-------------|----|-------------|
| P1 | 0 | 6 | 6 | 6 | 0 |
| P2 | 0 | 14 | 14 | 8 | 6 |
| P3 | 0 | 17 | 17 | 3 | 14 |
Moyenne WT = (0+6+14)/3 = **6,67 ms** ; moyenne TAT = (6+14+17)/3 = **12,33 ms**.
#### Exemple 2 – Round Robin (quantum = 4 ms)
Même ensemble de processus, arrivée à 0.
**Étapes** :
- **t=0‑4** : P1 exécute 4 ms (reste BT=2)
- **t=4‑8** : P2 exécute 4 ms (reste BT=4)
- **t=8‑11** : P3 exécute 3 ms (BT=0 → terminé à t=11)
- **t=11‑13** : P1 exécute ses 2 ms restants → terminé à t=13
- **t=13‑17** : P2 exécute ses 4 ms restants → terminé à t=17
**Calcul** :
| Processus | CT | TAT = CT‑AT | BT | WT = TAT‑BT |
|-----------|----|-------------|----|-------------|
| P1 | 13 | 13 | 6 | 7 |
| P2 | 17 | 17 | 8 | 9 |
| P3 | 11 | 11 | 3 | 8 |
Moyenne WT = (7+9+8)/3 = **8,0 ms** ; moyenne TAT = (13+17+11)/3 = **13,67 ms**.
> **Remarque** : Le RR offre un meilleur temps de réponse mais peut augmenter le temps d’attente comparé à FCFS lorsque les bursts sont hétérogènes.
### C. Synchronisation et communication inter‑processus
Lorsque plusieurs processus partagent des ressources (mémoire, fichiers), il faut éviter les **conditions de course**. Deux mécanismes fondamentaux :
- **Sémaphores** (comptage ou binaire) : opérations `wait()` (P) et `signal()` (V).
- **Mutex** (mutual exclusion) : sémaphore binaire utilisé pour protéger une section critique.
**Exemple de sémaphore binaire** pour protéger une variable globale `counter` :
```c
sem_t mutex; // initialisé à 1
int counter = 0;
void increment() {
sem_wait(&mutex); // P
counter++;
sem_post(&mutex); // V
}
```
> **À retenir** : Un sémaphore initialisé à 1 garantit qu’au plus un processus peut être dans la section critique à la fois.
Les **tubes (pipes)** permettent la communication unidirectionnelle : la sortie d’un processus devient l’entrée d’un autre. Exemple shell : `ls | grep .txt`.
---
## IV. Gestion de la mémoire
### A. Allocation contiguë vs pagination
Dans les premiers systèmes, la mémoire principale était allouée de façon **contiguë** : chaque processus obtenait un bloc d’adresses physiques consécutives. Cela entraînait deux types de **fragmentation** :
- **Fragmentation externe** : espace libre éparpillé, insuffisant pour allouer un nouveau bloc malgré une quantité totale suffisante.
- **Fragmentation interne** : espace perdu à l’intérieur d’un bloc alloué parce que la taille demandée est inférieure à la taille du bloc alloué.
La **pagination** résout la fragmentation externe en divisant la mémoire en **pages** de taille fixe (généralement 4 Ko). L’espace d’adressage logique d’un processus est également découpé en pages ; une **table des pages** associe chaque page logique à un cadre physique (frame).
> **Définition** : Une **page** est l’unité minimale de transfert entre la mémoire virtuelle et la mémoire physique.
### B. Algorithmes de placement (pour l’allocation contiguë)
Lorsque l’on travaille encore avec allocation contiguë (ex. : systèmes embarqués sans MMU), on utilise des stratégies de placement :
- **First Fit** : on parcourt la liste des trous libres et on alloue le premier trou assez grand.
- **Best Fit** : on choisit le trou libre dont la taille est la plus petite mais suffisante (minimise le gaspillage).
- **Worst Fit** : on choisit le plus grand trou libre (laisse le plus grand reste).
#### Exemple 1 – First Fit
Mémoire de 100 Mo, trous libres initialement : [0‑20], [30‑50], [60‑100] (en Mo).
Demande d’allocation de 25 Mo :
- Premier trou [0‑20] trop petit (20 < 25).
- Deuxième trou [30‑50] taille 20 Mo → encore trop petit.
- Troisième trou [60‑100] taille 40 Mo → suffisant → on alloue [60‑85].
Nouvelle liste de trous : [0‑20], [30‑50], [85‑100].
#### Exemple 2 – Best Fit
Même état initial. Demande de 15 Mo :
- Trou [0‑20] taille 20 Mo → reste 5 Mo.
- Trou [30‑50] taille 20 Mo → reste 5 Mo.
- Trou [60‑100] taille 40 Mo → reste 25 Mo.
Le meilleur ajustement est soit [0‑20] soit [30‑50] (reste 5 Mo). On choisit le premier rencontré → allocation [0‑15].
Nouveaux trous : [15‑20], [30‑50], [60‑100].
### C. Mémoire virtuelle, pagination et table des pages
Avec la pagination, chaque processus possède une **espace d’adressage virtuel** (souvent 2³² ou 2⁶⁴ octets) indépendamment de la taille de la RAM. Le **MMU** (Memory Management Unit) traduit les adresses virtuelles en adresses physiques à l’aide de la **table des pages**.
Chaque entrée de la table des pages contient :
- **Bit présent/absent** (P) : indique si la page est chargée en RAM.
- **Numéro de cadre** (frame number) si présent.
- **Bits de protection** (lecture/écriture/exécution).
- **Bit d’accès** (A) et **bit modifié** (M) utilisés par les algorithmes de remplacement.
Lorsque le processus accède à une page absente, une **page fault** se produit ; le noyau charge la page depuis le disque (swap) dans un cadre libre, éventuellement en évictant une page selon un algorithme de remplacement (LRU, FIFO, Clock).
> **Formule du temps d’accès effectif (EAT)** en présence de page faults :
$$
EAT = (1 - p) \times t_{mem} + p \times (t_{swap} + t_{mem})
$$
où
- $p$ = probabilité de page fault
- $t_{mem}$ = temps d’accès à la mémoire RAM (ex. : 100 ns)
- $t_{swap}$ = temps de lecture/écriture sur le disque (ex. : 8 ms)
#### Exemple 3 – Calcul du EAT
Supposons $t_{mem}=100$ ns, $t_{swap}=8$ ms = $8\times10^{6}$ ns, et $p=0,001$ (0,1 % de faults).
$$
EAT = (1-0,001)\times100 + 0,001\times(8\times10^{6}+100)
$$
$$
= 0,999\times100 + 0,001\times8\,000\,100
$$
$$
= 99,9 + 8\,000,1 = 8\,100\text{ ns} \approx 8,1\;\mu s
$$
Ainsi, même un faible taux de faults augmente considérablement le temps d’accès moyen.
> **À retenir** : La pagination élimine la fragmentation externe mais introduit un overhead lié aux page faults ; un bon algorithme de remplacement et une taille de page adaptée sont essentiels pour maintenir des performances acceptables.
---
## V. Gestion des fichiers et du système de fichiers
### A. Notions de fichier, répertoire et inode
Un **fichier** est une séquence d’octets nommée, associée à des métadonnées (taille, droits d’accès, timestamps). Dans les systèmes de type UNIX, chaque fichier est représenté par un **inode** (index node) qui stocke :
- Numéro d’inode (identifiant unique dans le système de fichiers)
- Type et droits d’accès (mode)
- Nombre de liens durs
- UID et GID du propriétaire
- Taille en octets
- Timestamps (atime, mtime, ctime)
- Pointeurs vers les blocs de données (directs, indirects simples, doubles, triples)
Un **répertoire** est un fichier spécial contenant une liste d’associations *nom → numéro d’inode*.
> **Définition** : Le **système de fichiers** est l’ensemble des structures (inodes, tables d’allocation de blocs, journal) qui permettent de stocker, retrouver et gérer les fichiers sur un périphérique de stockage bloc (disque dur, SSD, carte SD).
### B. Types de systèmes de fichiers
| Système | Caractéristiques principales | Utilisation typique |
|---------|------------------------------|---------------------|
| **FAT32** | Table d’allocation de fichiers simple, pas de journalisation, limité à 4 Go par fichier | Clés USB, cartes SD anciennes |
| **NTFS** | Journalisation, compression, chiffrement (EFS), ACL détaillées, support de grands volumes | Windows moderne |
| **ext4** | Journalisation, extents, préallocation, support de volumes jusqu’à 1 EiB | Linux grand public |
| **APFS** (Apple) | Copie‑on‑write, clonage instantané, chiffrement natif | macOS, iOS |
| **exFAT** | Pas de journalisation, conçu pour les clés USB de grande capacité, compatible multiplateforme | Stockage externe multi‑OS |
### C. Opérations de base sur les fichiers
Les appels système POSIX les plus utilisés sont :
- `int open(const char *pathname, int flags);` – ouvre ou crée un fichier, retourne un descripteur de fichier (fd).
- `ssize_t read(int fd, void *buf, size_t count);` – lit jusqu’à `count` octets dans `buf`.
- `ssize_t write(int fd, const void *buf, size_t count);` – écrit `count` octets depuis `buf`.
- `int close(int fd);` – libère le descripteur.
- `int unlink(const char *pathname);` – supprime le lien nommé vers l’inode (si le compte de liens tombe à 0, l’inode et ses données sont libérés).
> **À retenir** : Un descripteur de fichier est un entier non négatif qui représente une entrée dans la table des fichiers ouverts du processus ; il est indépendant du nom du fichier dans le système de fichiers.
#### Exemple 4 – Copie d’un fichier avec `open/read/write/close`
Objectif : copier `source.txt` vers `dest.txt` en blocs de 4096 octets.
```c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define BUF_SIZE 4096
int main(int argc, char *argv[]) {
int src = open("source.txt", O_RDONLY);
if (src == -1) { perror("open source"); return 1; }
int dst = open("dest.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst == -1) { perror("open dest"); close(src); return 1; }
char buf[BUF_SIZE];
ssize_t n;
while ((n = read(src, buf, BUF_SIZE)) > 0) {
ssize_t w = write(dst, buf, n);
if (w != n) { perror("write"); break; }
}
if (n == -1) perror("read");
close(src);
close(dst);
return 0;
}
```
**Exécution** :
1. `open` retourne des descripteurs, par exemple `src=3`, `dst=4`.
2. Boucle `read` : chaque appel lit jusqu’à 4096 octets ; si la fin du fichier est atteinte, `read` retourne 0.
3. `write` écrit exactement le nombre d’octets lus.
4. À la fin, les deux descripteurs sont fermés, libérant les ressources noyau.
#### Exemple 5 – Création d’un répertoire et vérification des droits
```bash
mkdir -p /home/etudiant/projets/tp1
chmod 750 /home/etudiant/projets/tp1
ls -ld /home/etudiant/projets/tp1
```
Résultat attendu :
```
drwxr-x--- 2 etudiant etudiant 4096 sept. 12 10:00 /home/etudiant/projets/tp1
```
Le répertoire possède les droits `rwx` pour le propriétaire, `rx` pour le groupe, et aucun droit pour les autres.
---
## VI. Sécurité et protection
### A. Modèle de protection (rings, ACL)
Les processeurs modernes définissent plusieurs **niveaux de privilège** (rings). Sur x86, on trouve :
- **Ring 0** : mode noyau (le SE s’exécute ici).
- **Ring 1‑2** : rarement utilisés (souvent réservés aux pilotes ou aux machines virtuelles).
- **Ring 3** : mode utilisateur (les applications).
Le SE effectue un **changement de niveau de privilège** uniquement via des interruptions logicielles (appels système) ou des exceptions.
Les **listes de contrôle d’accès (ACL)** permettent de spécifier, pour chaque fichier ou répertoire, quels utilisateurs ou groupes ont quels droits (lecture, écriture, exécution). Sur Linux, `getfacl` et `setfacl` manipulent les ACL étendues.
> **À retenir** : Un processus en mode utilisateur ne peut pas directement modifier les tables de pages ou exécuter des instructions privilégiées ; il doit passer par un appel système qui effectue la vérification des droits en mode noyau.
### B. Authentification, autorisation et chiffrement
- **Authentification** : vérification de l’identité d’un utilisateur (ex. : mot de passe, empreinte digitale, jeton). Le SE stocke les mots de passe sous forme de hachage (souvent avec `sha256` + sel) dans `/etc/shadow`.
- **Autorisation** : après authentification, le SE vérifie les ACL ou les droits associés au uid/gid du processus pour décider si l’opération est autorisée.
- **Chiffrement** : les systèmes modernes offrent le chiffrement de disque complet (LUKS sous Linux, BitLocker sous Windows) ou le chiffrement au niveau des fichiers (eCryptfs, APFS).
#### Exemple 6 – Vérification d’un mot de passe avec `crypt`
Supposons que l’entrée dans `/etc/shadow` pour l’utilisateur `alice` soit :
```
alice:$6$ saltsalt $xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:18000:0:99999:7:::
```
Le champ après le deuxième `$` est le **sel** (`saltsalt`). L’algorithme utilisé est SHA‑512 (`$6$`). Pour vérifier un mot de passe candidat `Mdp123` :
1. Calculer le hachage : `hash = crypt("Mdp123", "$6$saltsalt")`.
2. Comparer le résultat avec le champ stocké après le deuxième `$`.
3. Si identique, l’authentification réussit.
> **À retenir** : Le sel rend chaque hachage unique, empêchant l’utilisation de tables de précalcul (rainbow tables) pour retrouver le mot de passe.
---
## VII. Interfaces utilisateur et interaction homme‑machine
### A. Ligne de commande (CLI)
La CLI permet à l’utilisateur d’exécuter des commandes texte via un **shell**. Les caractéristiques principales :
- **Prompt** : indique l’état (ex. : `etudiant@lab:~$`).
- **Historique** : les commandes précédentes sont enregistrées (ex. : `~/.bash_history`).
- **Redirection** : `<`, `>`, `>>`, `|` permettent de contrôler les flux d’entrée/sortie.
- **Substitution de commande** : `` `cmd` `` ou `$(cmd)` permet d’insérer la sortie d’une commande dans une autre.
#### Exemple 7 – Utilisation de redirections et de pipes
```bash
# Compter le nombre de lignes contenant le mot "erreur" dans un fichier log
grep -i erreur /var/log/syslog | wc -l
```
Explication :
1. `grep -i erreur /var/log/syslog` sélectionne les lignes contenant « erreur » (insensible à la casse).
2. Le résultat est redirigé (`|`) vers `wc -l` qui compte les lignes.
3. Le nombre affiché est le nombre d’occurrences.
### B. Interface graphique (GUI)
Une GUI repose sur un **serveur d’affichage** (X11 ou Wayland sous Linux, Quartz sous macOS, le gestionnaire de fenêtres de Windows) qui reçoit les événements clavier/souris du noyau et les transmet aux applications. Les bibliothèques de widget (GTK, Qt, WinForms, WPF) permettent de construire des fenêtres, des boutons, des menus, etc.
> **À retenir** : Même si l’apparence diffère, une application GUI effectue finalement les mêmes appels système qu’une application CLI (ouverture de fichiers, lecture/écriture, création de processus).
---
## Synthèse du chapitre
- **Définition** : Le SE gère les ressources matérielles et fournit une abstraction aux applications et aux utilisateurs.
- **Fonctions essentielles** : gestion des processus, de la mémoire, des fichiers, des périphériques, sécurité et interface utilisateur.
- **Architecture** : noyau (monolithique, micro-noyau, hybride) + appels système + shell/CLI ou GUI.
- **Gestion des processus** : états, PCB, ordonnancement (FCFS, SJF, RR, priorité), métriques WT/TAT/RT, synchronisation (sémaphores, mutex).
- **Gestion de la mémoire** : allocation contiguë (first/best/worst fit), pagination, table des pages, page fault, EAT.
- **Systèmes de fichiers** : inodes, répertoires, types (FAT32, NTFS, ext4, APFS), opérations de base (open/read/write/close/unlink).
- **Sécurité** : modèle de rings, ACL, authentification (hash + sel), autorisation, chiffrement.
- **Interfaces** : CLI (shell, redirections, pipes) et GUI (serveur d’affichage, widget toolkit).
> **À retenir** : Un bon SE doit offrir **efficacité** (faible overhead), **équité** (ordonnancement juste), **sécurité** (isolation des processus) et **facilité d’usage** (interfaces claires).
---
## Glossaire
| Terme | Définition |
|-------|------------|
| **Noyau (kernel)** | Partie du SE qui s’exécute en mode privilégié et gère directement le matériel. |
| **Appel système (system call)** | Interface permettant à un programme en mode utilisateur de demander un service au noyau. |
| **PCB (Process Control Block)** | Structure de données contenant le contexte d’exécution d’un processus. |
| **Ordonnancement (scheduling)** | Algorithme qui décide quel processus obtient le CPU ensuite. |
| **Quantum** | Tranche de temps fixe attribuée à chaque processus dans l’algorithme Round Robin. |
| **Page** | Unité fixe de mémoire virtuelle (généralement 4 Ko) utilisée dans la pagination. |
| **Table des pages** | Structure qui associe chaque page logique d’un processus à un cadre physique en RAM. |
| **Page fault** | Interruption générée lorsque le processus tente d’accéder à une page absente en mémoire physique. |
| **Inode** | Structure contenant les métadonnées d’un fichier dans un système de fichiers de type UNIX. |
| **ACL (Access Control List)** | Liste spécifiant les permissions d’accès (lecture, écriture, exécution) pour chaque utilisateur/groupe sur un fichier ou répertoire. |
| **Shell** | Programme interprétant les commandes texte entrées par l’utilisateur (CLI). |
| **GUI (Graphical User Interface)** | Interface basée sur des fenêtres, icônes, menus et pointeurs permettant une interaction visuelle avec l’ordinateur. |
| **Redirection** | Mécanisme de changement des flux d’entrée/sortie d’une commande (ex. : `>`, `<`, `|`). |
| **Chiffrement de disque** | Technique qui rend illisible le contenu d’un périphérique de stockage sans la clé de déchiffrement. |
---
## Exercices d'application corrigés
### Exercice 1 – Ordonnancement FCFS
**Énoncé** : Quatre processus arrivent tous à l’instant 0 avec les burst times suivants :
| Processus | BT (ms) |
|-----------|---------|
| A | 5 |
| B | 2 |
| C | 8 |
| D | 3 |
1. Calculer le temps de retournement (TAT) et le temps d’attente (WT) de chaque processus.
2. Donner la moyenne du TAT et du WT.
**Corrigé** :
- Ordre d’exécution : A → B → C → D (arrivée identique, FCFS).
- **Chronologie** :
- A : 0‑5 → $CT_A=5$
- B : 5‑7 → $CT_B=7$
- C : 7‑15 → $CT_C=15$
- D : 15‑18 → $CT_D=18$
- **TAT = CT – AT** (AT = 0 pour tous) :
- $TAT_A = 5$ ms, $TAT_B = 7$ ms, $TAT_C = 15$ ms, $TAT_D = 18$ ms.
- **WT = TAT – BT** :
- $WT_A = 5-5 = 0$ ms
- $WT_B = 7-2 = 5$ ms
- $WT_C = 15-8 = 7$ ms
- $WT_D = 18-3 = 15$ ms
- **Moyennes** :
- $\overline{TAT} = (5+7+15+18)/4 = 45/4 = 11,25$ ms
- $\overline{WT} = (0+5+7+15)/4 = 27/4 = 6,75$ ms
---
### Exercice 2 – Ordonnancement Round Robin
**Énoncé** : Même ensemble de processus que l’exercice 1, mais avec un quantum de 3 ms. Supposer que tous arrivent à l’instant 0.
1. Déroulez l’ordonnancement étape par étape (tableau de Gantt simplifié).
2. Calculez le TAT et le WT de chaque processus.
3. Comparez les moyennes obtenues avec celles du FCFS.
**Corrigé** :
- **File initiale** : A, B, C, D.
- **Quantum** $q = 3$ ms.
| Temps | Processus exécuté | Reste BT après exécution |
|-------|-------------------|--------------------------|
| 0‑3 | A | 5‑3 = 2 |
| 3‑6 | B | 2‑3 → terminé (reste 0) |
| 6‑9 | C | 8‑3 = 5 |
| 9‑12 | D | 3‑3 = 0 (terminé) |
| 12‑14 | A | 2‑2 = 0 (terminé) |
| 14‑17 | C | 5‑3 = 2 |
| 17‑19 | C | 2‑2 = 0 (terminé) |
- **Chronologie détaillée** (avec instants de début/fin) :
- A : 0‑3, 12‑14 → $CT_A = 14$ ms
- B : 3‑6 → $CT_B = 6$ ms
- C : 6‑9, 14‑17, 17‑19 → $CT_C = 19$ ms
- D : 9‑12 → $CT_D = 12$ ms
- **TAT = CT – AT** (AT=0) :
- $TAT_A = 14$ ms
- $TAT_B = 6$ ms
- $TAT_C = 19$ ms
- $TAT_D = 12$ ms
- **WT = TAT – BT** :
- $WT_A = 14-5 = 9$ ms
- $WT_B = 6-2 = 4$ ms
- $WT_C = 19-8 = 11$ ms
- $WT_D = 12-3 = 9$ ms
- **Moyennes** :
- $\overline{TAT} = (14+6+19+12)/4 = 51/4 = 12,75$ ms
- $\overline{WT} = (9+4+11+9)/4 = 33/4 = 8,25$ ms
**Comparaison** :
- FCFS donnait $\overline{TAT}=11,25$ ms, $\overline{WT}=6,75$ ms.
- RR avec $q=3$ ms augmente légèrement le TAT (+1,5 ms) et le WT (+1,5 ms) mais améliore le temps de réponse (le premier processus commence immédiatement, comme en FCFS, mais les processus courts comme B terminent plus tôt).
---
### Exercice 3 – Allocation mémoire First Fit
**Énoncé** : Une mémoire de 120 Mo est initialement libre sous la forme d’un seul trou [0‑120]. Les demandes d’allocation arrivent dans l’ordre suivant :
1. 30 Mo
2. 20 Mo
3. 45 Mo
4. 15 Mo
En utilisant l’algorithme **First Fit**, indiquez après chaque allocation la liste des trous libres restants.
**Corrigé** :
- **État initial** : trou libre = [0‑120].
1. **Allocation 30 Mo** : premier trou assez grand est [0‑120] → on alloue [0‑30].
- Trous libres : [30‑120].
2. **Allocation 20 Mo** : premier trou [30‑120] (taille 90 Mo) suffit → on alloue [30‑50].
- Trous libres : [50‑120].
3. **Allocation 45 Mo** : premier trou [50‑120] (taille 70 Mo) suffit → on alloue [50‑95].
- Trous libres : [95‑120].
4. **Allocation 15 Mo** : premier trou [95‑120] (taille 25 Mo) suffit → on alloue [95‑110].
- Trous libres : [110‑120].
**Résultat final** : un seul trou libre de 10 Mo situé à l’adresse [110‑120].
---
### Exercice 4 – Calcul du EAT avec pagination
**Énoncé** : Un système possède un temps d’accès à la mémoire RAM de $t_{mem}=120$ ns et un temps d’accès au disque (swap) de $t_{swap}=6$ ms. Le taux de page fault observé est $p=0,0005$ (0,05 %).
1. Calculer le temps d’accès effectif $EAT$.
2. Si l’on réduit la taille de page de 4 Ko à 2 Ko, le taux de page fault double (nouveau $p'=0,001$). Quel est le nouvel $EAT'$ ?
3. Quel est le pourcentage d’augmentation du temps d’accès moyen ?
**Corrigé** :
1. Conversion : $t_{swap}=6$ ms = $6\times10^{6}$ ns.
$$
EAT = (1-p)\,t_{mem} + p\,(t_{swap}+t_{mem})
$$
$$
= (1-0,0005)\times120 + 0,0005\times(6\,000\,000+120)
$$
$$
= 0,9995\times120 + 0,0005\times6\,000\,120
$$
$$
= 119,94 + 3\,000,06 = 3\,120,00\text{ ns} \approx 3,12\;\mu s
$$
2. Avec $p'=0,001$ :
$$
EAT' = (1-0,001)\times120 + 0,001\times(6\,000\,000+120)
$$
$$
= 0,999\times120 + 0,001\times6\,000\,120
$$
$$
= 119,88 + 6\,000,12 = 6\,120,00\text{ ns} \approx 6,12\;\mu s
$$
3. Augmentation :
$$
\frac{EAT' - EAT}{EAT}\times100 = \frac{6\,120 - 3\,120}{3\,120}\times100
= \frac{3\,000}{3\,120}\times100 \approx 96,15\%
$$
Le temps d’accès moyen augmente d’environ **96 %** lorsqu’on divise la taille de page par deux tout en doublant le taux de faute de page.
---
### Exercice 5 – Droits d’accès et `chmod`
**Énoncé** : Un fichier `script.sh` possède initialement les droits `-rw-r--r--` (644). L’utilisateur veut :
- Lui donner le droit d’exécution pour le propriétaire uniquement.
- Retirer le droit de lecture au groupe.
- Donner le droit de lecture et d’exécution aux autres.
Quelle commande `chmod` (en notation octale ou symbolique) réalise ces modifications ? Quel sera le nouveau mode affiché par `ls -l` ?
**Corrigé** :
- Droits initiaux : propriétaire `rw-` (6), groupe `r--` (4), autres `r--` (4) → 644.
- Modifications souhaitées :
- Propriétaire : ajouter `x` → `rwx` (7).
- Groupe : retirer `r` → `---` (0).
- Autres : ajouter `x` et garder `r` → `r-x` (5).
Nouveau mode octal : **705**.
En notation symbolique, on peut faire :
```bash
chmod u+x,g-r,o+rx script.sh
```
Vérification :
```bash
$ ls -l script.sh
-rwx-----x 1 user user 0 sept. 12 10:00 script.sh
```
En effet, le propriétaire a `rwx` (7), le groupe n’a aucun droit (`---` = 0), les autres ont `r-x` (5).
---
### Exercice 6 – Sémaphore binaire et section critique
**Énoncé** : Deux processus P1 et P2 exécutent le code suivant, où `mutex` est un sémaphore binaire initialisé à 1.
```c
void tâche() {
sem_wait(&mutex); // P
/* section critique */
compteur++;
/* fin section critique */
sem_post(&mutex); // V
}
```
1. Expliquez pourquoi cet usage garantit l’exclusion mutuelle.
2. Si l’on oublie l’appel à `sem_post` dans une branche d’erreur (par exemple après `if (compteur>100) { /* erreur */ return; }`), quel problème peut survenir ?
3. Proposez une correction utilisant le principe **RAII** (Resource Acquisition Is Initialization) en C++ avec un `std::lock_guard`.
**Corrigé** :
1. Le sémaphore binaire vaut 1 initialement. Le premier appel à `sem_wait` le décrémente à 0 et permet au processus d’entrer dans la section critique. Tout autre appel à `sem_wait` trouvera la valeur à 0, sera bloqué jusqu’à ce que le premier processus exécute `sem_post`, qui remet la valeur à 1 et réveille un éventuel processus en attente. Ainsi, au maximum un processus peut être dans la section critique à la fois.
2. Si une branche d’erreur retourne sans exécuter `sem_post`, le sémaphore reste à 0. Tous les appels subséquents à `sem_wait` seront bloqués indéfiniment → **deadlock** (verrouillage mortel). Le compteur ne pourra plus être incrémenté, et le système peut se bloquer.
3. En C++11, on encapsule le sémaphore dans un objet qui libère automatiquement la ressource lors de la sortie du bloc :
```cpp
#include <semaphore>
#include <iostream>
std::binary_semaphore mutex{1}; // C++20, sinon utiliser std::mutex + lock_guard
int compteur = 0;
void tâche() {
std::lock_guard<std::mutex> guard(mutex_mutex); // si on utilise std::mutex
// section critique
++compteur;
if (compteur > 100) {
// gestion d'erreur : le lock_guard sera détruit automatiquement à la sortie du bloc
return;
}
// fin de la section critique, le guard libère le mutex automatiquement
}
```
Avec `std::lock_guard`, le mutex est libéré dès que l’objet `guard` sort de sa portée, même en cas de retour prématuré ou d’exception, évitant ainsi le deadlock.
---
## Erreurs fréquentes
| # | Erreur classique | Pourquoi elle se produit | Comment l’éviter |
|---|------------------|--------------------------|------------------|
| 1 | **Confondre temps d’attente (WT) et temps de réponse (RT)** | Le WT mesure le temps passé prêt mais non exécuté ; le RT mesure le délai entre l’arrivée et le premier démarrage. | Toujours rappeler les définitions : WT = temps passé dans la file prête ; RT = temps jusqu’au premier coup de CPU. |
| 2 | **Oublier de libérer un sémaphore (ou un mutex) dans toutes les voies de sortie** | Une branche d’erreur ou un retour prématuré laisse le verrou bloqué → deadlock. | Utiliser des mécanismes de libération automatique (`lock_guard` en C++, `try/finally` en Python, ou `defer` en Go). |
| 3 | **Appliquer un algorithme d’allocation de mémoire inadapté (ex. : Best Fit sur une mémoire fortement fragmentée)** | Best Fit peut créer de très petits trous inutilisables, aggravant la fragmentation externe. | Analyser la charge de travail ; si la fragmentation est un problème, privilégier First Fit ou utiliser la pagination. |
| 4 | **Confondre les droits octaux et symboliques lors de l’usage de `chmod`** | Une erreur de traduction (ex. : `chmod 755` au lieu de `chmod u=rwx,g=rx,o=rx`) peut donner des droits inattendus. | Toujours vérifier le résultat avec `ls -l` après modification ; s’entraîner à convertir entre les deux notations. |
| 5 | **Ne pas prendre en compte le coût d’un contexte de changement lors du calcul du nombre de switches en RR** | On compte seulement les intervalles d’exécution, oubliant que chaque préemption implique un sauvetage/restauration du contexte. | Rappeler que le nombre de switches ≈ nombre de préemptions = $\lceil BT/q \rceil - 1$ par processus, puis ajouter le switch initial si nécessaire. |
| 6 | **Utiliser un appel système sans vérifier son code de retour** | Un `open` qui échoue retourne -1 ; ignorer cela mène à des lectures/écritures sur un descripteur invalide → segmentation fault ou données corrompues. | Toujours tester la valeur de retour des appels système et gérer l’erreur (ex. : `if (fd==-1) { perror("open"); exit(EXIT_FAILURE); }`). |
---
**Fin du cours**. Vous disposez maintenant d’une vue complète et approfondie du système d’exploitation, prête à être étudiée seul ou en classe. Bon travail et succès au Probatoire !
Quiz du chapitre
5 questions — vérifie chaque réponse pour voir l'explication.