Tindo
Tindo
Tindo
par
Gilbert TINDO
Objectifs du cours :
- présenter les principaux concepts sur lesquels s’appuient les systèmes
d’exploitation multiprogrammés, ainsi que les éléments de leur mise en œuvre
- illustrer les concepts à l’aide d’un système : le système unix
- présenter les principes des systèmes d’exploitation distribués
1
3-2 Gestion des fichiers
3-2-1 Définitions
3-2-2 Les répertoires
3-2-3 Implantation d'un système de fichiers
3-2-4 Exemple de systèmes de fichiers: FAT32 & Unix V
3-3 Les entrées/sorties de bases
3-3-1 Les matériels d'E/S
3-3-2 Les logiciels d'E/S
3-3-3 Les disques
3-3-4 Les horloges
Bibliographie :
1) Andrew Tanenbaum : « Systèmes d’exploitation : Système centralisés, Systèmes
distribués », InterEditions
2
Chapitre 1 : Introduction
1.0 Introduction
Un système informatique est un ensemble de matériels et de logiciels destinés à résoudre les
problèmes d’un ensemble d’utilisateurs. Le matériel sans le logiciel n’est qu’un amas métallique
inutile. Le logiciel d’un système informatique peut se subdiviser en deux grandes classes :
- les logiciels d’application
- les logiciels système
Les logiciels d’application servent à résoudre des problèmes spécifiques d'un utilisateur Ils
peuvent être écrits par l’utilisateur lui même: il suffit que l'utilisateur sache programmer dans un
langage de programmation quelconque (C,C++, pascal, java, ...etc) et maîtrise les techniques du génie
logiciel. Les logiciels d'application peuvent aussi être trouvés sur le marché prêts à l'emploi: c’est
souvent le cas pour les logiciels de traitement de texte, les tableurs, les logiciels de gestion et
comptabilité, les didacticiels, ...etc.
Les logiciels systèmes sont plus complexes et servent en général d'interface entre les logiciels
d'application et le matériel. Sans ces logiciels systèmes, chaque programmeur devrait écrire des
programmes qui interfèrent directement avec le matériel, ce qui rendrait le travail du programmeur très
complexe. Les logiciels systèmes peuvent être regroupés en deux sous-classes : les utilitaires et le
système d’exploitation. Les utilitaires aident à développer des applications : ce sont les compilateurs,
les assembleurs, les éditeurs de liens, les chargeurs, les débogueurs, ...etc. Le système d’exploitation
quant à lui, a pour but de simplifier la tâche des utilisateurs en leur présentant une machine virtuelle
plus simple à exploiter que la machine réelle. Il permet aussi d’assurer l’exploitation efficace et
économique des différentes ressources de la machine.
Un ordinateur est composé d’un ensemble d’organes qui communiquent au moyen d’une ligne
de communication appelée bus . Le bus,à un instant donné est alloué à un et un seul des organes qui se
le partagent. L'allocation du bus est assurée par un organe appelé arbitre du bus. Toutes les unités
attachées au bus , le sont à travers d’autres unités appelées contrôleurs. On distingue ainsi : des
contrôleur de disques , des contrôleurs d’imprimantes , ... etc.
Quand l’ordinateur est mis sous tension, un programme spécial chargé en mémoire morte
s’exécute. Son rôle est de localiser sur le disque dur un secteur d’amorçage (premier secteur de tout
disque, appelé aussi MBR (master boot record)), de charger son contenu en mémoire centrale et de
lancer son exécution. Ce programme d'amorce sait où trouver le système d’exploitation, comment le
3
charger en mémoire et à partir de quelle adresse commencer son exécution. La partie du système
d’exploitation qui est ainsi chargée en mémoire par le programme d'amorce est généralement appelée
noyau . Ce dernier après être chargé en mémoire commence à s’exécuter et attend qu’un événement se
réalise. Un événement peut être une interruption logicielle ou matérielle. Une interruption est un signal
généré par une organe de l'ordinateur (interruption matérielle) ou par le programme en cours
d'exécution (interruption logicielle).
Pour chaque type d’interruption, un service est fourni par le noyau et est responsable de
l’action à entreprendre lorsque cette interruption survient.
Le système d’exploitation utilise une table de statut des différentes unités. Chaque entrée de
cette table indique :
(7) le type de l’unité
(8) l’adresse où l’on peut communiquer avec l’unité
(9) le statut de l’unité (si l’unité est occupée , est inoccupée ou inopérante.)
(10) une file d’attente sur l’unité
b) la gestion de la mémoire
A un moment donné plusieurs programmes peuvent se trouver en mémoire centrale et doivent
s'exécuter simultanément. Dans ce cas, chaque programme a une zone de mémoire qui lui est propre.
Le système d’exploitation est responsable de :
4
(22) allouer et libérer une zone mémoire
(23) charger des processus en mémoire
(24) retirer des processus de la mémoire
(25) garder la trace des zones de mémoire occupées par les différents processus
(26) gérer les espaces libres en mémoire
(27) assurer le transfert des données de ou vers la mémoire centrale
Le système d’exploitation assure ces fonctions à l’aide des appels système
f) gestion du réseau
Le système d’exploitation considère les accès au réseau comme une sorte d’accès au fichier.
Les détails de traitement d’accès réseau sont confinés dans le pilote de l’interface réseau
g) La protection du système
Le système d’exploitation fournit un moyen permettant de distinguer les utilisations autorisée
ou non d’une. ressource. La protection pour le système d’exploitation consiste à fournir un
mécanisme de contrôle des accès aux ressources par les processus des utilisateurs.
h) L’interpréteur de commandes
C’est l’interface entre l’utilisateur et le système d’exploitation. Certains systèmes
d’exploitation tels que MSDOS, UNIX n’intègre pas l’interpréteur dans le noyau. Pour eux il
5
s’agit d’un programme qui s’exécute en premier quand un utilisateur se connecte. Le but de
l’interpréteur est d’obtenir la prochaine commande et de l’exécuter.
Le noyau d’un système d’exploitation est la partie de ce système qui ne peut pas être
remplacée par un programme de l’utilisateur. Il contient les fonctions essentielles du système. On
distingue deux types de noyau :
(45) le noyau monolithique : il est composée d’un ensemble de procédures indépendantes
pouvant s’appeler les unes les autres . Il n’y a pas une organisation en module
(46) le micronoyau : c’est un noyau où tout ce qui n’est pas essentiel n’a pas été implémenté.
Un tel noyau implémente juste les fonctions essentielles. Les autres sont implémentées à un
niveau supérieure au dessus du noyau soit dans des bibliothèque soit même au niveau des
processus utilisateur.
6
Le bon fonctionnement d’un système de traitement par lot suppose :
- la limitation du temps d’occupation du processeur pour éviter qu’une boucle infinie dans un
programme ne bloque tout le système.
- la supervision des entrées/sorties pour éviter des boucles sur l’utilisation des périphériques
- la protection de la zone mémoire réservée au moniteur
Le rendement du système est ainsi amélioré. Mais un inconvénient demeure: le processeur
central s’occupe entièrement des entrées/sorties qui sont pénalisantes en temps.
Une amélioration est apportée en introduisant un processeur moins puissant spécialisé dans les
entrées/sorties. Les jobs collectés sur cartes sont transcrites sur une bande magnétique par le
processeur d’entrées/sorties. Dès que la bande est pleine, elle est envoyée au processeur central qui va
lire les programmes un à un et les exécuter. Les résultats de chaque programme sont envoyés en sortie
sur une autre bande magnétique. Dès que tous les jobs ont été exécutés, la bande contenant les
résultats est retirée et envoyée au processeur d’entrées/sorties qui se chargera de copier les résultats
sur imprimantes.
Du fait de l’introduction du processeur spécialisé dans les entrées/sorties, le processeur central
n’a plus accès aux périphériques lents que sont l’imprimante et les lecteurs de cartes. Le gain en temps
est donc considérable.
Génération 2: La multiprogrammation
La génération 2 commence avec l’introduction des tambours et disques magnétiques pour
stocker les données. Ces supports de données sont adressables et à accès plus rapide que les bandes
magnétiques. Les programmes sont lus sur les cartes perforées et enregistrés sur disques. Du fait que
les disques sont à accès aléatoire, le moniteur peut choisir d’exécuter un job quelconque parmi les jobs
enregistrés sur disques. Un nouveau module est ajouté au système d’exploitation, le planificateur dont
le rôle est entre autres, d’affecter une priorité à chaque programme; cette technique qui permet au
processeur d’aller chercher les programmes sur disques suivant une certaine priorité et de les exécuter
est appelée SPOULE (traduction de Spool : Simultaneous Peripheral Opération On Line).
L’évolution du spoule va conduire à l’introduction de la multiprogrammation. Dans un tel système
plusieurs programmes sont chargés en mémoire centrale et se partagent le processeur. A tout moment
le processeur est alloué à un seul programme, mais dès que celui-ci a besoin de faire des
entrées/sorties le processeur initialise celles-ci et les fait exécuter par les unités d’échanges.
L’exécution du programme est donc suspendue et le processeur commence l’exécution du programme
le plus prioritaire dans la liste des programmes en attente. Le système d’exploitation s’enrichit d’un
nouveau module: l’allocateur (dispatcher). L’allocateur peut aussi interrompre l’exécution d’un
programme qui dure trop longtemps même si ce dernier ne demande pas à faire des entrées/sorties :
c’est ce qu’on appelle la préemption.
Une amélioration des systèmes d’exploitation multiprogrammés conduit aux systèmes à temps
partagés caractérisés par :
- un mode d’exploitation interactif et conversationnel
- un quantum de temps accordé à chaque programme pour son exécution. Au delà de ce
quantum, le programme est interrompu même s’il ne demande pas des entrées/sorties. Ce type de
système donne l’illusion à chaque utilisateur qu’il a à sa disposition toutes les ressources de la
machine.
7
Génération 5:
La cinquième génération est celle des systèmes en réseau et des systèmes distribués. Un réseau
d’ordinateurs est un ensemble d’ordinateurs connectés à travers des liens de communication( fibre
optique, câble coaxial, ondes hertzienne, à etc) et qui peuvent s’échanger des informations. Un
système d’exploitation réseau permet de gérer un ordinateur relié à un réseau d’ordinateurs; chaque
utilisateur du réseau sait sur quelle machine il est connecté et peut envoyer un message ou transférer
des données à une autre machine en indiquant clairement son adresse. Dans un système distribué, il
peut il y avoir un ou plusieurs processeurs qui se partagent des mémoires communes, il peut même y
avoir plusieurs ordinateurs interconnectés, mais tout ceci est transparent à l’utilisateur. Tout se passe
pour l’utilisateur comme s’il a un seul processeur à sa disposition. Son programme peut être exécuté
sur un autre site que celui où il est connecté; ses données peuvent être sur le disque dur d’une autre
machine que celle sur laquelle il est connecté.
I-5 Résumé
Un ordinateur moderne comporte un ou plusieurs processeurs, une mémoire centrale, des
horloges, des terminaux, des disques, des interfaces de connexion à des réseaux, des imprimantes,
...etc. Ecrire des programmes qui prennent en compte tous ces éléments et les gèrent correctement est
un travail très difficile : la programmation dans ce cas serait l’apanage d’une très faible communauté
de spécialistes. Les systèmes d’exploitation libèrent le programmeur de la complexité du matériel en
offrant à ce dernier une machine virtuelle plus facile à utiliser et en gérant efficacement et économique
les différentes ressources de la machine.
Le système d’exploitation est donc un gestionnaire des ressources de la machine et une
machine étendue.
8
Partie I : Gestion des Processus
I – 1 Définitions
Système d’exploitation : C’est un programme qui joue le rôle d’intermédiaire entre
l’utilisateur et l’architecture matérielle de la machine. Il a pour but de fournir un
environnement dans lequel un utilisateur peut exécuter de façon commode et efficace des
programmes. Il peut être vu comme un gestionnaire des ressources de la machine ou comme
une machine virtuelle présentant une interface plus facile à utiliser.
Action atomique : C’est une action indivisible. Les états observables précèdent ou suivent
l’action. Il n’y a pas d’état intermédiaire.
Point d’observation : instant où on peut observer l’état du processeur (contenu des registres)
et de la mémoire. Il se situe entre deux instructions, ou à des endroits où la machine est
interruptible.
Etat observable : état de la machine à un point observable
Trace d’exécution : suite d’états observables produite par l’exécution d’un programme. Ils
sont datés et ordonnés.
Processus : Il correspond à l’exécution d’une tâche. Il est une entité active possédant un
contexte composé de :
- un programme
- un compteur de programme
- des registres
- un segment de code
- un segment de mémoire
- un segment de pile
- la liste des fichiers ouverts
- les informations d’ordonnancement
- les informations pour la comptabilité du temps
Contexte d’un processus : Ensemble des informations accessibles au cours du cycle de vie
du processus. On distingue un contexte processeur et un contexte en mémoire. Le contexte
processeur est formé du contenu de l’ensemble des registres de la machine. Le contexte en
mémoire est l’ensemble des informations stockés en mémoire et qui sont manipulées par le
processus.
Processus coopérants : ensemble de processus dont les contextes sont non disjoints.
L’exécution de l’un affecte l’exécution de l’autre.
9
Noyau d’un système d’exploitation : c’est la partie du système d’exploitation correspondant
à:
- la gestion des processus
- la gestion de la mémoire
- la gestion des entrées / sorties de bas niveau.
- La gestion de bas niveau des fichiers
Le noyau d’un système d’exploitation ne peut pas être remplacé par du code appartenant à un
utilisateur.
I-2 Processus séquentiels
Un processus séquentiel (ou processus) correspond à l’exécution d’une tâche. Un
processus a un contexte et s’exécute soit en mode maître, soit en mode esclave. La plupart du
temps, le processus s’exécute en mode esclave, mais dès qu’un appel système est effectué, le
processus entre en mode maître. Un processus n’est donc jamais simultanément en mode
maître et en mode esclave.
L’exécution d’un processus utilise deux piles :
- une pile noyau du processus
- une pile utilisateur du processus
La pile noyau suit immédiatement la pile utilisateur en mémoire et les deux constitue la pile
du processus. En mode maître, le processus utilise la pile noyau , et en mode esclave, il utilise
la pile utilisateur.
NB : le noyau du système d’exploitation a sa propre pile pour son exécution.
Diagramme de transition :
- nouveau / prêt : fin des initialisations
- prêt / actif : choisi par l’ordonnanceur
- actif / prêt : fin du quantum (une interruption)
- actif / bloqué : attente d’entrée/sortie ou d’un événement
- bloqué / prêt : fin des entrée / sortie ou réalisation de l’événement
- actif / terminé : fin du processus
10
I-3 Synchronisation entre processus
On peut ressentir la nécessité de synchroniser des processus coopérants, s’ils sont en
compétition pour une ressource critique. La ressource critique peut être :
- un fichier partagé
- une zone de mémoire
- une imprimante
- un table d’une base de données
- … etc
On peut aussi faire coopérer des processus pour essayer d’optimiser le temps d’exécution :
décomposer une tâche en plusieurs sous tâches et allouer les sous tâches à différents
processus. Assembler les solutions partielles pour avoir une solution globale.
Enfin, pour sa convenance un utilisateur peut choisir de travailler sur plusieurs tâches
partageant une même ressource à la fois. Par exemple :
- éditer un fichier
- compiler le fichier
- imprimer le fichier
11
verrou=0 ;
b) alternance .
Utilise une variable tour. Un processus entre en section critique si c’est son tour, c’est-à-dire
que la variable tour porte son numéro.
P0 P1
While (1) { While (1) {
While (tour!=0) /* attendre */ ; While (tour!=1) /* attendre */ ;
<section critique > <section critique >
tour=1 ; tour=0 ;
<section non critique> <section non critique>
} }
Inconvénient :
- Si l’un des processus est plus rapide Un processus non en section critique
empêche un autre d’entrée en section critique.
- Si l’un des processus se termine l’autre ne peut plus entrer en section critique.
c) solution de peterson.
#define False 0
#define True 1
#define N 2
int interesse[N] ;
int tour;
void entrer_region(int process)
{ int autre;
autre=1-process ;
interesse[process]=True ;
tour=process ;
while (tour==process && interesse[autre]==True) /*attendre */ ;
}
NB : l’attente active n’exclut pas en général les interblocages. Il suffit de prendre l’exemple
de deux processus dont l’un est prioritaire. Le moins prioritaire est en section critique.
12
a) le masquage d’interruption.
On autorise un processus à masquer les interruptions et à les démasquer à un instant donné. Le
code d’un processus devient :
<section non critique>
<masquer les interruptions >
<section critique>
<démasquer les interruptions>
Dès qu’un processus a masqué les interruptions, aucun autre processus ne peut devenir actif
puisque l’ordonnanceur utilise justement le mécanisme d’interruption pour réaliser allouer le
proceseur.
b) l’instruction TSL.
Cette instruction charge le contenu d’un mot mémoire dans un registre, puis met une valeur
non nulle à cette adresse mémoire, et tout cela de façon indivisible. Le processeur qui effectue
TSL verrouille le bus de données pour empêcher les autres d’accéder à la mémoire pendant la
durée de l’opération.
Producteur Consommateur
While (1) { While (1) {
Produire_objet() ; if (compteur==0) sleep() ;
if (compteur==N) sleep() ; retirer_objet() ;
compteur++ ; compteur-- ;
déposer_objet() ; If (compteur==(N-1))
If (compteur==1) wakeup(&producteur) ;
wakeup(&consommateur) ; }
}
NB : On peut cependant trouver des situations où un seul bit de réveil ne suffit pas. Multiplier
les bits de réveil ne résout pas le problème.
i) les sémaphores
Un sémaphore est une variable entière initialisée généralement à une valeur supérieure ou
égale à zéro, et qui ne peut être manipulée que par l’intermédiaire de deux appels systèmes
DOWN (ou P) et UP (ou V) dont l’exécution est atomique.
13
Le système gère une file d’attente (f) qui permet de garder les processus bloqué sur le
sémaphore, et c’est de cette file qu’un processus est réveillé par le sémaphore. Les primitives
Down et Up peuvent se présenter ainsi :
Void Down(int s,file f,int tP[], int nP) Void Up(int s,file f,int tP[],int *nP)
{ s--; { s++;
if (s<0) { if (s<=0) {
tP[nP]=”bloqué”; defiler(f,nP);
enfile(f,nP) ; tP[*nP]=”actif”;
} }
} }
Le code d’un processus utilisant un sémaphore pour se synchroniser avec d’autres processus
est :
<section non critique >
Down(s,f,tP,nP) ;
<section critique >
Up(s,f,tP,&nP);
<section non critique>
Montrons que la solution utilisant les sémaphores est correcte :
14
(7) Up(S1) (7’) Up(S2)
Si les instructions des deux processus sont exécutées dans l’ordre (i), (I’), il y aura
interblocage.
2) attente indéfinie en section critique
Si un processus reste trop longtemps en section critique, il empêche les autres d’y
entrer. Les solutions adoptées sont les suivantes :
- un processus qui entre en section critique devient très prioritaire, et donc en
sort très vite
- une horloge de garde est armée dès qu’un processus entre en section critique.
S’il n’en sort pas après un certain temps, le système d’exploitation l’interrompt
et libère la section critique
3) privation
Il y a risque de privation. Tout dépend de la politique de gestion de la file d’attente, où on
stocke les processus bloqués.
j) les moniteurs
Un moniteur est un outil de plus haut niveau que les sémaphores, permettant d’écrire des
noyaux de synchronisation. Un moniteur est une sorte de classe définie par le langage utilisé.
Il est constitué :
- d’un ensemble de variables d’état inaccessibles directement aux utilisateurs du
moniteur
- d’un ensemble de primitives manipulant ces variables et accessibles aux
utilisateurs du moniteur. Les procédures du moniteur s’exécutent toujours en
exclusion mutuelle.
- Parmi les variables d’état il y en a appelée condition (c), sur lesquelles on peut
réaliser les opérations suivantes :
i. c.attendre bloque le processus qui s’exécute sur la condition c
ii. c.vide retourne vrai si aucun processus n’est bloqué
iii. c.signaler réveille un des processus bloqué sur c si non (c.vide).
Si aucun processus n’est bloqué, cette opération n’a aucun effet.
Un seul processus peut être actif dans un moniteur à un instant donné. Par conséquent,
signaler doit toujours être la dernière instruction d’une procédure du moniteur.
Comment assurer qu’un seul processus est actif dans le moniteur à un instant donné ? En
utilisant un sémaphore binaire pour assurer l’accès exclusif au moniteur.
NB : Parmi les procédures du moniteur, il y en a une qui permet d’initialiser les variables
d’état.
15
#define N 100
typedef int compteur_evt ;
compteur_evt in=0 ; /* nombre objets dans tampon */
compteur_evt out=0 ; /* nombre objets retirés du tampon */
void producteur()
{ int objet,sequence=0 ;
while (1) {
produire_objet(&objet) ;
sequence++ ;
AWAIT(out,sequence-N) ;
Mettre_objet(objet);
ADVANCE(&in) ;
}
}
void consommateur()
{ int objet,sequence=0 ;
while (1) {
sequence++ ;
AWAIT(in,sequence) ;
Retirer_objet(&objet);
ADVANCE(&out) ;
Utiliser_objet(objet) ;
}
}
g) L’Echange de message
Les outils étudiés jusqu’ici ne permettent de synchroniser des processus que s’ils sont sur la
même machine. Si les processus à synchroniser tournent sur des machines différentes, aucune
des procédures précédentes ne peut marcher. L’échange de message offre une solution à ce
problème. On utilise deux primitives pour l’échange de messages :
Send(destination,message) ;
Receive(source,&message) ;
L’émetteur et le récepteur créent des boîtes aux lettres. Chaque processus communicants lit
les messages dans sa boîte aux lettres à l’aide de la primitive receive. Il dépose des messages
dans la boîte aux lettres de l’autre processus à l’aide de la primitive send. Les adresses source
et destination sont en fait des adresses permettant de repérer des boîtes aux lettres. Par
exemple une adresee IP suivi d’un numéro de port permet de repérer la boîte aux lettres d’un
processus en ce qui concerne le web (protocole http).
Si source est ANY, le récepteur attend des messages de n’importe quel émetteur. Le récepteur
peut se bloquer s’il n’y a pas de message. Les messages transitent à travers un réseau pour
arriver à destination. On peut donc être confronté à l’un des problèmes suivants :
- le problème de l’acquittement
- le problème de numérotation des messages
- la numérotation des processus. A quel processus délivrer un message entrant ?
- Faut-il utiliser ou non des boîtes aux lettres ?
16
Exemple : Producteur / Consommateur avec les messages
#define N 100
#define Taille 4
typedef int message[Taille] ;
void producteur()
{ int objet ;
message m ;
while (1) {
produire_objet(&objet);
receive(consommateur,&m) ;
faire_message(&m,objet) ;
send(consommateur,m) ;
}
}
void consommateur()
{ int i,objet ;
message m ;
for (i=0 ;i<N ;i++) send(producteur,m) ; /* expédie N messages vides */
while (1) {
receive(producteur, &m) ;
retirer_objet(&m,&objet) ;
send(producteur,m) ;
utiliser_objet(objet) ;
}
}
Ce système repose sur l’utilisation des boîtes aux lettres. Chaque processus a sa boîte aux
lettres.. Dans la zone de données du système d’exploitation, il y a un tampon pour le stockage
temporaire des messages sortants ou entrants. Le receive est bloquant si la boîte aux lettres du
récepteur est vide. Le send est aussi bloquant si la boîte aux lettres du récepteur est pleine.
NB : Des processus communicants utilisant des messages peuvent ne pas avoir des boîtes aux
lettres. On parle dans ce cas de rendez-vous. Un processus récepteur doit être au rendez-vous
quand un message arrive, si non il est perdu.
Quelques solutions :
- envoyer un message d’acquittement à la réception d’un message
- si le message est perdu, il n’y a pas d’acquittement. Dans ce cas, on retransmet
le message
- Si acquittement perdu, on retransmet aussi le message. Le message doit avoir un
numéro pour éviter les duplications à la réception.
17
- un compteur de programme
- un ensemble de registres
- une pile
Il partage avec tous les autres processus légers du même processus :
- le segment de code
- le segment de données
- les ressources du système d’exploitation telles que la table des descripteurs de
fichiers, et la table des signaux
Un processus poids lourd est tout simplement un processus ayant un seul processus léger. Un
processus ne peut rien faire s’il n’a pas de processus léger.
NB : Chaque processus léger a sa propre copie de certaines variables globales telles que
errno.
Les processus légers partagent le processeur de la même façon que les processus poids
lourds. Un processus léger peut créer des processus légers fils. A un instant donné, un
processus léger peut être dans l’un des états étudiés pour les processus poids lourds.
Pour éviter les conflits d’accès, les processus légers utilisent les mécanismes de
synchronisation étudiés pour les processus pour accéder aux ressources partagées.
L’intérêt des processus légers est de permettre une exécution parallèle à l’intérieur
d’un même processus grâce au mécanisme de pseudo-parallélisme où à un parallélisme réel si
en présence de multiprocesseurs. Il existe plusieurs modèles pour l’implémentation des
processus légers :
- le modèle répartiteur / travailleurs. Un processus répartiteur confie les requêtes à
traiter à un ensemble de processus légers travailleurs. Seul le répartiteur a accès
aux boîtes à lettres pour récupérer les requêtes
- le modèle de groupe : chaque processus léger accède à la boîte aux lettres pour
récupérer une requête et la traiter
- le modèle pipeline
Unix peut être vu comme un ensemble de couches superposées les unes au dessus des
autres. Le noyau (quatrième ligne) interagit directement avec le matériel. Les programmes
d’application sont en général indépendants du matériel, ce qui permet leur portabilité. Les
applications au dessus du noyau interagissent avec ce dernier en invoquant les appels système.
Un appel système demande au noyau de réaliser un certain service au profit du programme
18
appelant. Le système unix autorise un périphérique à interrompre le processeur de façon
asynchrone.
A la réception d’une interruption, le noyau sauvegarde le contexte du processus
actif, détermine la cause de l’interruption et exécute le traitant d’interruption approprié. A la
fin de l’exécution du traitant, le noyau restaure le contexte du processus actif et reprend son
exécution comme s’il ne s’était rien passé. Les périphériques sont en général organisés en
niveaux de priorité :
Erreur machine
Horloge
Disques
Périphériques réseaux
Terminaux
Logiciels
La première représente la priorité la plus haute, alors que la sixième ligne représente la
priorité la plus basse.
L’exécution d’un processus se déroule suivant deux modes : le mode esclave (ou
programme, ou utilisateur) et le mode maître (ou noyau, ou superviseur). Les différences entre
les deux modes sont :
- en mode esclave, un processus a accès à ses propres instructions et ses données,
mais pas aux instructions et aux données du noyau. Par contre un processus en
mode maître peut accéder aux données des zones utilisateur et noyau.
- Certaines instructions du jeu d’instruction d’une machine sont privilégiées et ne
peuvent être invoquées qu’en mode maître. Par exemple, l’instruction qui
masque les interruptions, l’instruction qui manipule le mot d’état du processeur.
19
iv.le temps passé en mode noyau
v. le temps passé en mode utilisateur
vi.le temps passé uc consommé
vii. le temps passé en sommeil récemment
viii. une liste des signaux pendants
ix.un pointeur sur la zone utilisateur du processus
x. la priorité du processus
xi.les registres du processeur (CO,SS,CS,DS,…)
xii. l’état du processus
20
c. prêt en mémoire
d. bloqué en mémoire
e. prêt en mémoire virtuelle
f. bloqué en mémoire virtuelle
g. suspendu (suite par exemple au traitement d’un interblocage)
h. En création
i. zombi (terminé)
les transitions sont :
(a,b) : un appel system, une interruption
(b,a) : retour après un appel système ou fin d’un traitant d’interruption
(b,b) : étant en mode noyau, le processus fait un appel system ou reçoit une
interruption
(b,d) : processus bloqué car attente d’un événement (entrée/sortie)
(c,b) : le processeur est alloué au processus
(b,g) : le processus est suspendu (pour résoudre un problème d’interblocage)
(d,c) : l’événement attendu s’est réalisé
(d,f) : il n’y a pas assez de place en mémoire. Le processus est mappé sur disque
(c,e) : pas de place en mémoire : swap out
(e,c) ; place en mémoire : swap in
(f,e) : l’événement attendu s’est réalisé, mais il n’y a pas assez de place en mémoire
(h,e) : processus vient d’être créé, mais il n’y a pas assez de place en mémoire
(h,c) : processus vient d’être créé et il y a de la place en mémoire
(b,i) : le processus s’est terminé mais a encore une entrée dans la table des processus
21
charger le noyau en mémoire et de lancer son exécution. Le noyau initialise les structures de
données internes, c’est-à-dire :
- construit la liste chaînée des tampons libres
- construit la liste chaînée des inodes libres
- initialise la tables des processus
- initialise la tables des inodes
- initialise la tables de description des fichiers ouverts
- monte les systèmes de fichiers
L’exécution du noyau constitue le processus numéro zéro. Quand le noyau a créé et initialisé
toutes les structures de données internes, il crée le processus init qui porte le numéro
d’identification 1. Il est la racine de l’arborescence des processus dans un système Unix. Son
code exécutable est le fichier /sbin/init. Après quelques initialisations en mode maître, il lit le
fichier /etc/inittab et traite les lignes de ce fichier les unes après les autres. Au cours du
traitement de /etc/inittab, init crée des processus qui vont scruter les terminaux à la recherche
des demandes de connexion.
Les niveaux de priorités au dessus de la base des priorités correspondent aux processus en
mode noyau. Les trois derniers niveaux du mode noyau sont interruptibles. Les priorités des
processus utilisateurs sont recalculées périodiquement suivant un algorithme du genre :
Priorité = (Temps CPU récent)/2 + (base des priorités).
NB : Les priorités les moins importantes sont positives.
1- 7 Les interblocages
conditions nécessaires :
j. exclusion mutuelle
k. détention et attente
l. absence de réquisition
m. attente circulaire
Toutes ces quatre conditions doivent être réunies pour qu’il y ait interblocage. Des processus
sont dits en interblocage quand chacun de ces processus attend un événement qui ne peut
provenir que de l’un des processus de l’ensemble.
22
Résolution des interblocages :
Trois approches peuvent être utilisées pour gérer le problème des interblocage :
n. prévention des interblocages : Utiliser une méthode assurant qu’au
moins l’une des conditions citées ci-dessus n’est pas remplie
i. ne pas partager les ressources en exclusion mutuelle.
ii. Un processus qui détient une ressource ne doit pas demander
une autre sans au préalable avoir libérer celle qu’il détient déjà
iii. Si une ressource demandée ne peut pas être allouée à un
processus, celles qu’il détient déjà sont requisitionnées
iv.Imposer un ordre sur les ressources en les numérotant. Un
processus qui détient une ressource de numéro i, ne peut
demander qu’une ressource de numéro supérieure.
o. évitement dynamique des interblocages : Avant d’allouer une ressource à
un processus, s’assurer que cette allocation ne peut pas créer une
situation d’attente circulaire. On peut déterminer pour chaque processus
à l’avance le nombre d’exemplaire de ressources de chaque type dont il
aura besoin et les lui allouer dès le début de son exécution. On alloue
une ressource que si après cette allocation, le système reste dans un état
sûr. Vérifier si l’allocation d’une ressource ne conduit pas à un cycle
dans le graphe d’allocation des ressources du système.
p. Détection des interblocages :Il faut fournir :
i. Un algorithme qui permet de détecter s’il y a interblocage. Si un
seul exemplaire de chaque ressource, on peut utiliser le graphe
d’attente entre processus (qui attend une ressource de qui). Un
cycle implique l’existence d’un interblocage, sinon utiliser un
algorithme du banquier à plusieurs exemplaires de ressources
pour voir si on n’est dans un état sûr ou pas.
ii. Un algorithme permettant de remédier à la situation
d’interblocage. Indiquer le problème à l’opérateur. Il peut alors
tuer des processus ou alors retirer des ressources à certains
processus.
q. la politique de l’autruche : On choisit de ne rien faire, car le prix à payer
pour éviter les interblocages est très élevé .
23
Partie II : Gestion des Informations : Le Cas d’Unix
2-1 Gestion élémentaire de la mémoire
Le gestionnaire de la mémoire est la partie du système d’exploitation qui gère la
hiérarchie de mémoire installée sur un ordinateur. Les adresses manipulées par un
programme en cours d’exécution sont des adresses logiques. L’ensemble des adresses
logiques manipulées par un programme constituent son espace d’adressage logique.
L’ensemble des adresses physiques (ou réelle) constituent l’espace d’adressage physique. Les
compilateurs supposent que le processus aura toute la mémoire physique a sa disposition et
affecte les adresses logiques de zéro à un certain maximum.
2 – 1 – 1 La Monoprogrammation sans va-et-vient ni pagination
Ici le rôle du gestionnaire est de partager la mémoire entre un seul programme
utilisateur et le système d’exploitation. Trois organisations possibles sont souvent
rencontrées :
a) le système d’exploitation se trouve aux adresses basses de la mémoire (RAM). Le
programme utilisateur se trouve juste au dessus. (Ex. dans les premiers
ordinateurs)
b) Les gestionnaires des périphériques sont en mémoire morte (ROM) et le leste du
système d’exploitation se trouve aux adresses basses de la mémoire. Le
programme utilisateur se trouve au dessus de la partie du système en RAM. (Ex :
Les ordinateurs personnels tournant sous ms-dos)
c) Le système d’exploitation se trouve en mémoire morte (ROM) et le programme
utilisateur en RAM. (Ex : les systèmes embarqués et ordinateurs de poche).
Le comportement du système peut être donné par l’algorithme simple suivant :
Tant que (vrai) faire
- Afficher le prompt
- Lire une requête
- Exécuter la reqûte
Ftantque
Dans un tel système, la gestion de la mémoire est assez simple. Un processus obtient toute la
mémoire qu’il demande. La seule protection à faire est de s’assurer que le processus
utilisateur n’écrase pas le système d’exploitation. Dans MSDOS, une telle protection n’était
même pas assurée.
La technique de recouvrement :
C’est une technique qui consiste à découper le programme à exécuter en plusieurs
morceaux de telle sorte que chaque morceau puisse tenir en mémoire centrale. Les morceaux
doivent être indépendants, c’est-à-dire qu’aucun morceau ne doit faire appel à un autre. La
décomposition en morceaux indépendants est faite par le programmeur en incluant à certains
points du code, certaines instructions spéciales. A la compilation, le compilateur prépare les
différents modules indépendants et les stocke sur le disque. Une instruction placée à la fin de
chaque module par le programmeur permet de rechercher le prochain bloc sur disque et de le
placer en mémoire à la place qu’occupait le module précédent.
Cette technique était utilisait dans les vieux systèmes à une époque où la technologie
ne permettait pas de réaliser des mémoires centrales de très grande capacité. Pour exécuter des
gros programmes sous MSDOS, on était obligé de recourir à cette technique. Aujourd’hui elle
est devenue obsolète.
2 – 1 – 2 Multiprogrammation
24
Ici on autorise l’exécution de plusieurs processus utilisateurs. Lorsqu’un processus est
bloqué en attente des entrées/sorties, un autre processus prêt peut devenir actif. On augmente
ainsi le taux d’utilisation du processeur. Ceci exige que plusieurs programmes soient chargés
en mémoire à un instant donné. La mémoire est donc divisée en partitions, chacune contenant
un seul processus. Les partitions sont de taille fixe. La mémoire est divisée en n partitions de
tailles si possible inégales. Le partitionnement est fait au démarrage du système. La taille
d’une partition ne change pas au cours de la session. A chaque partition est associée une file
d’attente. Un processus est toujours chargé en mémoire dans la file d’attente de la plus petite
partition pouvant le contenir.
Inconvénients : On peut avoir les files de petites partitions pleines tandis que les files des
grandes partitions sont vides. Dans ce cas les petits travaux doivent attendre pour accéder à la
mémoire alors qu’elle contient des partitions inutilisées.
Une autre organisation consiste à utiliser une seule file d’attente. Dès qu’une partition
est libre, tout processus pouvant y tenir et se trouvant en tête de file est chargé.
Allouer une grande partition à un petit processus crée un gaspillage de la mémoire.
Pour éviter ce gaspillage, dès qu’une partition devient libre, on parcourt l’unique file d’attente
pour rechercher le plus grand processus pouvant y tenir. Ceci peut une fois de plus pénaliser
les petits processus. On évite cela en associant à chaque petit processus un compteur initialisé
à zéro. Chaque fois qu’il est ignoré, son compteur est incrémenté d’une unité. Un processus
ne pourra pas être ignoré k fois, où k est un paramètre système.
Problèmes :
- La réallocation ou translation d’adresse. Les compilateurs supposent que les
adresses manipulées par les programmes commencent toutes à zéro. Il faut donc
au cours du chargement en mémoire d’un processus, recalculer les adresses
manipulées en ajoutant à chaque adresse l’adresse de début de la partition.
- La protection. Les partitions doivent être protégées pour éviter qu’un processus
chargé dans une partition donnée n’accède à une autre partition.
Une solution est d’utiliser un registre de base et un registre de limite. Le registre de
base est chargé avec l’adresse de début de la partition et le registre de limite avec la
longueur de la partition.
2 - 2 Le va-et-vient
Le va-et-vient apporte une solution au problème posé par la multiprogrammation qui
consiste à exécuter un grand nombre de processus que de partitions disponibles pouvant les
contenir. Un processus qui libère sa partition est envoyé intégralement sur disque. Ici les
partitions ne sont plus de taille fixe. Leur nombre et leurs tailles sont variables. L’opération de
va-et-vient peut créer des trous dans la mémoire.
25
Pour charger un processus de k unités d’allocation, il faut trouver k trous libres
consécutifs en mémoire. Il peut être nécessaire de parcourir toute la mémoire pour trouver un
tel espace.
2 – 3 La mémoire virtuelle
Elle permet de résoudre un certain nombre de problèmes :
a) Exécuter un programme dont la taille est supérieure à la mémoire disponible
b) Exécuter un ensemble de programme dont la taille totale est supérieure à la mémoire
disponible.
Les programmes sont stockés en mémoire virtuelle qui est en réalité une partie du disque.
Sous LINUX par exemple, la mémoire virtuelle peut être une partition du disque ou un fichier
spécial. Les parties du programme ou le programme tout entier font des va-et-vient entre la
mémoire virtuelle sur disque et la mémoire centrale. L’espace d’adressage réel d’un
processus est partie de la mémoire centrale qui peut être contigüe ou non. La correspondance
adresse virtuelle adresse physique est faite pendant l’exécution par une unité spéciale de la
machine appelée MMU (Memory Management Unit).
2 - 3 - 1 la pagination
En général chaque programme considère que la mémoire qu’il utilise est numérotée de
0 à un certain maximum. Le nombre d’adresses logiques utilisées peut être très supérieur à la
taille de la mémoire physique. L’espace virtuel contigu occupé par le processus est divisé en
pages de même taille. La mémoire centrale est divisée en cadres de page de taille égale à celle
26
des pages. Chaque processus a dans son contexte une table des pages qui permet de faire la
correspondance entre les adresses virtuelles et les adresses physiques. Chaque adresse
virtuelle comporte deux parties. Un numéro de page et un déplacement dans la page. Le
numéro de page sert d’index dans la table des pages. Il y a autant d’entrées dans la table des
pages qu’il y a de pages dans la mémoire virtuelle. Chaque entrée de la table des pages est une
structure comportant :
- un bit de présence. Ce bit vaut 1 si la page correspondante est effectivement
mappée en mémoire centrale. Il vaut 0 dans le cas contraire.
- Un numéro de cadre de page. Ce numéro indique dans quelle page physique la
page virtuelle a été réellement mappée.
- Un bit de protection : 0 indique un accès en lecture/écriture, et 1 indique un
accès en lecture seule. La protection peut aussi très bien être codée sur trois
bits : un bit pour l’accès en lecture (1=oui, 0=non), un bit pour l’accès en
écriture, un bit pour l’exécution.
- Les bits de modification : permet de savoir si la page a été modifiée depuis sa
dernière sauvegarde. Si ce bit est positionné à 1, la page doit être enregistrée sur
disque en cas de son remplacement par l’algorithme de remplacement des pages.
- Le bit de référence : ce bit est positionné chaque fois qu’une page est accédée en
lecture ou en écriture.
Quand le processeur veut accéder à une zone mémoire, il passe l’adresse virtuelle au
MMU. Le MMU transforme cette adresse virtuelle en adresse physique, en se servant de la
table des pages du processus. Deux situations peuvent se produire quand le MMU accède à
l’entrée correspondante de la table des pages:
- La page recherchée se trouve en mémoire. Il suffit alors d’accéder à
l’information recherchée.
- La page recherchée ne se trouve pas en mémoire. Il se produit alors un
déroutement appelé défaut de page. Le traitement du défaut de page peut être
simple ou complexe. Il est simple s’il y a un cadre de page libre. Il suffit alors
de charger la page virtuelle dans ce cadre de page. Il est complexe s’il n’y a pas
de cadre de page libre. Il faut alors à l’aide d’un algorithme de remplacement
des pages libérer un cadre de page, y stocker la mémoire virtuelle
correspondante avant d’accéder à l’information recherchée.
Le système d’exploitation gère dans ses structures internes une table des cadres de pages.
Chaque entrée de cette table est une structure qui contient comme informations :
- indication si le cadre de page est libre
- identification du processus à qui le cadre de page est alloué
- adresse de début du cadre de page.
27
- Utilisation d’une mémoire associative : L’accès est rapide, mais il faudra
nettoyer la mémoire associative chaque fois qu’on change de processus. En effet
on remarque que certaines pages sont en général utilisées plus fréquemment que
d’autres. On peut donc garder les tables de pages en mémoire centrale et stocker
une petite table de page en mémoire associative contenant les pages les plus
fréquemment. Une entrée de cette table en mémoire associative contient les
informations suivantes :
i. Le numéro de page virtuelle
ii. Un bit de modification
iii.Un bit de protection rwx
iv.Le numéro du cadre de page
v. Un bit de validité qui indique si l’entrée est utilisée ou non.
NB : La recherche d’une page virtuelle se fait en comparant simultanément son numéro à
toutes les entrées valide de la mémoire associative. Si trouvé, alors on récupère l’adresse
physique. Si non trouvé, on charge la page en mémoire associative à partir de la table des
pages en mémoire.
28
permet d’indiquer toujours l’unique entrée de la table de pages inversée où on trouvera un
pointeur sur une liste chaînée dont chaque nœud indique :
- un numéro de page virtuelle
- un numéro de cadre de page.
2 – 4 la segmentation
Elle permet de séparer les programmes et les données dans des espaces logiquement
indépendants, et facilite le partage et la protection des données. L’idée de la segmentation est
d’utilisé pour chaque processus un espace d’adressage qui ne soit pas forcément contigu.
L’espace d’adressage logique est donc une collection de segments. Un segment est une zone
contiguë de la mémoire de taille variant de 0 à un certain maximum. Un segment peut
contenir des données, la pile ou le code du processus. Il ne contient jamais un mélange
d’objets de types différents. L’adresse logique spécifie le segment et le déplacement à
29
l’intérieur du segment. Chaque adresse manipulée comporte les deux parties. Un processus a
une table des segments. Une entrée de la table des segments a :
- une adresse de base du segment, qui est l’adresse physique de début du segment
- la taille du segment
Les registres de la machine peuvent être utilisés pour sauvegarder la table des segments. On
peut aussi la stocker en mémoire. Un registre de la machine pointe sur la table des segments.
Un autre registre indique la taille de la table des segments. L’utilisation des segments facilite
la gestion du partage des zones mémoire par les processus. Au niveau du système
d’exploitation des tables permettent de garder les informations sur les segments. Les segments
peuvent devenir très grands et devenir encombrant en mémoire, d’où la nécessité de les
paginer.
30
- le bit suivant indique le type de sélecteur (0=GDT, 1=LDT)
- le reste des bits représente un index dans la table des descripteurs de segments
appropriée. (13 bits 8K descripteurs différents)
Une entrée dans une table des descripteurs de segment est une structure dont la taille totale est
de huit octets :
- 2 octets pour la taille d’un segment (la taille varie de 1 à 64ko)
- 3 octets suivants donne l’adresse de début du segment en mémoire centrale (24 bits
pour une adresse au lieu des 20 bits du mode réel)
- 1 octet de contrôle, appelé registre des indicateurs. Il indique le type du segment
(segment de données, segment de code ou segment système), le bit de protection
contre l’écriture, bit indiquant si le segment peut être lu, le bit de présence, le bit
d’accès, le niveau de privilège qui est codé sur deux bits : le niveau 0 est le
privilège maximal, le niveau 1 est celui des fonctions systèmes appelées par les
applications et les utilitaires, le niveau 2 est celui des extensions du systèmes
(serveur SQL, gestionnaire de réseau), le niveau 3 est celui des applications qui se
déroulent sous le contrôle du système
- les deux derniers octets sont réservés pour un usage future
La table des descripteurs globaux contient des entrées pour :
- les segments de codes du systèmes d’exploitation
- les segments de données du système d’exploitation
- les descripteurs des segments de mémoire qui contiennent les tables locales des
descripteurs
- les descripteurs de segments spéciaux appelés porte d’appel (CALL GATE). Ils ne
définissent pas un segment mémoire mais un point d’entrée dans une routine dont
le segment de code peut avoir un privilège de plus haut rang que l’appelant.
L’entrée indique aussi clairement l’offset dans le segment. On évite ainsi
d’autoriser l’accès à tout un segment du système. Une porte d’appel se comporte
comme un segment de données. Le système d’exploitation met leur privilège au
niveau 3, pour permettre qu’elles soient appelée par toutes les applications.
- Descripteur TSS ( Task State Segment) : Zone mémoire ou le contenu de tous les
registres est sauvegardé ou permettant de restaurer le contenu des registres.
Parmi les 16 bits d’un sélecteur de segment on a un bit indiquant si le segment est une table
de segments globale (0) ou une table de segments locale (1), et qui précède les deux derniers
bits qui eux indiquent le niveau de privilège du sélecteur (RPL :requested privilege level). Le
descripteur d’un segment de code a un niveau de privilège (DPL :Descriptor Privilege level).
Pour l’accès aux données et aux instructions, on doit avoir RPL=DPL et pour l’accès aux
données seulement, on doit avoir RPL<=DPL.
Le mode protégé permet de gérer jusqu’à 8193 tables de descripteur, dont une table
globale et 8192 tables locales. A un moment donné, deux tables seulement peuvent être
actives : la table des descripteurs globaux (GDT) et une table des descripteurs locaux
(LDT :local descriptor table). Les deux registres GDTR et LDTR contiennent les références
de ces deux tables. Le registre GDTR contient l’adresse physique des tables globales et LDTR
contient la référence d’une LDT dans la table des descripteurs globale.
La table globale contient les segments de code et de données du système, les segments
contenant les tables des descripteurs locaux. Pour faire une commutation, il suffit de changer
le contenu de LDTR.
31
Avec les 80386, 80486, et les pentium, le mécanisme est presque le même. Seulement
les registres sont plus grands, et l’architecture plus sophistiquée permettent :
- d’améliorer l’efficacité dans le processus de communication de contexte.
- D’accéder à plus de mémoire
32
Choix du processus à enlever de la mémoire
a) Il se fait parmi les processus bloqués en attente d’un événement.
b) Il se fait parmi les processus prêts si aucun processus n’est bloqué.
S’il y en a plusieurs, celui dont la somme du temps de résidence en mémoire et de la priorité
est la plus grande est enlevé
2 – 3 – 2 La pagination
La mémoire centrale est organisée en trois parties :
- le noyau
- la carte mémoire
- le reste de la mémoire divisé en cadre de pages (page frame), chacun pouvant
contenir une page de texte, de données, de piles, de la table des pages ou de la liste
des pages libres.
Le noyau et la carte mémoire ne sont jamais envoyé sur disque. La carte mémoire contient des
informations décrivant le contenu des cadres de pages. L’entrée i de la carte mémoire décrit
le cadre de pages numéro i et contient les informations suivantes :
- référence de l’entrée suivante
- référence de l’entrée précédente
Ces deux entrées sont utilisées lorsque le cadre de page est dans la liste des cadres libres.
- le type de périphérique de la mémoire virtuelle
- numéro du périphérique
- code de hachage du bloc
Si le cadre de page contient une page, ces trois informations permettent de localiser
l’emplacement réservé à la page sur le périphérique.
- référence dans la table des processus
- indication si la page contient du texte, des données ou une partie de la pile.
- Décalage dans le segment
- Indicateurs utilisés par l’algorithme de pagination :
- Bit indiquant si la page est libre
- Bit de verrouillage
- Bit indiquant si la page est demandée
- …etc
Lorsqu’un processus demande une page, le système ôte le premier cadre libre de la liste des
cadres libres et y place la page. Si la liste des cadres de pages libres est vide, le processus est
suspendu en attendant que le voleur de pages (ou démon de pages) libère des cadres de pages.
Le démon de pages a pour pid 2. Le permuteur a pour numéro 0 et init est le numéro 1.
33
Algorithme de remplacement des pages.
Le démon de pages se réveille toutes les 250ms pour exécuter cet algorithme.
1) Le démon de pages vérifie si le nombre de cadres de pages libres
est au moins égal à un paramètre système appelé lotsfree (~1/4 de
la mémoire). Si le démon constate qu’il y a plus de lotsfree
cadres de pages libres il s’endort. Si au contraire il y a moins de
lotsfree cadres de pages libres, il libère des pages de la mémoire
jusqu’à ce qu’il y ait au moins lotsfree cadres de pages libres.
2) On utilise deux paramètres min et max. Le démon de page
s ‘exécute lorsqu’il y a moins de min cadres de pages libres et
continu son exécution jusqu’à ce qu’il y ait au moins max cadres
de pages libres.
Dans tous les cas le choix des pages à libérer se fait suivant l’algorithme de l’horloge :
Les pages sont disposées dans une liste circulaire. Un pointeur permet de référencer la
page la plus ancienne. On parcourt la liste dans le sens des aiguilles d’une montre. Au premier
passage, on met les bits de référence à zéro. Au second passage, les pages dont les bits de
référence sont toujours à zéro n’ont pas été référencé depuis. On les libère en les recopiant sur
le disque.
NB : On peut utiliser une horloge à deux aiguilles.
34
2 – 4 Gestion des fichiers
Le système de fichiers désigne l’ensemble de l’organisation utilisée par un système
d’exploitation pour représenter les fichiers. Le système de fichiers est lié en général aux
périphériques utilisés. Il propose une organisation à utiliser par les unités de stockage de
masse pour permettre de:
- Organiser les unités de masse pour leur permettre d’accueillir les fichiers
- Stocker les différents blocs de données d’un fichier
En général un système d’exploitation implémente plusieurs systèmes de fichiers, ce qui lui
permet d’utiliser différents types de périphériques.
Le gestionnaire des fichiers est la partie du système d’exploitation qui implémente les
différents systèmes de fichiers de ce système. Il offre des services permettant :
- de stocker les fichiers sur les périphériques de masse en assurant leur sécurité
- de fournir une interface permettant aux utilisateurs de manipuler les fichiers (les
structurer, les nommer, les modifier, …etc)
- d’assurer l’indépendance vis-à-vis des périphériques, c’est-à-dire permettre
d’accéder aux fichiers de la même façon quel que soit le périphérique de
stockage
- de retrouver efficacement les différents blocs d’un fichier
L’utilisateur ne cherche pas à savoir quel est la taille des blocs d’un disque, sur quels blocs
ses fichiers sont stockés, quel est le nombre de secteurs d’un bloc logique, comment les blocs
libres ou occupés du disque sont repérés ? Ce qui est important pour l’utilisateur c’est que le
système lui présente une interface permettant de créer et d’accéder à ses fichiers. Le système
de fichiers cache donc la complexité liée à la gestion des fichiers dans un système
informatique.
35
Structure d’un inode :
- identificateur de l’utilisateur (UID)
- type de fichier (catalogue, ordinaire, spécial bloc, spécial caractère,fifo).
Type=0 inode libre
- droits d’accès aux fichiers
- date création,
- date dernier accès
- date dernière modification du fichier
- date dernière modification du inode
- nombre de liens du fichier
- pointeurs sans indirection sur des blocs de données
- pointeurs simple indirection sur les blocs de données
- pointeurs double indirection sur les blocs de données
- pointeurs triple indirection sur les blocs de données
- taille du fichier en octets
NB : l’inode n’indique aucun chemin d’accès au fichier. Le inode numéro 1 est réservé à la
gestion des blocs défectueux, le inode numéro 2 est réservé à répertoire racine.
36
La gestion des entrées/sorties sous unix est intégrée dans le système de fichiers. En
effet les périphériques sont considérés comme des fichiers spéciaux. A chaque périphérique
est associé un chemin à partir du répertoire /dev, par exemple /dev/lp pour l’imprimante,
/dev/net pour le réseau , /dev/hda pour le premier disque dur. Ces fichiers sont accessibles
exactement comme on accède aux autres fichiers. On a besoin, ni de commandes spéciales, ni
d’appels système spécifiques. Les programmes peuvent ouvrir et lire les fichiers spéciaux
comme s’il s’agissait de fichiers ordinaires. Les mécanismes de protection sont utilisés pour
limiter les accès aux périphériques. On distingue deux types de fichiers spéciaux :
- les fichiers spéciaux blocs
- les spéciaux caractères
A chaque périphérique est associé un pilote qui est la partie du système qui tient compte des
spécificités du périphérique. Il présente au reste du système une interface qui permet
d’accéder au périphérique en masquant le matériel.
NB : Dans certains cas, le gestionnaire de ligne peut être court –circuiter et les données
brutes sont transférées. C’est par exemple le cas quand il faut transférer les données entre
deux ordinateurs en utilisant une ligne série.
37
Partie IV : Introduction aux systèmes d’exploitation distribués
3-1 Définitions et caractéristiques des systèmes distribués
Un système distribué est un ensemble d’unités centrales autonomes reliées par des
voies de communication haut débit. Sur le plan physique, il s’agit d’un réseau d’unités
centrales. Mais au niveau logiciels, l’ensemble est géré comme un tout de façon transparente
aux utilisateurs. Un système d’exploitation distribué est un système d’exploitation capable de
piloter ce genre de système de sorte que chaque utilisateur ait l’illusion de travailler sur une
unique machine très puissante.
38
Un système d’exploitation distribué est un système qui s’exécute sur un ensemble de
machines sans mémoire partagée, mais que pourtant l’utilisateur voit comme une seule et
unique machine.
b) la souplesse
La structure du système doit faciliter son évolution. Pour cela, le noyau doit être le
plus petit possible et assurer quatre services minimum :
- les mécanismes de communication inter processus
- la gestion de la mémoire
- la gestion et l’ordonnancement des processus
- la gestion des entrées / sorties de bas niveau
Le noyau ne fournit ni système de fichiers, ni les répertoires. Les services que le noyau ne
fournit pas sont implantés par des serveurs de niveau utilisateurs. Pour obtenir un service, un
processus envoie un message au serveur approprié. Il existe une interface bien définie pour
chaque service. On peut implémenter et installer des nouveaux services sans avoir à arrêter le
système ou le réinitialiser avec un nouveau noyau. Le système est donc très souple.
c) la fiabilité
Le système doit rester opérationnel en cas de panne, voire de plusieurs serveurs. Les
fichiers et autres ressources doivent être protégés contre une utilisation abusive. Il faut assurer
une tolérance aux pannes au niveau de l’utilisation des fichiers en introduisant par exemple
des serveurs de fichiers sans état.
d) les performances
L’exécution des programmes devrait être plus rapide que sur un système
monoprocesseur. Les performances peuvent être mesurées par :
- le temps de réponse
- le débit
- le taux d’utilisation du système
- le pourcentage utilisé de la bande passante du réseau
39
Le système d’exploitation est structuré en un ensemble de processus. Certains sont des
serveurs et fournissent des services à d’autres appelés clients. Les serveurs et les clients
tournent sous le même micro-noyau et sont des processus utilisateur. Sur la même machine,
on peut avoir des clients, des serveurs ou des clients et des serveurs. Les clients et les serveurs
utilisent un protocole simple pour communiquer de type question / réponse. Le client
demande un service au serveur. Ce dernier lui retourne une réponse. Le protocole sous jacent
a trois couches :
- la couche physique
- la couche liaison de données
- la couche question / réponse . Cette couche définit l’ensemble des questions et
des réponses.
Les services de communications fournis par le micro-noyau se limitent à deux appels système
qui peuvent être implémenter par deux procédures de la bibliothèque :
Send (destination, &mptr)
Receive (addr, &mptr)
Send envoie le message pointé par mptr à un processus identifié par destination.
Il existe plusieurs variantes de ces primitives.
3-2-1-1 L’adressage
Le client doit connaître l’adresse du serveur à qui il envoie un message. Plusieurs
méthodes peuvent être utilisées pour réaliser cet adressage :
1) un seul processus tourne sur la machine. Dans ce cas, on peut utiliser le
numéro de la machine. Le noyau sait alors qu’il y a un seul processus
utilisateur, et donc c’est à lui que le message est destiné.
2) Plusieurs processus tournent sur la machine. Dans ce cas le numéro de la
machine n’est pas suffisant.
i. Numéro de machine + numéro de processus : Chaque machine peut
numéroter ses processus à partir de zéro. L’adresse est donc composée
de deux parties : La première partie indique le numéro du processus et
la deuxième le numéro de la machine (pid@machine)
ii. Numéro de machine + un numéro d’identification : chaque
processus serveur à son lancement choisi un numéro aléatoire (sur 32
bits par exemple). ( un port d’écoute) . l’adresse est numéro machine +
numéro d’identification.
iii. Choix aléatoire de l’identificateur de processus. Chaque serveur
choisir son numéro dans un espace d’adressage (sur 64 bits par
exemple). Le processus émetteur diffuse un message sur le réseau local
contenant le numéro de processus. La machine qui reçoit ce message
répond à l’expéditeur (Cette adresse est gardée dans le cache).
iv. Utiliser un serveur de noms : L’expéditeur demande à un serveur
de noms où se trouve le serveur en utilisant une chaîne de caractères.
Le serveur de noms reçoit ce message et répond au client.
NB : Inconvénients
iii : il n’y a pas de transparence. Le client doit connaître la machine du serveur.
iii : la diffusion des messages sur le réseau peut surcharger le réseau
iv : le serveur de noms peut tomber en panne
40
Les primitives synchrones : Le processus qui fait un send est bloqué jusqu’à la fin de la
transmission du message. De la même façon, le processus qui fait receive est bloqué jusqu’à
la réception totale du message. La primitive receive peut indiquer de quel client viendra le
message. Dans ce cas l’appelant sera bloqué jusqu’à ce que ce client particulier lui envoie un
message.
Primitives asynchrones : Le processus qui exécute l’appel système send ou receive n’est pas
bloqué. Il continue son exécution après l’instruction qui suit send ou receive. L’inconv énient
est que le processus qui appelle send ne doit pas modifier son tampon jusqu’à transfert
complet du message et ne sait pas quand le tampon est complètement transféré.
Solutions :
a) le noyau recopie le tampon dans un de ses tampons internes, et plus tard dans un de
ses tampons de transmission. il y a une copie de trop, ce qui implique un gaspillage
b) interrompre l’expéditeur pour lui dire que son message est parti, et donc que son
tampon est à nouveau disponible. le client et le serveur sont des processus
utilisateurs, donc la programmation des interruptions est difficiles.
Primitives avec tampon : Généralement elle utilisent des boîtes aux lettres. Le serveur
demande à son noyau de lui créer une boîte aux lettre et lui communique son adresse à
rechercher dans les messages. Le destinataire se contente de lire sa boîte aux lettres.
Malheureusement la taille des boîtes aux lettres est limitée. Quand elle sont saturées, le noyau
peut :
- détruire le message
- armer un temporisateur qui lui permet de détruire le message après un certain
temps.
Primitives sans tampons : le processus à l’autre bout de la communication doit être au rendez-
vous. Le noyau peut garder le message pendant un certain dans un tampon interne. Au bout de
ce temps si le processus n’est pas toujours au rendez-vous, il détruit le message.
Primitives non fiables : L’expéditeur n’a aucune garantie sur la réception du message. Le
problème de savoir si le message est arrivé est laissé à la charge des utilisateurs.
Primitives fiables : L’expéditeur attend un accusé de réception pour savoir si le message a été
reçu. Les accusé de réception sont traités au niveau des noyaux. Deux formes possibles :
a) demande (client au serveur) – Ack (noyau à noyau) – réponse (serveur au client) –
Ack (noyau à noyau).
b) Demande (client au serveur) – réponse (serveur au client) – ack (noyau à noyau)
NB : Avec le modèle client / serveur les communications ressemblent aux entrées / sorties. On
voudrait qu’elles ressemblent aux communications dans les systèmes centralisés.
41
utilise les fonctions classiques de la bibliothèque, mais elles seront exécutées de façon
transparente sur le serveur. En résumé tout se passe de la manière suivante :
- la procédure du client appelle le subrogé client de façon classique
- le subrogé client construit un message et fait un déroutement vers le noyau
- le noyau envoie le message au noyau distant
- le noyau distant donne le message au subrogé serveur
- le subrogé serveur récupère les paramètres et appelle le serveur
- le serveur effectue le travail et envoie les résultats au subrogé serveur
- le subrogé serveur met les résultats dans un message et fait un déroutement vers
le noyau
- le noyau distant envoie le message au noyau du client
- le noyau du client donne le message au subrogé client
- le subrogé client en extrait le résultat et le donne au client
NB : Après l’appel send, le subrogé client fait un un appel à receive et se bloque jusqu’à ce
que la réponse arrive. Quand le message arrive au niveau du serveur, le noyau le donne au
subrogé serveur. Ce dernier a normalement fait un receive et est bloqué en attente de
messages. Il désassemble le message et en extrait :
- l’identification de la procédure à exécuter
- les paramètres de la procédure
et passe ces informations au serveur qui appelle la procédure de façon classique. Au niveau du
serveur, les références aux variables sont en fait des références à des emplacement du tampon
du subrogé serveur.
42
Elles sont en général automatiquement générées. Un compilateur approprié lit la
spécification du serveur qui indique :
- le nom du serveur
- la version du serveur
- les procédures du serveur
- les paramètres de chaque procédure
- le type de chaque paramètre
- indication pour chaque paramètre si entrée ou sortie
- le type du résultat renvoyé par chaque procédure
et génère les procédures des subrogés.
Inconvénients :
L’importation et l’exportation des interfaces fait perdre beaucoup de temps, car les
processus clients sont généralement brefs.
Quelques problèmes :
- le client est incapable de localiser le serveur
- la demande du client au serveur est perdue
- la réponse du serveur au client est perdue
- le serveur tombe en panne après avoir reçu une demande
- le client tombe en panne après avoir émis une demande
43
Dans la synchronisation des processus, le plus important n’est pas de savoir à quelle
heure réelle un événement a eu lieu. Très souvent l’ordre de précédence entre deux événement
est largement suffisant. Soient a et b deux événement on les date de la façon suivante :
- si a précède b dans le même processus, alors t(a)<t(b)
- si a et b sont l’émission ou la réception d’un message alors t(a)<t(b)
où t(x) est le temps logique associé à l’événement x. Chaque machine a son horloge locale qui
avance à son rythme ; si ab (a précède b) alors t(a)<t(b). En outre on suppose que :
- entre deux événements sur une machine l’horloge doit avancer d’au moins une
unité.
- Si une machine reçoit un message dont le temps associé est supérieur au temps
de son horloge locale, il avance le temps de son horloge locale au temps reçu
plus une unité.
Algorithmes de synchronisation d’horloge :
a) algorithme de cristian
on suppose que l’une des machine a un receveur du temps universel (Tuc). Cette machine est
le serveur du temps. Toutes les autres machines s’adressent à ce serveur pour avoir le temps.
Soit To le temps de la demande et T1 le temps de la réponse, sur la machine locale. Le temps
de transfert est en moyenne (T1-To)/2. Le compteur de temps de la machine locale est
réinitialisé à Tuc+(T1-To)/2.
b) algorithme de berkeley
On a encore un serveur de temps. Mais c’est ce dernier qui scrute chaque machine pour lui
demander l’heure. A partir des différentes réponse, il calcule l’heure moyenne et demande à
chaque machine d’avancer son horloge ou de la retarder (le serveur aussi).
Ici le serveur n’a pas besoin d’avoir un récepteur de temps universel. Son heure peut
être régulièrement ajuster par un opérateur.
44
- l’heure courante (estampille)
Ce message est envoyé à tous les autres processus y compris à l’émetteur. On utilise un
système de communication fiable. A la réception d’un message un processus peut réagir de
l’une des façons suivantes :
- le récepteur n’est pas dans la section critique et ne veut pas y entrer. Il retourne
OK à l’émetteur.
- Le récepteur est déjà dans la section critique, il ne répond pas et mémorise le
message dans sa file d’attente
- Le récepteur veut entrer en section critique, mais ne l’a pas encore fait. Il
compare l’estampille du message qu’il vient de recevoir avec celle des messages
qu’il a fabriqué lui-même et fait parvenir aux autres processus. La plus ancienne
estampille remporte. Si le récepteur a une estampille plus récente, il répond par
OK à l’émetteur. Si le récepteur a la plus vieille estampille, il ne répond pas et
mémorise le message.
Après avoir envoyé ses demandes d’entrée en section critique, un processus se met en attente
jusqu’à ce que tous les autres lui donne leur autorisation. Après avoir reçu toutes les
autorisations, un processus peut entrer en section critique. Quand il en sort, il envoie un OK à
tous les processus présents dans sa file d’attente.
Inconvénients : Chaque processus participe à la coordination. Si un processus se plante, il ne
répondra plus aux requêtes. Le silence sera interprété comme un refus. La probabilité que l’un
des coordinateurs se plante est plus élevée. On a fait pire que l’algorithme centralisé. Le trafic
a augmenté dans le réseau.
Une amélioration : Un processus doit toujours envoyé un message de refus s’il ne veut pas
donner de OK. Sans réponse après un certain temps, on peut conclure que le destinataire est
en panne.
45
3-3-3 Exclusion mutuelle à l’aide des algorithmes à Election
On suppose que chaque processus a un numéro unique permettant de l’identifier. On
suppose aussi que chaque processus connaît les numéros de tous les autres processus.
Dans un système de fichiers distribué, le service de gestion des fichiers fait appel à des unités
de stockage situées sur différentes machines interconnectées à travers des liens de
communication. Le système de gestion de fichier est réalisé sur chaque machine par un
processus appelé « serveur de fichiers ». Un serveur de fichiers s’exécute sur une machine
pour rendre les services définies par le service de fichiers. Les différents serveurs de fichiers
d’un système distribué peuvent :
46
- jouer le même rôle
- être chacun spécialisé dans certaines tâches (service unix, service windows, …
etc)
Les utilisateurs n’ont pas à savoir où sont les serveurs de fichiers. Ils appellent tout
simplement les services indiqués et ceux-ci sont réalisés de façon transparente.
47
Méthode commentaires
Sémantique unix Chaque opération sur un fichier est instantanément visible par
les autres processus
Sémantique de session Aucun changement n’est visible avant la fermeture du fichier
Fichiers immuables Pas de mise à jour possible ; simplification du partage et de la
duplication
transactions Tous les changements se font en tout ou rien
La différence entre les systèmes se situe souvent au niveau de la structuration des services de
fichiers et de gestion. Très souvent on sépare le service de fichiers et le service de gestion des
fichiers. Dans ce cas l’accès à un fichier implique l’accès préalable au service de gestion pour
transformer le nom symbolique en nom interne que le serveur utilisera. La hiérarchie de
répertoire est souvent répartie sur plusieurs serveurs. L’arborescence est la même sur tous les
serveurs ; cependant le nom de certains fichiers sur certains serveurs ne sont en réalité que des
pointeurs vers les fichiers réels. Un serveur peut être avec ou sans état .
48
Travaux Dirigés et Pratiques :
h) Opérations sur les processus en C et Java (création, destruction, manipulation, …etc)
i) Les processus légers en C et Java
j) La communication inter processus en C et Java
k) Etude de NFS : Systèmes de fichiers distribués ou réseau ?
l) Conception d’un noyau de synchronisation
49
TRAVAUX PRATIQUES EN LANGAGE C
50