Systèmes d'Exploitation — Fondamentaux
Un système d’exploitation (OS) est un ensemble de logiciels qui gèrent les ressources matérielles d’un ordinateur et fournissent des services aux programmes applicatifs. C’est l’intermédiaire entre le matériel et les applications.
Rôles d’un OS§
Abstraction du matériel : l’OS cache la complexité du matériel derrière des interfaces standardisées. Un programme n’écrit pas directement dans les registres du disque ; il appelle write(). L’OS traduit cet appel en instructions matérielles spécifiques à chaque périphérique.
Gestion des ressources : l’OS alloue et arbitre l’accès au processeur, à la mémoire, aux périphériques. Sans OS, plusieurs programmes ne pourraient pas s’exécuter simultanément sans se corrompre mutuellement.
Interface utilisateur : CLI (shell) ou GUI (interface graphique). Sous Linux, le shell (bash, zsh, fish) est le principal point d’entrée.
Sécurité et protection : isolation entre les processus, contrôle d’accès aux fichiers et périphériques, gestion des permissions.
Historique§
| Période | Événement |
|---|---|
| 1950s | Pas d’OS — les programmes contrôlaient directement le matériel |
| 1960s | Batch processing, premiers OS (IBM OS/360) |
| 1969 | Unix créé par Thompson et Ritchie aux Bell Labs (AT&T) |
| 1971 | Premier Unix distribué, codé en assembleur |
| 1973 | Unix réécrit en C — portabilité révolutionnaire |
| 1983 | Richard Stallman lance le projet GNU (OS libre) |
| 1985 | Windows 1.0 (Microsoft) |
| 1991 | Linus Torvalds publie le noyau Linux 0.01 |
| 1994 | Linux 1.0 — utilisable en production |
| 2003 | macOS X stable (noyau Darwin, issu de BSD/Mach) |
| 2008 | Android (noyau Linux) pour mobile |
Noyau (Kernel)§
Le noyau est le cœur de l’OS. Il s’exécute en mode privilégié (mode noyau / ring 0) avec accès total au matériel.
Types de noyaux§
Noyau monolithique : tout le code OS s’exécute dans un seul espace d’adressage en mode noyau. Drivers, gestion mémoire, gestion de processus — tout est intégré. Exemples : Linux, Windows NT.
Avantages : communication interne rapide (appels de fonctions directs), performances élevées. Inconvénients : un bug dans un driver peut planter tout le système.
Micro-noyau : seul le minimum absolu (communication inter-processus, scheduling de base, gestion mémoire basique) est dans le noyau. Le reste (drivers, système de fichiers) tourne en espace utilisateur. Exemples : Mach, QNX, GNU Hurd.
Avantages : meilleure isolation, plus sûr, plus facile à déboguer. Inconvénients : performances inférieures (IPC plus coûteux que les appels de fonctions).
Noyau hybride : compromis entre monolithique et micro-noyau. Certains services tournent en mode noyau pour les performances, d’autres en mode utilisateur pour l’isolation. Exemples : Windows NT, macOS (XNU).
Mode utilisateur vs mode noyau§
| Mode | Accès matériel | Instructions privilégiées | Exemples |
|---|---|---|---|
| Mode noyau (ring 0) | Total | Toutes disponibles | Noyau Linux, drivers |
| Mode utilisateur (ring 3) | Aucun direct | Interdites | Applications, shells |
Un programme en mode utilisateur ne peut pas accéder directement à la mémoire d’un autre programme ou au matériel. Il doit passer par le noyau via des appels système.
Appels système (Syscalls)§
Un appel système est l’interface entre un programme en mode utilisateur et le noyau. C’est la seule façon légale d’accéder aux ressources matérielles.
# Tracer les syscalls d'un programme avec strace (Linux)
strace ls -la /tmp
# Exemples de syscalls courants
# read(fd, buf, count) — lire depuis un descripteur de fichier
# write(fd, buf, count) — écrire dans un descripteur de fichier
# open(path, flags) — ouvrir un fichier
# fork() — créer un processus fils
# exec(path, argv, envp) — remplacer l'image du processus
# exit(status) — terminer le processus
# mmap(addr, len, ...) — mapper de la mémoire
# socket(domain, type, ...) — créer un socket réseau
Mécanisme d’un syscall :
- L’application prépare les arguments et place le numéro du syscall dans un registre
- Elle déclenche une interruption logicielle (
int 0x80sur x86,syscallsur x86-64) - Le CPU passe en mode noyau et saute au gestionnaire d’interruption
- Le noyau exécute le service demandé
- Le résultat est retourné, le CPU repasse en mode utilisateur
Distributions Linux§
Linux est le noyau ; une distribution (distro) combine le noyau avec des outils GNU, un gestionnaire de paquets, une interface et des logiciels.
| Distribution | Base | Gestionnaire de paquets | Usage |
|---|---|---|---|
| Ubuntu | Debian | APT (deb) | Grand public, serveurs |
| Debian | — | APT (deb) | Stabilité, serveurs |
| Fedora | Red Hat | DNF (rpm) | Développeurs, nouveautés |
| RHEL / CentOS Stream | Red Hat | DNF (rpm) | Entreprise |
| Arch Linux | — | Pacman | Avancés, rolling release |
| Alpine | — | APK | Conteneurs (très léger ~5 Mo) |
| Kali Linux | Debian | APT | Sécurité, pentest |
Architecture générale d’un OS§
Applications (firefox, vim, python...)
│
Bibliothèques standard (glibc, libc)
│
Interface syscalls (POSIX)
│
NOYAU
┌──────────────────────────┐
│ Scheduler (ordonnanceur) │
│ Gestionnaire mémoire │
│ Système de fichiers VFS │
│ Pile réseau │
│ Drivers (matériel) │
└──────────────────────────┘
│
Matériel (CPU, RAM, disques, réseau...)
Processus de démarrage (Boot)§
Le démarrage d’un ordinateur suit une séquence précise avant que l’OS ne soit utilisable.
1. Mise sous tension
2. BIOS/UEFI — POST (Power-On Self Test)
3. BIOS/UEFI cherche un périphérique amorçable
4. Chargement du MBR (512 octets au début du disque)
5. MBR charge le bootloader (GRUB, rEFInd, LILO)
6. Bootloader charge le noyau OS en RAM
7. Le noyau initialise les périphériques et monte le système de fichiers
8. Init / systemd démarre les services
9. Interface de connexion (login)
BIOS vs UEFI§
| Aspect | BIOS | UEFI |
|---|---|---|
| Firmware | 16 bits, IBM PC | 32/64 bits, moderne |
| Interface | Texte uniquement | Graphique possible |
| Partition | MBR (max 2 To, 4 partitions) | GPT (max 9,4 Zo, 128 partitions) |
| Sécurité | Aucune | Secure Boot |
| POST | Lent | Plus rapide |
MBR — Master Boot Record§
Secteur de 512 octets en début de disque amorçable. Contient :
- 446 octets de code bootloader de premier niveau
- 64 octets de table de partitions (4 entrées de 16 octets)
- 2 octets de signature (0x55AA)
Le BIOS charge le MBR à l’adresse mémoire 0x7C00 et lui transfère le contrôle.
Composants du bootloader (GRUB)§
Le bootloader est divisé en deux étapes :
- Premier niveau (first-stage) : code minimal dans le MBR, charge le second niveau
- Second niveau (GRUB) : interface de sélection du noyau, prise en charge des systèmes de fichiers
Composants d’un OS§
| Composant | Rôle |
|---|---|
| Kernel | Gère le matériel, les processus, la mémoire |
| Bibliothèques système | Fonctions réutilisables (GNU C Library, glibc) |
| Shell / CLI | Interface textuelle (bash, zsh) |
| GUI | Interface graphique (GNOME, KDE, Windows Explorer) |
| Système de fichiers | Organisation du stockage (ext4, NTFS, APFS) |
| Drivers | Communication avec le matériel |
| Applications | Logiciels utilisateur |
Machines virtuelles et conteneurs§
| Technologie | Isolation | Overhead | Usage |
|---|---|---|---|
| Bare metal | Aucune | Aucun | Production haute performance |
| VM (VirtualBox, VMware, Hyper-V) | Forte (OS complet) | Élevé | Tests multi-OS, isolation totale |
| Conteneur (Docker) | Moyenne (partage le noyau) | Faible | Déploiement applicatif, CI/CD |
VM : émule un matériel complet, chaque VM a son propre noyau. Bonne isolation mais consommation élevée.
Docker : partage le noyau de l’hôte, isole uniquement les processus et le système de fichiers. Démarrage en secondes, images légères.
Commandes essentielles Linux§
# Informations système
uname -a # version du noyau et architecture
hostnamectl # informations sur le système
lscpu # informations sur le processeur
free -h # utilisation de la mémoire
df -h # espace disque par partition
uptime # temps de fonctionnement et charge moyenne
# Gestion des processus
ps aux # liste tous les processus
top # moniteur de processus interactif
htop # version améliorée de top
kill -9 PID # terminer un processus de force (SIGKILL)
killall nom # terminer tous les processus par nom
nice -n 10 cmd # lancer une commande avec priorité réduite
# Informations réseau
ip addr # interfaces et adresses IP
ss -tunlp # sockets ouverts (remplace netstat)
Parsing d’entrées en C§
Le parsing d’entrées est fondamental dans les programmes OS. Il s’agit de lire et analyser des données depuis stdin, des arguments en ligne de commande, ou des fichiers.
Arguments de la ligne de commande§
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// argc : nombre d'arguments (program = argv[0])
// argv : tableau de chaînes de caractères
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <fichier> [options]\n", argv[0]);
return EXIT_FAILURE;
}
// Parcourir les arguments
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
printf("Mode verbeux activé\n");
} else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
int n = atoi(argv[++i]); // Argument suivant
printf("N = %d\n", n);
} else {
printf("Fichier : %s\n", argv[i]);
}
}
return EXIT_SUCCESS;
}
Parsing avec getopt§
getopt est la fonction POSIX standard pour analyser les options.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int opt;
int verbeux = 0;
char *fichier = NULL;
// ':' après une lettre = argument obligatoire
while ((opt = getopt(argc, argv, "vf:n:h")) != -1) {
switch (opt) {
case 'v': verbeux = 1; break;
case 'f': fichier = optarg; break;
case 'n': printf("N = %d\n", atoi(optarg)); break;
case 'h':
printf("Usage: %s [-v] [-f fichier] [-n nombre]\n", argv[0]);
return 0;
default:
fprintf(stderr, "Option inconnue\n");
return EXIT_FAILURE;
}
}
// Arguments restants (non-options) : argv[optind] à argv[argc-1]
for (int i = optind; i < argc; i++) {
printf("Argument : %s\n", argv[i]);
}
return 0;
}
Lecture depuis stdin§
#include <stdio.h>
#include <string.h>
// Lire ligne par ligne
char ligne[1024];
while (fgets(ligne, sizeof(ligne), stdin) != NULL) {
// Supprimer le \n final
ligne[strcspn(ligne, "\n")] = '\0';
printf("Lu : '%s'\n", ligne);
}
// Lire mot par mot avec scanf
int nombre;
char mot[256];
while (scanf("%d %255s", &nombre, mot) == 2) {
printf("%d -> %s\n", nombre, mot);
}
// Détecter EOF
if (feof(stdin)) printf("Fin de stdin\n");
Parsing de fichiers structurés (CSV)§
#include <stdio.h>
#include <string.h>
void parse_csv(FILE *f) {
char ligne[4096];
while (fgets(ligne, sizeof(ligne), f)) {
ligne[strcspn(ligne, "\n")] = '\0';
char *token = strtok(ligne, ","); // Découper par virgule
while (token != NULL) {
// Supprimer les espaces en début/fin (trim)
while (*token == ' ') token++;
printf("Champ: '%s'\n", token);
token = strtok(NULL, ",");
}
}
}
Parsing en shell (bash)§
# Analyser des options en shell
while [[ "$#" -gt 0 ]]; do
case $1 in
-v|--verbose) verbose=1 ;;
-f|--fichier) fichier="$2"; shift ;;
-n|--nombre) nombre="$2"; shift ;;
-h|--help) echo "Usage: $0 [-v] [-f fichier]"; exit 0 ;;
*) echo "Option inconnue: $1"; exit 1 ;;
esac
shift
done
# Lire stdin ligne par ligne en shell
while IFS= read -r ligne; do
echo "Ligne: $ligne"
done < fichier.txt
# Ou depuis une commande
commande | while IFS= read -r ligne; do
echo "Output: $ligne"
done
# Découper une chaîne
IFS=',' read -ra champs <<< "alice,bob,charlie"
for champ in "${champs[@]}"; do
echo "$champ"
done