cursus.

Cours 2 · ProcessusLeçon 2 sur 2

Ordonnancement

8 h de lecture9 sections Version PDF

À la fin de cette leçon, vous saurez

Temps de réponse, temps d'attente et équité ; FIFO, SJF, priorités, tourniquet et files multi-niveaux ; préemption et calculs de moyennes sur diagrammes de Gantt.

Une caisse de supermarché, une seule, et quatre clients : trois avec un article, un avec un caddie plein. Les servir dans l'ordre d'arrivée est équitable et peut faire attendre trois personnes vingt minutes pour trois secondes d'achat. Ouvrir une caisse rapide sert d'abord les petits paniers, réduit l'attente moyenne — et laisse le caddie plein attendre indéfiniment si les petits paniers continuent d'arriver.

Tout l'ordonnancement est dans cette scène. Il n'y a pas de bonne réponse, seulement des compromis entre critères qui s'opposent, et le choix dépend de ce que la machine est censée faire. Ce chapitre les pose, les calcule et les compare — c'est le plus arithmétique du cours, et celui qui se travaille le mieux au tableau.

Ce qu'on cherche à optimiser

L'ordonnanceur choisit, parmi les processus prêts au sens du chapitre 3, celui qui obtient le processeur. Six critères servent à juger sa décision, et il est impossible de les satisfaire tous.

CritèreDéfinitionQui s'en soucie
Temps de séjourde l'arrivée à la finle travail par lots
Temps d'attentetemps passé dans la file des prêtstout le monde
Temps de réponsede l'arrivée à la première exécutionl'interactif
Débitprocessus terminés par unité de tempsle serveur
Utilisationpart du temps où le processeur travaillele serveur
Équitépas de processus indéfiniment ignoréle système partagé

Deux oppositions structurent tout le chapitre. Temps de réponse contre débit : donner souvent la main pour que chacun réagisse vite multiplie les changements de contexte, dont le chapitre 3 a rappelé qu'ils sont un pur surcoût. Attente moyenne contre équité : servir d'abord les courts améliore la moyenne et peut faire attendre un long pour toujours.

Le diagramme de Gantt

C'est l'outil de calcul du chapitre, et il faut le tracer même quand on croit pouvoir s'en passer. Prenons quatre processus arrivés ensemble à l'instant 0 :

ProcessusDurée
P18
P24
P39
P45

En les servant dans l'ordre d'arrivée :

 0        8       12          21        26 ├── P1 ──┼── P2 ─┼──── P3 ───┼─── P4 ──┤ Attente :  P1 = 0   P2 = 8   P3 = 12   P4 = 21Attente moyenne = (0 + 8 + 12 + 21) / 4 = 10,25Séjour  : P1 = 8, P2 = 12, P3 = 21, P4 = 26 → moyenne 16,75

Deux règles pour ne pas se tromper. Le temps d'attente d'un processus est son temps de séjour moins son temps d'exécution — autrement dit tout le temps où il aurait voulu le processeur sans l'avoir. Et lorsque les arrivées ne sont pas simultanées, l'attente se compte à partir de l'arrivée, pas de l'instant 0 : c'est l'erreur la plus fréquente en TD.

Les algorithmes classiques

Premier arrivé, premier servi (FIFO). Non préemptif, trivial, équitable au sens strict. Son défaut porte un nom : l'effet convoi. Un processus long placé en tête bloque tous les autres derrière lui, y compris ceux qui n'avaient besoin que d'un instant de processeur avant de repartir sur une entrée/sortie. Réordonnons la file précédente du plus long au plus court — P3, P1, P4, P2 : l'attente moyenne passe de 10,25 à 12, pour exactement le même travail.

Le plus court d'abord (SJF). On sert le processus dont la prochaine rafale de calcul est la plus brève. On démontre qu'il minimise l'attente moyenne — l'argument est simple : échanger deux processus voisins dont le plus long précède le plus court diminue toujours la somme des attentes, donc l'optimum n'a aucune inversion. Sur notre exemple, 7,5 contre 10,25.

Deux objections, et elles sont sérieuses. La famine : un flux continu de tâches courtes peut repousser une tâche longue indéfiniment. Et surtout, on ne connaît pas la durée à l'avance. En pratique on l'estime à partir du passé, par une moyenne exponentielle qui donne plus de poids aux rafales récentes :

τn+1=αtn+(1α)τn\tau_{n+1} = \alpha\, t_n + (1-\alpha)\, \tau_n

Le pari est celui du chapitre 7 d'architecture, transposé : un processus qui vient d'être interactif le restera probablement.

Les priorités. Chaque processus porte un numéro, et le plus prioritaire passe. SJF n'en est qu'un cas particulier, où la priorité est l'inverse de la durée. Même défaut, donc, et la même parade : le vieillissement, qui augmente progressivement la priorité d'un processus en attente. Sans lui, un processus de faible priorité peut attendre des heures — la légende veut qu'un travail soumis en 1967 sur l'IBM 7094 du MIT ait été retrouvé encore en attente lors de l'arrêt de la machine en 1973.

Le tourniquet (round robin). Chaque processus reçoit un quantum de temps ; à l'expiration, l'interruption d'horloge rend la main au noyau, qui le replace en fin de file. C'est l'algorithme du temps partagé, et le seul de la liste qui borne le temps de réponse : avec nn processus et un quantum qq, aucun n'attend plus de (n1)q(n-1)q.

Le choix de qq est un compromis exemplaire. Trop grand, le tourniquet dégénère en FIFO — si le quantum dépasse la plus longue rafale, plus personne n'est jamais préempté. Trop petit, le changement de contexte, qui coûte quelques microsecondes, mange une part croissante du temps : avec un quantum de 100 µs et un changement de contexte de 5 µs, 5 % du processeur part en pure administration. Les valeurs usuelles vont de 10 à 100 millisecondes.

Même charge, mêmes durées, même processeur : seul l'ordre de service change, et l'attente moyenne varie de 7,5 à 13,25. SJF donne bien le minimum — mais il l'obtient en faisant attendre le plus long, qui n'a aucune garantie de passer un jour. Et le tourniquet est le PIRE des quatre sur ce critère, ce qui ne le disqualifie pas : voir le tableau ci-dessous.

Le classement s'inverse dès qu'on change de critère, et c'est le tableau le plus instructif du chapitre :

AlgorithmeAttente moyenneTemps de réponse moyen
FIFO (P1 P2 P3 P4)10,2510,25
Le plus court d'abord7,57,5
Tourniquet, quantum 413,256

Le tourniquet perd sur l'attente parce qu'il découpe chaque processus en tranches, ce qui retarde toutes les fins. Il gagne largement sur le temps de réponse, parce que tout le monde a touché le processeur avant l'instant 16, alors qu'en FIFO le dernier servi attend 21. Sur un poste de travail, c'est le second chiffre que l'utilisateur ressent : personne ne chronomètre une compilation en tapant du texte, mais tout le monde remarque une saisie qui répond avec une seconde de retard.

Quiz · vérifiez votre compréhension Sans réponse

Un administrateur observe qu'un serveur passe 20 % de son temps en changements de contexte. Le quantum du tourniquet est réglé à 20 µs et un changement de contexte coûte 5 µs. Quel réglage corrige la situation, et quel effet secondaire faut-il accepter ?

Les files multi-niveaux

Aucun algorithme simple ne convient à une machine réelle, qui exécute simultanément un éditeur interactif, une compilation et un service réseau. La solution employée par tous les systèmes courants est de les combiner.

Les files multi-niveaux répartissent les processus en catégories — système, interactif, par lots — chacune ayant sa propre file, son propre algorithme et sa propre priorité. Un processus n'en change jamais, ce qui est leur limite : la catégorisation est faite une fois pour toutes, alors que le comportement d'un processus varie.

Les files multi-niveaux avec rétroaction lèvent cette limite, et c'est le mécanisme à comprendre. Un processus entre au niveau le plus prioritaire, avec un quantum court. S'il épuise son quantum, on en déduit qu'il est gourmand en calcul, et on le descend d'un niveau, où il recevra un quantum plus long mais moins souvent. S'il se bloque avant la fin de son quantum — parce qu'il attend une saisie ou un disque —, il est interactif, et il reste haut ou remonte.

Le système classe donc les processus par observation, sans que personne ne les déclare. Un éditeur de texte, qui passe son temps bloqué en attente de frappe, occupe naturellement les niveaux hauts et réagit instantanément ; une compilation, qui consomme tout ce qu'on lui donne, descend et s'exécute quand le reste est calme. On y ajoute du vieillissement pour éviter la famine des niveaux bas.

Préemption

Un ordonnanceur non préemptif ne reprend le processeur que si le processus le rend volontairement : en se terminant, ou en se bloquant sur une entrée/sortie. FIFO et SJF de base sont dans ce cas.

Un ordonnanceur préemptif peut le retirer à tout moment, et cette capacité repose entièrement sur un mécanisme matériel : l'interruption d'horloge du chapitre 8 d'architecture. Sans elle, un programme qui ne rend jamais la main est indélogeable.

C'est plus qu'une question de performance. Un système coopératif, comme les Windows et Mac OS d'avant 1995, est à la merci de chaque programme : une seule boucle infinie gèle la machine entière. La préemption est donc d'abord une question de protection, au sens du chapitre 1 — le système ne peut garantir quoi que ce soit s'il ne peut pas reprendre son processeur.

Elle a un coût, cependant, et il ne se limite pas au changement de contexte : préempter un processus au milieu d'une modification de données partagées est exactement ce qui produit les conditions de concurrence du chapitre 5.

Quiz · vérifiez votre compréhension Sans réponse

Dans une file multi-niveaux avec rétroaction, un processus qui se bloque avant la fin de son quantum est maintenu ou remonté en priorité haute. Quelle est la logique ?

À vous

L'exercice implémente les trois algorithmes sur un même jeu de processus, puis calcule attente et temps de séjour moyens. Le squelette fournit le tracé du diagramme de Gantt en texte : c'est lui qui permet de vérifier un calcul faux, exactement comme au tableau.

Trois expériences valent d'être menées. Comparer FIFO selon l'ordre d'arrivée, pour mesurer l'effet convoi. Vérifier que SJF donne bien le minimum, en essayant de le battre par un autre ordre. Et faire varier le quantum du tourniquet de 1 à 20 pour voir l'attente évoluer, puis dégénérer en FIFO.

Exercice · JavaScript · à vous de jouer

Implémentez SJF et le tourniquet, puis comparez attente, séjour et temps de réponse.

En attente
// Chaque processus : nom, instant d'arrivée, durée de calcul.
const CHARGE = [
  { nom: "P1", arrivee: 0, duree: 8 },
  { nom: "P2", arrivee: 0, duree: 4 },
  { nom: "P3", arrivee: 0, duree: 9 },
  { nom: "P4", arrivee: 0, duree: 5 },
];

// Un ordonnancement est une suite de tranches [nom, debut, fin].
function bilan(nom, charge, tranches) {
  const gantt = tranches
    .map((t) => t.nom + " " + t.debut + "-" + t.fin)
    .join(" | ");

  let attente = 0, sejour = 0, reponse = 0;
  for (const p of charge) {
    const siennes = tranches.filter((t) => t.nom === p.nom);
    const fin = siennes[siennes.length - 1].fin;
    const premiere = siennes[0].debut;
    sejour  += fin - p.arrivee;
    attente += fin - p.arrivee - p.duree;   // séjour moins temps de calcul
    reponse += premiere - p.arrivee;
  }
  const n = charge.length;
  console.log(nom);
  console.log("   " + gantt);
  console.log("   attente " + (attente / n).toFixed(2) +
              " | séjour " + (sejour / n).toFixed(2) +
              " | réponse " + (reponse / n).toFixed(2));
}

// FIFO : dans l'ordre d'arrivée, chacun jusqu'au bout.
function fifo(charge) {
  const file = [...charge].sort((a, b) => a.arrivee - b.arrivee);
  const tranches = [];
  let t = 0;
  for (const p of file) {
    t = Math.max(t, p.arrivee);
    tranches.push({ nom: p.nom, debut: t, fin: t + p.duree });
    t += p.duree;
  }
  return tranches;
}

// SJF non préemptif : parmi les ARRIVÉS, le plus court d'abord.
function sjf(charge) {
  return [];   // ← à écrire
}

// Tourniquet : chacun reçoit au plus « quantum », puis repasse en fin de file.
function tourniquet(charge, quantum) {
  return [];   // ← à écrire
}

// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Écrivez sjf et tourniquet.
// 2. Vérifiez : FIFO 10,25 — SJF 7,50 — tourniquet q=4 : attente 13,25 mais
//    réponse 6. Expliquez pourquoi le tourniquet perd sur l'un et gagne
//    sur l'autre.
// 3. Faites varier le quantum de 1 à 20 : à partir de quelle valeur le
//    tourniquet redevient-il exactement FIFO ?

bilan("FIFO, ordre d'arrivée", CHARGE, fifo(CHARGE));

Console de sortie
Le résultat s'affiche dans la console

En travaux pratiques

Travaux pratiques 4 · sur machine

Qui passe en premier

Simuler les politiques d'ordonnancement pour en voir les effets, puis les retrouver sur la vraie machine avec les outils qui règlent les priorités.

3 h
Avant de commencer
  • Le TP 3 : création de processus
  • Les commandes nice, chrt, top
  1. 1. Simuler à la main

    Cinq tâches, avec dates d'arrivée et durées données. Calculez à la main les temps d'attente et de rotation moyens pour premier arrivé premier servi, puis pour le plus court d'abord.

  2. 2. L'effet convoi

    Construisez un jeu de tâches où une longue arrive en premier. Comparez les deux politiques et mesurez l'écart d'attente moyenne.

  3. 3. Le tourniquet

    Simulez le tourniquet avec un quantum de 1, puis 4, puis 20. Tracez l'attente moyenne et le nombre de changements de contexte en fonction du quantum.

  4. 4. La famine

    Avec un ordonnancement par priorités fixes, construisez une situation où une tâche n'est jamais élue. Ajoutez ensuite le vieillissement et vérifiez qu'elle finit par passer.

  5. 5. Sur la vraie machine

    Lancez deux boucles de calcul infinies et observez leur partage du processeur. Changez la courtoisie de l'une avec nice et mesurez le nouveau partage.

  6. 6. Interactif contre calcul

    Lancez une boucle de calcul, puis tapez dans un éditeur. La frappe reste-t-elle fluide ? Expliquez ce que fait l'ordonnanceur, et pourquoi il a raison.

  7. 7. Le temps réel

    Lancez un processus avec chrt en politique FIFO temps réel et observez ce qui arrive aux autres. Faites-le dans une machine virtuelle, et dites pourquoi cet avertissement est là.

C'est réussi quand
  • Vos deux moyennes concordent avec la simulation à la main
  • Vous montrez qu'un quantum trop petit dégrade le débit
  • nice -n 19 sur une boucle change visiblement le partage dans top
  • Vous savez expliquer pourquoi le plus court d'abord est optimal ET inapplicable

Ce que la suite en fait

Le bloc III commence là où celui-ci s'arrête. La préemption vient d'être présentée comme une nécessité ; le chapitre 5 montre son revers. Retirer le processeur à un processus au milieu d'une suite d'instructions qui modifie une donnée partagée laisse cette donnée dans un état incohérent, que le processus suivant lira.

L'exercice de ce chapitre y prépare directement : vous y aurez découpé des exécutions en tranches et fait alterner des processus. Il suffira de leur donner une variable commune pour que le problème apparaisse.

À retenir

Flashcards · 1 / 5Toucher pour retourner
Fin de la leçon

Vous avez parcouru les 9 sections.

Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.