Garden of KnowledgeApplied Sciences › Computer Science › Software › Systèmes d'Exploitation
February 24, 2026

Processus et Threads

Processus§

Un processus est une instance d’un programme en cours d’exécution. C’est l’unité d’allocation de ressources de l’OS.

Composition d’un processus§

Un processus comprend :

PCB — Process Control Block§

Structure de données maintenue par le noyau pour chaque processus. Contient :

ChampContenu
PIDIdentifiant unique du processus
PPIDPID du processus parent
ÉtatNouveau / Prêt / En cours / Bloqué / Terminé
Compteur programmeAdresse de la prochaine instruction
Registres CPUSauvegardés lors d’un changement de contexte
Informations mémoireTables de pages, limites de segments
Liste des fichiers ouvertsDescripteurs de fichiers
Informations de schedulingPriorité, temps CPU consommé
Informations de sécuritéUID, GID, capacités

États d’un processus§

         fork()


        NOUVEAU
           │ admitted

         PRÊT ◄──────────── interrupt / I/O complète

    scheduler dispatch


       EN COURS ───── I/O ou événement ──► BLOQUÉ

           │ exit()

        TERMINÉ

Création de processus (Unix)§

#include <unistd.h>

pid_t pid = fork();  // Crée une copie exacte du processus

if (pid < 0) {
    // Erreur
} else if (pid == 0) {
    // Code exécuté par le processus fils
    execl("/bin/ls", "ls", "-la", NULL);  // Remplace l'image du processus
} else {
    // Code exécuté par le processus parent
    // pid contient l'ID du fils
    wait(NULL);  // Attendre la fin du fils
}

fork() crée un processus fils qui est une copie du parent (Copy-On-Write). exec() remplace l’image du processus par un nouveau programme. La séquence fork() + exec() est le mécanisme standard de création de processus sous Unix.

Communication inter-processus (IPC)§

MécanismeDescriptionUsage
PipesCanal unidirectionnel, lié au cycle de viels | grep .py
Named Pipes (FIFO)Pipe persistant dans le système de fichiersProcessus sans lien de parenté
SignauxNotifications asynchronesSIGTERM, SIGKILL, SIGHUP
Shared MemoryZone mémoire partagéeÉchanges rapides de données
SemaphoresSynchronisation entre processusAccès exclusif à une ressource
SocketsCommunication réseau ou locale (Unix sockets)Client-serveur
Message QueuesFiles de messages structurésÉchanges asynchrones

Threads§

Un thread (fil d’exécution) est la plus petite unité d’exécution schedulable. Plusieurs threads s’exécutent dans le même processus et partagent ses ressources.

Processus vs Thread§

DimensionProcessusThread
Espace mémoireSéparé (isolation totale)Partagé au sein du processus
CréationLente (copie des ressources)Rapide (quelques registres)
CommunicationVia IPC (coûteux)Via mémoire partagée (direct)
IsolationForteFaible (un thread peut corrompre les autres)
CoûtÉlevéFaible
ParallélismeMulti-CPU naturelMulti-CPU selon le modèle

Ce que partage un thread§

Un thread partage avec les autres threads du même processus :

Un thread possède en propre :

Modèles de threading§

1:1 (Kernel-level threads) : chaque thread utilisateur correspond à un thread noyau. Parallélisme réel sur multi-cœurs. Contexte switch coûteux (implique le noyau). Modèle de Linux/Windows.

N:1 (User-level threads) : plusieurs threads utilisateur mappés sur un seul thread noyau (bibliothèque en espace utilisateur). Pas de parallélisme réel. Context switch très rapide. Problème : un appel système bloquant bloque tous les threads.

M:N (Hybrid) : M threads utilisateur mappés sur N threads noyau (N < M). Compromis. Utilisé par Go (goroutines), Haskell (green threads). Complexe à implémenter.

Threads en Python§

import threading
import time

def tache(nom, duree):
    print(f"Thread {nom} démarre")
    time.sleep(duree)
    print(f"Thread {nom} termine")

# Créer des threads
t1 = threading.Thread(target=tache, args=("A", 2))
t2 = threading.Thread(target=tache, args=("B", 1))

t1.start()
t2.start()

t1.join()  # Attendre la fin de t1
t2.join()  # Attendre la fin de t2
print("Tous les threads ont terminé")

# Thread avec héritage
class MonThread(threading.Thread):
    def __init__(self, nom):
        super().__init__()
        self.nom = nom

    def run(self):
        print(f"Thread {self.nom} en cours")

# GIL (Global Interpreter Lock) en Python
# Le GIL empêche deux threads Python d'exécuter du bytecode simultanément.
# Conséquence : les threads Python n'apportent pas de parallélisme pour le CPU-bound.
# Pour le parallélisme CPU : utiliser multiprocessing.
# Les threads sont utiles pour l'I/O-bound (réseau, disque).

Threads avec partage de ressources§

import threading

compteur = 0
verrou = threading.Lock()

def incrementer(n):
    global compteur
    for _ in range(n):
        with verrou:  # Acquisition et libération automatique
            compteur += 1

threads = [threading.Thread(target=incrementer, args=(10000,)) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(f"Compteur final : {compteur}")  # 100000 (garanti avec le verrou)

Ordonnancement (Scheduling)§

Le scheduler décide quel processus/thread s’exécute sur quel cœur à quel moment.

Critères d’évaluation§

Algorithmes§

FCFS — First Come First Served : les processus sont exécutés dans l’ordre d’arrivée. Simple mais sujet à l’effet convoi (un long processus bloque tous les suivants).

SJF — Shortest Job First : exécuter d’abord le processus avec le temps CPU le plus court estimé. Optimal pour minimiser le temps d’attente moyen. Problème : nécessite de connaître le temps d’exécution à l’avance.

SRTF — Shortest Remaining Time First : version préemptive de SJF. Un nouveau processus plus court préempte le processus en cours.

Round Robin : chaque processus reçoit un quantum de temps fixe (10-100 ms). Si non terminé, il retourne à la fin de la file. Adapté aux systèmes interactifs.

Processus : P1(24), P2(3), P3(3)  —  quantum = 4

Gantt chart :
P1(0-4) | P2(4-7) | P3(7-10) | P1(10-14) | P1(14-18) | P1(18-22) | P1(22-24)

Schedulers à priorités : chaque processus a une priorité. Le processus de plus haute priorité s’exécute en premier. Risque de famine (starvation) pour les processus de basse priorité.

Files multiniveaux avec rétroaction (MLFQ) : plusieurs files de priorités. Les processus commencent en haute priorité et descendent s’ils consomment tout leur quantum. Les I/O-bound remontent. Simule le comportement de SJF sans connaître les temps d’exécution à l’avance. Utilisé par Linux (CFS — Completely Fair Scheduler).

CFS — Completely Fair Scheduler (Linux)§

Le scheduler par défaut de Linux depuis 2.6.23. Chaque processus reçoit une fraction équitable du CPU proportionnelle à sa priorité (nice value). Utilise un arbre rouge-noir ordonné par “virtual runtime” pour sélectionner le prochain processus en O(log n).

# Modifier la priorité d'un processus (nice : -20 à +19)
nice -n 10 commande         # lancer avec nice 10
renice -n 5 -p 1234         # changer le nice d'un processus existant
ps -o pid,ni,comm -p 1234   # voir le nice value

Changement de contexte§

Lors d’un changement de contexte, le noyau :

  1. Sauvegarde l’état du processus sortant dans son PCB (registres, compteur programme)
  2. Met à jour les structures de données de scheduling
  3. Charge l’état du processus entrant depuis son PCB
  4. Passe au nouveau processus

Coût : 1-10 µs. À éviter pour les workloads très latence-sensitifs (c’est pourquoi les event loops (Node.js, asyncio) peuvent être plus efficaces que les threads pour l’I/O).

Signaux Unix§

Un signal est une forme d’interruption logicielle envoyée à un processus. Le noyau utilise les signaux pour notifier des événements ou des conditions exceptionnelles.

Signaux courants§

SignalNuméroDescriptionAction par défaut
SIGINT2Interruption clavier (Ctrl+C)Terminer
SIGTERM15Demande de terminaison (gracieuse)Terminer
SIGKILL9Terminaison forcée (non interceptable)Terminer
SIGSEGV11Violation de segmentation mémoireCore dump + terminer
SIGHUP1Déconnexion du terminalTerminer
SIGALRM14Alarme timer (alarm())Terminer
SIGCHLD17Changement d’état d’un filsIgnorer
SIGPIPE13Écriture dans un pipe sans lecteurTerminer
SIGUSR1/210/12Signaux utilisateur définisTerminer

Signaux standard vs temps réel§

CritèreStandardTemps réel (SIGRTMIN-SIGRTMAX)
File d’attenteNon (un seul signal en attente)Oui (plusieurs instances)
Données additionnellesNonOui (via sigqueue())
Ordre garantiNon spécifiéGaranti par numéro
PrioritéPlus haute que temps réel (Linux)Par numéro de signal

Gestion des signaux en C§

#include <signal.h>
#include <stdio.h>

// Gestionnaire de signal
void gestionnaire(int sig) {
    printf("Signal %d reçu\n", sig);
}

int main() {
    // Enregistrer un gestionnaire pour SIGINT
    signal(SIGINT, gestionnaire);

    // Méthode recommandée : sigaction (plus précis)
    struct sigaction sa;
    sa.sa_handler = gestionnaire;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;  // Redémarrer les appels système interrompus
    sigaction(SIGTERM, &sa, NULL);

    // Envoyer un signal à un processus
    kill(pid, SIGTERM);

    // Attendre un signal
    pause();  // Suspendre jusqu'au prochain signal

    return 0;
}

Pipes — Communication inter-processus§

Un pipe est un mécanisme IPC permettant de connecter la sortie d’un processus à l’entrée d’un autre, sans fichier intermédiaire.

Types de pipes :

TypeNomCommunicationUsage
Unnamed pipepipe()UnidirectionnelParent-enfant uniquement
Named pipemkfifo() (FIFO)Uni ou bidirectionnelProcessus sans lien de parenté
#include <unistd.h>

int fd[2];
pipe(fd);  // fd[0] = lecture, fd[1] = écriture

pid_t pid = fork();
if (pid == 0) {
    // Processus fils : lit depuis le pipe
    close(fd[1]);                         // Fermer l'extrémité écriture
    read(fd[0], buffer, sizeof(buffer));
} else {
    // Processus parent : écrit dans le pipe
    close(fd[0]);                         // Fermer l'extrémité lecture
    write(fd[1], message, strlen(message) + 1);
}
# Pipe shell : stdout de ls devient stdin de grep
ls -la | grep ".md"

# Named pipe
mkfifo /tmp/mon_pipe
cat /tmp/mon_pipe &   # lecteur en arrière-plan
echo "données" > /tmp/mon_pipe  # écrivain

Appels système§

Les appels système sont l’interface entre les processus utilisateur et le noyau.

CatégorieExemples
Contrôle de processusfork(), exec(), wait(), exit(), kill()
Gestion de fichiersopen(), read(), write(), close(), lseek()
Gestion des signauxsignal(), sigaction(), kill(), pause()
IPCpipe(), mkfifo(), shmget(), msgget()
Informationgetpid(), getppid(), time()
—The Gardener