Processus et jobs#
Tout programme en cours d’exécution sur un système Linux est un processus. Comprendre comment les processus naissent, communiquent, et meurent est une compétence fondamentale pour tout administrateur système ou développeur travaillant sous Linux. Bash offre un ensemble complet d’outils pour inspecter l’état du système, envoyer des signaux à des processus, contrôler l’exécution en arrière-plan et ajuster les priorités d’ordonnancement.
Ce chapitre couvre l’ensemble du cycle de vie d’un processus : de sa création via l’appel système fork jusqu’à sa terminaison, en passant par la gestion des signaux Unix, le contrôle des jobs interactifs, le système de fichiers virtuel /proc, et les outils de surveillance en temps réel.
Qu’est-ce qu’un processus ?#
Définition 37 (Processus)
Un processus est une instance d’un programme en cours d’exécution. Il est caractérisé par :
Un PID (Process IDentifier) : numéro unique attribué par le noyau à la création du processus.
Un PPID (Parent Process IDentifier) : le PID du processus qui l’a créé (son parent).
Un état : running, sleeping, stopped, zombie, etc.
Un espace d’adressage : la mémoire virtuelle allouée au processus.
Des descripteurs de fichiers : les fichiers et sockets ouverts.
Des informations d’identité : UID, GID, groupes supplémentaires.
Création d’un processus : fork et exec#
Sous Linux, tout processus (sauf le processus initial) est créé par un appel fork() qui duplique un processus existant. Le processus fils obtient une copie de l’espace d’adressage de son parent. Puis, dans la majorité des cas, le fils appelle execve() pour remplacer son image mémoire par un nouveau programme. C’est le modèle fork-exec.
Remarque 31
Le processus numéro 1 — systemd sur les distributions modernes, autrefois init — est le père de tous les processus sur le système. C’est le premier processus lancé par le noyau au démarrage. Tout processus créé par la suite est un descendant, direct ou indirect, de ce processus initial. Lorsqu’un processus se termine, son parent doit lire son code de retour via wait() ; s’il ne le fait pas, le processus reste dans un état zombie (Z) dans la table des processus jusqu’à ce que le parent appelle wait().
États d’un processus#
État |
Code |
Signification |
|---|---|---|
Running |
R |
En cours d’exécution ou prêt à être schedulé |
Sleeping interruptible |
S |
En attente d’un événement (E/S, signal) |
Sleeping uninterruptible |
D |
Attente d’E/S disque non interruptible |
Stopped |
T |
Suspendu par un signal (SIGSTOP, SIGTSTP) |
Zombie |
Z |
Terminé, mais le parent n’a pas encore lu le code retour |
Dead |
X |
Mort (rarement visible) |
La commande ps#
ps (process status) est l’outil classique pour photographier l’état des processus à un instant donné.
# Processus du terminal courant (minimaliste)
ps
# Tous les processus du système, format BSD
ps aux
# Tous les processus, format POSIX complet
ps -ef
# Tous les processus avec threads
ps -eLf
# Processus d'un utilisateur spécifique
ps -u alice
ps -u 1000
# Processus par nom
ps -C firefox
ps -C sshd
# Format personnalisé
ps -eo pid,ppid,user,%cpu,%mem,stat,cmd --sort=-%cpu | head -20
# Processus dans une arborescence
ps -ejH # Hiérarchie
ps axjf # Format forêt (ASCII art)
# Filtrer et trier
ps aux --sort=-%mem | head -10 # Top 10 par mémoire
ps aux --sort=-%cpu | head -10 # Top 10 par CPU
```{prf:definition} Colonnes de ps aux
:label: definition-12-02
Les colonnes principales de ps aux sont :
USER : propriétaire du processus
PID : identifiant du processus
%CPU : pourcentage du temps CPU utilisé
%MEM : pourcentage de la RAM physique utilisée
VSZ : mémoire virtuelle totale allouée (en Ko)
RSS : mémoire résidente réelle (Resident Set Size, en Ko) — la mémoire physique effectivement utilisée
TTY : terminal de contrôle (
?si aucun)STAT : état du processus (R, S, D, T, Z, X, plus modificateurs s, l, +, <, N)
START : heure ou date de démarrage
TIME : temps CPU cumulé consommé
COMMAND : commande complète avec arguments
## `top` et `htop` : surveillance en temps réel
Contrairement à `ps` qui prend un instantané, `top` et `htop` affichent les processus en temps réel avec mise à jour périodique.
### top
```bash
# Lancer top
top
# Options utiles au lancement
top -u alice # N'afficher que les processus d'alice
top -p 1234,5678 # Surveiller uniquement les PIDs spécifiés
top -b -n 1 # Mode batch, une seule itération (pour les scripts)
top -d 0.5 # Rafraîchissement toutes les 0,5 secondes
Raccourcis interactifs dans top :
Touche |
Action |
|---|---|
|
Quitter |
|
Tuer un processus (demande le PID puis le signal) |
|
Renice (changer la priorité) |
|
Filtrer par utilisateur |
|
Trier par utilisation mémoire |
|
Trier par utilisation CPU |
|
Trier par temps CPU cumulé |
|
Développer chaque CPU séparément |
|
Afficher/masquer les threads |
|
Sélectionner les colonnes affichées |
htop#
htop est une version améliorée de top avec une interface en couleurs, une navigation au clavier intuitive et la possibilité de sélectionner des processus par arborescence.
# Installation si nécessaire
sudo apt install htop
# Lancer htop
htop
# Options
htop -u alice # Processus d'alice seulement
htop -p 1234,5678 # PIDs spécifiques
htop -d 5 # Délai de 0,5 secondes (en dixièmes)
Raccourcis htop :
Touche |
Action |
|---|---|
|
Configuration (colonnes, couleurs) |
|
Rechercher un processus |
|
Filtrer par texte |
|
Vue arborescente |
|
Choisir le tri |
|
Envoyer un signal |
|
Quitter |
|
Marquer un processus |
|
Filtrer par utilisateur |
Signaux Unix#
Les signaux sont des notifications asynchrones envoyées à un processus pour lui indiquer qu’un événement s’est produit. Chaque signal a un numéro et un nom conventionnel.
Définition 38 (Signal Unix)
Un signal est un mécanisme de communication interprocessus qui permet d’envoyer une notification asynchrone à un processus. Chaque signal a un comportement par défaut (souvent terminer le processus, le suspendre, ou l’ignorer), mais un processus peut intercepter la plupart des signaux et définir son propre gestionnaire, sauf SIGKILL et SIGSTOP qui ne peuvent ni être interceptés ni ignorés.
Principaux signaux#
Signal |
Numéro |
Comportement par défaut |
Usage |
|---|---|---|---|
|
1 |
Terminer |
Recharger la configuration (démons) |
|
2 |
Terminer |
Ctrl+C depuis le terminal |
|
3 |
Core dump |
Ctrl+\ depuis le terminal |
|
9 |
Terminer (fatal) |
Terminaison forcée, non interceptable |
|
15 |
Terminer |
Terminaison gracieuse (défaut de |
|
19 |
Suspendre (fatal) |
Suspension, non interceptable |
|
20 |
Suspendre |
Ctrl+Z depuis le terminal |
|
18 |
Reprendre |
Reprendre un processus suspendu |
|
10 |
Terminer |
Signal utilisateur personnalisé |
|
12 |
Terminer |
Signal utilisateur personnalisé |
|
17 |
Ignorer |
Notification parent : fils terminé |
|
13 |
Terminer |
Écriture sur pipe sans lecteur |
|
14 |
Terminer |
Timer (alarm()) expiré |
kill, pkill, killall#
# kill : envoyer un signal à un PID
kill 1234 # Envoie SIGTERM (15) par défaut
kill -9 1234 # Envoie SIGKILL : force la terminaison
kill -SIGTERM 1234 # Équivalent, forme symbolique
kill -15 1234 # Équivalent, forme numérique
kill -SIGHUP 1234 # Demander rechargement de configuration
# Envoyer un signal à plusieurs processus
kill -9 1234 5678 9012
# Lister tous les signaux disponibles
kill -l
# pkill : envoyer un signal par nom de processus
pkill firefox # Terminer tous les processus "firefox"
pkill -9 firefox # Forcer la terminaison
pkill -u alice # Terminer tous les processus d'alice
pkill -f "python script.py" # Correspondance sur la ligne de commande complète
# killall : similaire à pkill
killall firefox
killall -9 nginx
killall -HUP sshd # Recharger la config de sshd
# Vérifier si un processus existe
kill -0 1234 2>/dev/null && echo "Processus en vie" || echo "Processus mort"
Remarque 32
La différence entre SIGTERM (15) et SIGKILL (9) est fondamentale. SIGTERM est une demande gracieuse de terminaison : le processus peut l’intercepter, nettoyer ses ressources (fermer les fichiers, sauvegarder l’état, terminer les connexions) et s’arrêter proprement. SIGKILL est une terminaison forcée par le noyau : le processus n’a aucune opportunité de se nettoyer. On doit toujours essayer SIGTERM en premier, attendre quelques secondes, puis seulement recourir à SIGKILL si le processus ne répond pas.
Jobs en arrière-plan#
Le shell Bash gère le concept de jobs : des pipelines ou commandes lancés depuis le terminal, pouvant s’exécuter en premier plan (foreground) ou en arrière-plan (background).
Lancer une commande en arrière-plan#
# Ajouter & à la fin pour lancer en arrière-plan
sleep 100 &
[1] 12345 # [numéro_job] PID
# Plusieurs commandes en arrière-plan
wget https://exemple.fr/fichier1.iso &
wget https://exemple.fr/fichier2.iso &
# La commande rend la main immédiatement
echo "Le shell est libre pendant que le téléchargement tourne"
jobs, fg, bg#
# Lancer plusieurs jobs en arrière-plan
sleep 200 & # Job [1]
sleep 300 & # Job [2]
sleep 400 & # Job [3]
# Lister les jobs en cours
jobs
# [1] Running sleep 200 &
# [2]- Running sleep 300 &
# [3]+ Running sleep 400 &
# + = job courant (le plus récent)
# - = job précédent
# Afficher PIDs
jobs -l
# [1] 12345 Running sleep 200 &
# Ramener un job au premier plan
fg %1 # Job numéro 1
fg %2 # Job numéro 2
fg # Le job courant (marqué +)
# Suspendre un job au premier plan avec Ctrl+Z, puis l'envoyer en arrière-plan
# Ctrl+Z donne : [1]+ Stopped sleep 200
bg %1 # Le job reprend en arrière-plan
# Arrêter un job en arrière-plan
kill %1 # Envoie SIGTERM au job 1
kill %2 %3
Exemple 23 (Workflow interactif typique)
Un workflow courant lors d’une session de travail intensive :
# Démarrer une longue compilation en arrière-plan
make all 2>&1 | tee build.log &
echo "Compilation lancée en arrière-plan, PID=$!"
# Travailler pendant ce temps
vim src/main.c
# Vérifier l'avancement
jobs
tail -f build.log &
# Si besoin de terminer : Ctrl+C stoppe le tail, la compilation continue
# Pour voir si la compilation est terminée :
jobs
### `$!` : PID du dernier processus en arrière-plan
```bash
commande_longue &
pid_sauvegarde=$!
echo "Processus lancé avec PID $pid_sauvegarde"
# Attendre la fin du processus
wait $pid_sauvegarde
echo "Processus $pid_sauvegarde terminé avec code $?"
# Attendre tous les processus fils
wait
echo "Tous les processus en arrière-plan sont terminés"
nohup et disown : détacher du terminal#
Quand le terminal se ferme, un signal SIGHUP est envoyé à tous les jobs attachés. nohup et disown permettent de protéger un processus de ce signal.
# nohup : immuniser contre SIGHUP avant le lancement
nohup commande_longue &
# La sortie va dans nohup.out par défaut
# Rediriger explicitement
nohup python3 script.py > /var/log/script.log 2>&1 &
echo "PID: $!"
# disown : détacher un job déjà lancé
commande_longue &
disown %1 # Détacher le job 1 du shell
disown -h %1 # Marquer comme immunisé contre SIGHUP (mais garder dans jobs)
disown -a # Détacher tous les jobs
Remarque 33
La différence entre nohup et disown est liée au moment de l’utilisation. nohup s’utilise avant le lancement : il intercepte SIGHUP et redirige les sorties. disown s’utilise après le lancement : il retire le processus de la table des jobs du shell, de sorte que le shell ne lui envoie plus SIGHUP à sa fermeture. Pour les processus de longue durée sur un serveur distant, l’outil de choix reste cependant tmux ou screen (couverts au chapitre 15), qui permettent de reprendre la session ultérieurement.
nice et renice : priorité des processus#
Le noyau Linux utilise un ordonnanceur pour partager le temps CPU entre les processus. La valeur de nice (-20 à +19) influence la priorité d’ordonnancement : plus la valeur est basse, plus le processus est prioritaire.
Définition 39 (Valeur nice)
La valeur nice d’un processus est un entier compris entre -20 (priorité maximale) et +19 (priorité minimale, valeur par défaut = 0). Un processus à valeur nice élevée est « gentil » (nice) envers les autres : il leur cède volontiers du temps CPU. Seul l’administrateur (root) peut assigner des valeurs négatives (priorités supérieures à la normale). La priorité d’ordonnancement (priority ou PR dans top) est calculée à partir de la valeur nice.
# Lancer une commande avec une priorité réduite
nice -n 10 make -j4 # Priorité réduite (nice = 10)
nice -n 19 rsync -av ... # Priorité minimale (pour les backups)
nice -n -5 processus_urgent # Priorité élevée (root requis pour négatif)
# Modifier la priorité d'un processus déjà en cours
renice -n 10 -p 1234 # Réduire la priorité du PID 1234
renice -n 0 -p 1234 # Revenir à la priorité normale
renice -n -10 -p 1234 # Augmenter la priorité (root requis)
renice -n 5 -u alice # Réduire la priorité de tous les processus d'alice
renice -n 5 -g webservers # Par groupe
# Voir la valeur nice dans top/ps
ps -o pid,ni,pri,comm -p 1234
La commande trap : intercepter les signaux#
trap permet à un script d’intercepter des signaux et d’exécuter du code en réponse.
# Syntaxe : trap 'commandes' signal [signal ...]
# Nettoyage à la sortie (signal EXIT est pseudo-signal Bash)
fichier_temp=$(mktemp)
trap 'rm -f "$fichier_temp"; echo "Nettoyage effectué"' EXIT
# Intercepter Ctrl+C (SIGINT)
trap 'echo "Interruption détectée, abandon propre..."; exit 1' INT
# Ignorer un signal
trap '' HUP # Ignorer SIGHUP
# Restaurer le comportement par défaut
trap - HUP # Restaurer le comportement par défaut pour SIGHUP
# Exemple complet : script robuste avec nettoyage
#!/usr/bin/env bash
set -euo pipefail
TEMP_DIR=$(mktemp -d)
LOCK_FILE="/var/run/monscript.lock"
nettoyage() {
echo "Nettoyage en cours..."
rm -rf "$TEMP_DIR"
rm -f "$LOCK_FILE"
echo "Nettoyage terminé."
}
trap nettoyage EXIT
trap 'echo "Signal INT reçu"; exit 130' INT
trap 'echo "Signal TERM reçu"; exit 143' TERM
# Corps du script
echo "Travail en cours dans $TEMP_DIR"
touch "$LOCK_FILE"
# ... traitement ...
Remarque 34
Le signal pseudo EXIT est une extension Bash qui déclenche le gestionnaire trap quelle que soit la raison de la sortie du script : fin normale, exit explicite, ou mort sur signal. C’est le mécanisme recommandé pour les nettoyages (fichiers temporaires, verrous, processus fils) car il est invoqué dans tous les cas. Les signaux INT et TERM doivent aussi être interceptés si on souhaite un comportement propre lors d’une interruption manuelle.
Le système de fichiers /proc#
/proc est un système de fichiers virtuel (pseudo-filesystem) monté par le noyau Linux qui expose en temps réel des informations sur les processus et le système sous forme de fichiers texte lisibles.
# Structure de /proc
ls /proc/
# 1 2 3 ... (un répertoire par PID)
# cpuinfo meminfo version uptime loadavg ...
# Explorer le répertoire d'un processus
ls /proc/$$ # $$ = PID du shell courant
# Informations importantes dans /proc/PID/
cat /proc/$$/cmdline | tr '\0' ' ' # Ligne de commande (séparée par NUL)
cat /proc/$$/status # Informations détaillées (état, mémoire, uid)
cat /proc/$$/environ | tr '\0' '\n' # Variables d'environnement
ls -la /proc/$$/fd # Descripteurs de fichiers ouverts
cat /proc/$$/maps # Mappings mémoire (bibliothèques chargées)
cat /proc/$$/limits # Limites de ressources (ulimit)
cat /proc/$$/io # Statistiques d'E/S
cat /proc/$$/net/tcp # Connexions TCP du namespace réseau
# Informations système globales dans /proc/
cat /proc/cpuinfo # Informations détaillées sur les CPUs
cat /proc/meminfo # État de la mémoire
cat /proc/uptime # Temps depuis le démarrage
cat /proc/loadavg # Charge moyenne sur 1, 5 et 15 minutes
cat /proc/version # Version du noyau
cat /proc/filesystems # Systèmes de fichiers supportés
cat /proc/mounts # Systèmes de fichiers montés
cat /proc/net/dev # Statistiques réseau par interface
Exemple 24 (Lire l’utilisation CPU d’un processus via /proc)
Il est possible d’écrire un moniteur de processus minimal en pur Bash en lisant /proc :
#!/usr/bin/env bash
surveiller_processus() {
local pid="$1"
local nom
[[ -d /proc/$pid ]] || { echo "PID $pid introuvable"; return 1; }
nom=$(cat /proc/$pid/comm 2>/dev/null)
echo "=== Processus $pid ($nom) ==="
echo "--- Status ---"
grep -E "^(Name|State|Pid|PPid|VmRSS|VmSize|Threads):" /proc/$pid/status
echo "--- Fichiers ouverts ---"
ls -la /proc/$pid/fd 2>/dev/null | wc -l
echo "descripteurs ouverts"
}
surveiller_processus $$
## Visualisation : arbre de processus Linux
```{code-cell} python
:tags: [hide-input]
fig, ax = plt.subplots(figsize=(16, 10))
ax.set_xlim(-1, 17)
ax.set_ylim(-1, 11)
ax.axis('off')
ax.set_title('Arbre de processus Linux (de systemd aux processus utilisateur)',
fontsize=14, fontweight='bold', pad=15)
palette = sns.color_palette("muted", 10)
def draw_node(ax, x, y, label, sublabel, color, width=2.6, height=0.7, fontsize=9):
box = patches.FancyBboxPatch(
(x - width/2, y - height/2), width, height,
boxstyle="round,pad=0.12", linewidth=2,
edgecolor=color, facecolor=color, alpha=0.25
)
ax.add_patch(box)
border = patches.FancyBboxPatch(
(x - width/2, y - height/2), width, height,
boxstyle="round,pad=0.12", linewidth=2,
edgecolor=color, facecolor='none'
)
ax.add_patch(border)
ax.text(x, y + 0.12, label, ha='center', va='center',
fontsize=fontsize, fontweight='bold', color=color, fontfamily='monospace')
if sublabel:
ax.text(x, y - 0.18, sublabel, ha='center', va='center',
fontsize=7.5, color='#666', style='italic')
def draw_edge(ax, x1, y1, x2, y2, color='#888', style='-'):
ax.annotate('', xy=(x2, y2 + 0.35),
xytext=(x1, y1 - 0.35),
arrowprops=dict(arrowstyle='->', color=color, lw=1.8,
linestyle=style))
# Niveau 0 : systemd (PID 1)
draw_node(ax, 8, 10, "systemd (PID 1)", "père de tous les processus",
palette[0], width=3.5)
# Niveau 1 : services systemd
services = [
(2, 8, "sshd", "PID 120"),
(5.5, 8, "NetworkManager", "PID 210"),
(9, 8, "cron", "PID 350"),
(12.5,8, "login", "PID 445"),
(15.5,8, "Xorg", "PID 512"),
]
for sx, sy, nom, pid in services:
draw_node(ax, sx, sy, nom, pid, palette[1], width=2.8)
draw_edge(ax, 8, 10, sx, sy, palette[1])
# Niveau 2 : shells et sessions
shells = [
(1, 5.8, "sshd (session)", "PID 1230"),
(3.5, 5.8, "bash", "PID 1231"),
(9, 5.8, "bash (login)", "PID 1450"),
(12, 5.8, "bash", "PID 1451"),
(15.5,5.8, "gnome-session", "PID 2010"),
]
parents = [2, 2, 12.5, 12.5, 15.5]
for (sx, sy, nom, pid), px in zip(shells, parents):
draw_node(ax, sx, sy, nom, pid, palette[2], width=2.9)
draw_edge(ax, px, 8, sx, sy, palette[2])
# Niveau 3 : processus utilisateur
procs = [
(0.5, 3.5, "vim", "PID 2345"),
(3.5, 3.5, "python3", "PID 2400"),
(7, 3.5, "make -j4", "PID 3100"),
(10.5, 3.5, "top", "PID 3200"),
(14, 3.5, "firefox", "PID 4000"),
(16, 3.5, "xterm", "PID 4100"),
]
parents3 = [1, 3.5, 9, 12, 15.5, 15.5]
for (sx, sy, nom, pid), px in zip(procs, parents3):
draw_node(ax, sx, sy, nom, pid, palette[3], width=2.5)
draw_edge(ax, px, 5.8, sx, sy, palette[3])
# Niveau 4 : sous-processus de make
subprocs = [
(5.5, 1.5, "gcc main.c", "PID 3101"),
(8.0, 1.5, "gcc utils.c", "PID 3102"),
(10.5,1.5, "ld -o prog", "PID 3103"),
]
for sx, sy, nom, pid in subprocs:
draw_node(ax, sx, sy, nom, pid, palette[4], width=2.5)
draw_edge(ax, 7, 3.5, sx, sy, palette[4])
# Légende des états
legend_items = [
(palette[0], "PID 1 : systemd"),
(palette[1], "Services système"),
(palette[2], "Shells / sessions"),
(palette[3], "Processus utilisateur"),
(palette[4], "Sous-processus"),
]
for i, (col, label) in enumerate(legend_items):
rect = patches.Rectangle((0.1, 0.1 + i * 0.5), 0.4, 0.35,
facecolor=col, alpha=0.6, edgecolor='none')
ax.add_patch(rect)
ax.text(0.65, 0.27 + i * 0.5, label, va='center', fontsize=9, color='#333')
plt.tight_layout()
plt.show()
Résumé#
Ce chapitre a exploré la gestion des processus sous Linux depuis le terminal Bash :
Un processus est une instance de programme identifiée par son PID et son PPID, pouvant se trouver dans différents états (running, sleeping, zombie, stopped). Tout processus Linux est un descendant de
systemd(PID 1).psprend un instantané des processus (ps aux,ps -ef,ps -eo), tandis quetopethtopoffrent une vue en temps réel avec contrôle interactif.kill,pkilletkillallenvoient des signaux aux processus.SIGTERM(15) demande une terminaison gracieuse ;SIGKILL(9) force la terminaison sans possibilité d’interception.Les jobs permettent de gérer les commandes lancées depuis le terminal :
&pour l’arrière-plan,Ctrl+Zpour suspendre,fg/bgpour basculer,jobspour lister.nohupetdisownpermettent de détacher un processus du terminal pour qu’il survive à la fermeture de session.niceetrenicecontrôlent la priorité d’ordonnancement (échelle -20 à +19).trappermet d”intercepter des signaux dans les scripts pour effectuer des nettoyages propres./procest un système de fichiers virtuel qui expose les informations de chaque processus en temps réel.
Le chapitre suivant aborde les outils réseau disponibles depuis le terminal : curl, wget, ssh, rsync, les outils de diagnostic et de configuration réseau.