Processus et Ressources

Comment une machine organise l’exécution simultanée de plusieurs programmes ?

 

Programmes et Processus

Il ne faut pas confondre :

  • programme (exécutable) : fichier binaire contenant des instructions machines
  • processus : instance d’exécution d’un programme entraînant des échanges de données (registres, mémoire, interfaces, …)

Dans une même machine, il peut y avoir plusieurs instances d’exécution d’un même programme  !

Activité : processus multiples

Sur les systèmes d'exploitation Windows, on peut aisément obtenir la liste des processus chargés en mémoire à l'aide du Gestionnaire des tâches (Ctrl+Alt+Suppr).

Plusieurs onglets apparaissent. Celui permettant d'obtenir le plus de détails est :

  • Windows 7 : onglet Processus
  • Windows 10 : onglet Détails
Ouvrir le gestionnaire des tâches de votre ordinateur (Windows) et citer plusieurs processus d'un même programme simultanément chargés.
CORRECTION

Plusieurs instances de Firefox sont chargées : une par onglet.

On identifie également des processus « système » : svchost, conhost, …

 

Objectif : partager un (ou plusieurs) processeur entre différents processus, de manière à ce que chaque processus possède son propre processeur virtuel (point de vue conceptuel).

Problème : un processeur (ou un cœur de processeur) ne peut exécuter qu’une seule instruction à la fois, il doit « basculer » constamment d’un processus à l’autre.

 

 

 

PID et PPID

Chaque processus possède un code unique appelé identifiant de processus (PIDProcess IDentifier).

Mais chaque processus (mis à part le premier !) a lui même été lancé par un processus parent, identifié par un identifiant de processus parent (PPIDParent Process IDentifier).

Sous Linux le premier processus démarré s’appelle init et a le PID 1 et le PPID 0

Sous Windows c’est le processus Idle(ou System Idle Process),  de PID 0  (idle signifie « inactif » en anglais)

Activité : processus premier

Sur les systèmes d'exploitation Windows, le Gestionnaire des tâches (Ctrl+Alt+Suppr) permet d'afficher les PID des processus :

  • Windows 7 : onglet Processus
    • Menu Affichage/Sélectionner des colonnes ...
    • cocher PID
  • Windows 10 : onglet Détails
    • par un clic-droit sur les titres des colonnes
    • cocher PID
Ouvrir le gestionnaire des tâches de votre ordinateur (Windows) et trier les processus par leur PID. Donner le nom du premier processus chargé par le noyau.
CORRECTION

Processus inactif du système (idle)

 

Contexte d’exécution

Lorsque plusieurs processus sont lancés, le (cœur d’un) processeur doit basculer de l’un à l’autre (ce basculement rapide est appelé multiprogrammation).

Ce basculement étant imprévisible, la vitesse de traitement d’un processus donné :

  • n’est pas uniforme  : elle pourra être plus rapide au début de l’exécution qu’à la fin, ou l’inverse !
  • n’est pas reproductible : si le même processus s’exécute une nouvelle fois, sa durée d’exécution sera différente.

Activité : programmation concurrente en Python

Python permet la programmation multiprocessus en utilisant des sous-processus, appelés aussi processus légers ou threads.

Le programme Python suivant utilise le module threading et créé successivement 4 threads (objets de type Thread), dont la cible (target) est une fonction qui sera appelée au démarrage du thread (méthode start()), avec les arguments args.

Dans ce cas, la ressource à laquelle l’accès est concurrent est la sortie standard de Python appelée sys.stdout, un canal de communication vers lequel sont envoyées les données lors de l’appel à la fonction print.

import threading

def action(n):
    for i in range(4):
        print("Thread ", n , ": étape" , i, flush = True)
    print("________Fin Thread", n, flush = True)
    
for n in range(4):
    t = threading.Thread(target = action, args = (n,))
    t.start()

L’argument flush = True dans les print permet que les basculements de contexte ne se produisent pas au milieu d’une ligne.

Tester le programme suivant et constater que l’exécution des 4 sous-processus n’est ni uniforme, ni reproductible.

 

Le contexte d’exécution (execution context) d’un processus est l’ensemble des éléments liés à son exécution :

  • PID
  • État du processus
  • Valeurs des registres du processeur
  • Mémoire : Plage d’adresses de la mémoire allouée par le processus
  • Ressources :
    • fichiers ouverts
    • connexions réseau en cours d’utilisation

Cet ensemble de données constitue le bloc de données contextuelles ou bloc de contrôle du processus (PCB – Process Control Block)

Il est sauvegardé à chaque changement de contexte (context switching) : opération de remplacement d’un contexte d’exécution par un autre

 

 

 

États d’un processus

Lorsqu’un programme est lancé, les instructions machine qui le composent sont chargées en mémoire de travail (RAM) : le processus associé est alors créé.

Pendant son existence au sein d’une machine, un processus peut avoir différents états :

  • nouveau : le processus est en cours de création, l’exécutable est en mémoire et le PCB initialisé
  • prêt (ready ou runnable) ou en attente (waiting) : le processus attend d’être affecté à un processeur
  • élu (running) : les instructions du processus sont en cours d’exécution (il utilise le CPU)
    seul un processus peut être en exécution sur processeur à un instant donné.
  • bloqué (blocked) ou endormi (sleeping) : le processus est interrompu en attente qu’un événement se produise
  • terminé (terminated) : le processus est terminé (soit normalement, soit suite à une anomalie).
    il doit être déchargé de la mémoire par l’OS, et les ressources qu’il utilisait libérées.

Selon les systèmes d’exploitation, il peut se produire d’autres états possibles pour des processus :

  • zombie : le processus est terminé mais ne peut pas être déchargé de la mémoire

 

 

Ordonnancement des processus

La gestion des états des processus est confiée à un programme du système d’exploitation appelé ordonnanceur (scheduler). Il est chargé de gérer les accès concurrents aux ressources.

L’utilisation du processeur doit être optimisée : un processus en attente d’une ressource (donnée en mémoire, entrée/sortie, …) doit être mis dans l’état bloqué pour permettre à un autre processus en attente d’un processeur (état prêt) à passer dans l’état élu.

Lorsque plusieurs processus sont en attente (état prêt), l’ordonnanceur doit choisir le processus à élire : il les classe dans une file d’attente.

Dans le cas d’architecture à plusieurs processeurs, un répartiteur (dispatcher) attribut à chaque processeur un processus élu.

L’ordonnanceur de l’OS sélectionne un processus dans la file d’attente et le laisse s’exécuter pendant un délai maximum déterminé appelé le quantum de temps. À la fin de ce temps, ce processus et remis à l’état prêt et un autre est élu.

Une interruption matérielle peut venir écourter le quantum de temps.

Lorsqu’il n’y a plus de processus en attente (état prêt), le processeur redevenant inactif, sa vitesse peut être ralentie afin de diminuer sa consommation énergétique.

 

Politiques d’ordonnancement

L’ordonnanceur peut envisager diverses politiques d’ordonnancement :

  • Premier arrivé, premier servi : simple, mais peu adapté
  • Plus court d’abord : efficace, mais difficile de classer à l’avance la durée d’exécution d’un processus
  • Priorité : chaque processus se voit attribué un niveau de priorité
  • Tourniquet : chaque processus se voit attribué une durée d’exécution (appelé quantum de temps). Quand un processus a atteint cette durée, il cède la place à un autre.
  • … politiques hybrides …

En pratique l’ordonnanceur octroie un quantum de temps à tous les processus tout en gérant les priorités.

 

Processus concurrents

Les exécutions des processus sont entrelacées. On parle d’exécution concurrente et l’OS est qualifié de multitâches.

Lorsqu’un processus est interrompu, son PCB est sauvegardé et quand son exécution reprend, le processus se retrouve dans l’exact état dans lequel il était.

→ Il n’a aucune connaissance de ce qui s’est passé autour de lui pendant ce temps !

Si ce processus ne manipulait que des objets visibles par lui seul (des variables par exemple), il n’y aura aucune conséquente.

En revanche, dans le cas ou ce processus accède à des ressources partagées (des fichiers par exemple), il peut y avoir des conséquences non souhaitées.

 

Activité : accès concurrents à un fichier

Le programme suivant à pour finalité d'écrire dans un fichier texte :

from os import getpid
import sys
import time
import random
pid = str(getpid()) # obtention du PID du processus
if len(sys.argv) > 1:
    num = sys.argv[1]   # obtention du numéro d'ordre de lancement
else :
    num = ""
with open("test.txt", "w", buffering=1) as fichier:
    for i in range(10):
        fichier.write(pid.rjust(5)+" "+num+" --> "+str(i).ljust(2)+"\n")
        fichier.flush() # vidage du tampon
        #time.sleep(0.1) # ralentissement artificiel du processus d'écriture
        time.sleep(random.random()/10)
Créer un fichier processus.py contenant ce script Python.

Si on exécute une unique instance de ce programme, on obtient un fichier texte test.txt de cette forme :

63037   --> 0
63037   --> 1
...
63037   --> 9

63037 étant le PID du processus qui a été exécuté (ce ne sera pas forcément toujours le même !)

 

Si on exécute simultanément plusieurs instances de ce programme, les processus vont être amenés à écrire leurs lignes dans un même fichier texte (même ressource).

Créer, dans le même dossier que processus.py le programme multiprocessus.py avec le code suivant :
import sys
from subprocess import Popen

for n in range(3):
    Popen([sys.executable, "processus.py", str(n)])

Pour observer les processus en cours d'utilisation, nous utiliserons le petit programme NTop.

  • Avant d’exécuter le programme multiprocessus.py, ouvrir une fenêtre de commande (depuis VScode).
  • Taper la commande ntop
  • Depuis ntop :
    • Activer le mode d'affichage en arborescence : :tree
    • Rechercher le processus Python (celui de la console de Pyzo et qui va exécuter multiprocessus.py) : :search python

Depuis Pyzo, lancer le programme multiprocessus.py et observer l'affichage de ntop :

Relever les PID des 3 processus python.exe lancés par multiprocessus.py.

 

 

Cette fois-ci, dans le fichier test.txt, on obtient un résultat de la forme :

31732   2 --> 0
31732   2 --> 1
31732   2 --> 2
28772   1 --> 3
28772   1 --> 4
28772   1 --> 5
28772   1 --> 6
 8560   0 --> 7
 8560   0 --> 8
28772   1 --> 9
Qu'est-ce qui peut paraître étonnant dans ce résultat ?

 

Pourquoi ce résultat ?

 

Synchronisation

 

Interblocage

Un interblocage (ou étreinte fatale, deadlock en anglais) est un phénomène qui se produit lorsque des processus concurrents s’attendent mutuellement. Un processus peut aussi s’attendre lui-même. Les processus bloqués dans cet état le sont définitivement, il s’agit donc d’une situation catastrophique.

Conditions nécessaires

Une situation de blocage sur une ressource peut survenir si et seulement si toutes les conditions suivantes sont réunies simultanément dans un système :

  • Exclusion mutuelle : au moins une ressource du système doit être en accès exclusif.
    Sinon, les processus ne seraient pas empêchés d’utiliser la ressource si nécessaire. Un seul processus peut utiliser la ressource à un instant donné.
  • Rétention et attente (hold and wait ou resource holding) : un processus détient une ressource et demande une ressource supplémentaire détenue par un autre processus.
  • Non préemption : une ressource ne peut être libérée que volontairement par le processus qui la détient.
    Elle ne peut pas être préemptée ou acquise de force par un autre processus
  • Attente circulaire : chaque processus doit attendre une ressource qui est détenue par un autre processus, qui à son tour attend que le premier processus libère la ressource.
    En général, il existe un ensemble de processus en attente, P = { P1 , P2 ,…, PN }, tel que P1 attend une ressource détenue par P2 , P2 attend une ressource détenue par P3

Ces quatre conditions sont connues sous le nom de « conditions de Coffman » d’après leur première description dans un article de 1971 par Edward G. Coffman, Jr.

Bien que ces conditions soient suffisantes pour produire un blocage sur les systèmes de ressources à instance unique, elles indiquent uniquement la possibilité d’un blocage sur les systèmes ayant plusieurs instances de ressources.

Exemples

Le processus P1 utilise la ressource R2 qui est attendue par le processus P2 qui utilise la ressource R1, attendue par P1

Activité : interblocage au carrefour

4 véhicules se présentent en même temps à un carrefour ou la règle de circulation est la priorité à droite. Elles souhaitent toutes aller tout droit.

Quelle est la ressource que les voitures souhaitent partager ?

 

Expliquer en quoi les conditions de Coffman sont remplies dans ce cas de figure et rendent ainsi possible l'interblocage.

 

En pratique, comment cette situation est-elle réglée ? Laquelle des conditions de Coffman est supprimée ?

 

Correction

Ressource : le carrefour

  • Exclusion mutuelle : le carrefour ne peut pas être emprunté par deux voitures à la fois
  • Rétention et attente : chaque voiture détient la priorité sur la voiture de gauche et souhaite accéder au carrefour.
  • Non préemption : aucune des voiture n’a le droit de passer
  • Attente circulaire : chaque voiture attend que la voiture à sa droite passe

En pratique, un conducteur décide d’avancer, prudemment, tout en remerciant poliment le conducteur à qui il a pris la priorité. Cela revient à exercer une préemption sur le carrefour.

 

 

 

Quelques commandes

Linux

ps (process status)

Affiche un cliché instantané de tous les processus en cours

Documentation Ubuntu

L’option −eo pid,ppid,s,command de cette commande affiche dans l’ordre :

  • l’identifiant du processus : pid (Process IDentifier),
  • l’identifiant du processus parent : ppid (Parent Process IDentifier),
  • l’état du processus : s (State)
    • R : processus en cours d’exécution (Running)
    • S : processus endormi (Sleeping)
  • le nom de la commande à l’origine du processus : command

 

Activité

Sur un système Linux, on exécute la commande suivante :

$ ps -eo pid,ppid,stat,command
PID PPID STAT COMMAND
1 0 Ss /sbin/init
.... .... .. ...
1912 1908 Ss Bash
2014 1912 Ss Bash
1920 1747 Sl Gedit
2013 1912 Ss Bash
2091 1593 Sl /usr/lib/firefox/firefox
5437 1912 Sl python programme1.py
5440 2013 R python programme2.py
5450 1912 R+ ps -eo pid,ppid,stat,command
Donner le nom de la première commande exécutée par le système d'exploitationSystème d'exploitation En informatique, un système d'exploitation (souvent appelé OS - de l'anglais Operating System) est un ensemble de programmes qui dirige l'utilisation des ressources d'un ordinateur par des logiciels applicatifs. source : https://fr.wikipedia.org/wiki/Système_d'exploitation.
Relever les identifiants des processus actifs sur cet ordinateur au moment de l’appel de la commande ps .
Donner le nom et le PID du processus depuis lequel la commande ps a été exécutée.
Donner les autres commandes (et leurs PID) qui ont été exécutées à partir de ce processus.
Expliquer l'ordre dans lequel les deux commandes python programme1.py et python programme2.py ont été exécutées.
Peut-on prédire que l'une des deux commandes python programme1.py et python programme2.py finira avant l’autre ?
CORRECTION
  • init
  • 5440 (et 5450)
  • 1912 bash
  • 5437 python programme1.py
  • 5437  programme1.py  puis 5440 programme2.py
  • non

 

 

top

affiche la liste en temps réel de tous les processus en cours d’exécution

 

htop

affiche le résultat en temps réel et est équipée de fonctionnalités conviviales

 

kill

tue un processus

 

 

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.