diff --git a/content/systemes_exploitation/4-Memoire/images/base_limit.svg b/content/systemes_exploitation/4-Memoire/images/base_limit.svg
new file mode 100644
index 0000000..bc5a89c
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/base_limit.svg
@@ -0,0 +1,767 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/base_limit_right.svg b/content/systemes_exploitation/4-Memoire/images/base_limit_right.svg
new file mode 100644
index 0000000..a9f268c
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/base_limit_right.svg
@@ -0,0 +1,1169 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/base_limit_segments.svg b/content/systemes_exploitation/4-Memoire/images/base_limit_segments.svg
new file mode 100644
index 0000000..398690e
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/base_limit_segments.svg
@@ -0,0 +1,1011 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/bits_adresse_memoire.svg b/content/systemes_exploitation/4-Memoire/images/bits_adresse_memoire.svg
new file mode 100644
index 0000000..77f30d4
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/bits_adresse_memoire.svg
@@ -0,0 +1,82 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/memoire_paginee_correspondance.svg b/content/systemes_exploitation/4-Memoire/images/memoire_paginee_correspondance.svg
new file mode 100644
index 0000000..d438feb
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/memoire_paginee_correspondance.svg
@@ -0,0 +1,470 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/mmu.svg b/content/systemes_exploitation/4-Memoire/images/mmu.svg
new file mode 100644
index 0000000..ce038ef
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/mmu.svg
@@ -0,0 +1,1374 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/mmu_right_exception.svg b/content/systemes_exploitation/4-Memoire/images/mmu_right_exception.svg
new file mode 100644
index 0000000..c29f4fb
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/mmu_right_exception.svg
@@ -0,0 +1,1195 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/mmu_simple.svg b/content/systemes_exploitation/4-Memoire/images/mmu_simple.svg
new file mode 100644
index 0000000..151771f
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/mmu_simple.svg
@@ -0,0 +1,1119 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/mmu_table_2.svg b/content/systemes_exploitation/4-Memoire/images/mmu_table_2.svg
new file mode 100644
index 0000000..2d4e3d2
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/mmu_table_2.svg
@@ -0,0 +1,1712 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/page_noyau.svg b/content/systemes_exploitation/4-Memoire/images/page_noyau.svg
new file mode 100644
index 0000000..64ef05e
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/page_noyau.svg
@@ -0,0 +1,938 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/images/page_noyau_meltdown.svg b/content/systemes_exploitation/4-Memoire/images/page_noyau_meltdown.svg
new file mode 100644
index 0000000..816c2aa
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/images/page_noyau_meltdown.svg
@@ -0,0 +1,1193 @@
+
+
+
+
diff --git a/content/systemes_exploitation/4-Memoire/index.md b/content/systemes_exploitation/4-Memoire/index.md
new file mode 100644
index 0000000..5af6c69
--- /dev/null
+++ b/content/systemes_exploitation/4-Memoire/index.md
@@ -0,0 +1,470 @@
+---
+title: "Systèmes d'exploitation : Gestion de la mémoire"
+date: 2021-09-24
+tags: ["système", "mémoire", "pagination", "thread"]
+categories: ["Systèmes d'exploitation", "Cours"]
+---
+
+Au départ étaient les systèmes mono-tâche et mono-utilisateur. Les programmes
+accédaient à toute la mémoire disponible. Il n'y avait pas besoin de mécanisme
+spécifique de gestion. Mieux, **l'adresse en mémoire de notre programme était
+connu à l'avance** au moment de la compilation. Simple quand il y a un seul
+processus en mémoire.
+
+Le vénérable MS-DOS par exemple ne proposait aucune protection mémoire, **même
+la table d'interruptions** était accessible (et modifiable) par les programmes.
+Il était alors possible d'adresser 1Mo de mémoire dont 640Ko de RAM.
+
+L'accès aux routines du systèmes se faisait par les interruptions en passant
+l'adresse d'une sous-routine:
+
+ * int 08h — Timer interrupt
+ * int 10h — Video services
+ * Int 16h — Keyboard services
+ * int 21h — MS-DOS services
+
+```asm
+; SC_PutChar == 0x02 la sous routine de notre
+; interruption 21h (MS-DOS services)
+
+mov ah, 02h ; on passe la sous-routine
+mov dl, ‘A’
+int 21h
+mov ah, 4Ch ; SC_Exit == 0x4C
+mov al, 0 ; EXIT_SUCCESS
+int 21h
+```
+
+## Les systèmes multi-tâches
+
+Les opérations disques, à l'origine sur des bandes magnétiques, prenaient du
+temps. Afin de rentabiliser l'utilisation des processeurs qui passaient la
+plupart de leur temps à "glander", sont apparus les systèmes multi-tâches. Le
+principe est simple : permettre à **plusieurs processus de cohabiter en
+mémoire**. Plusieurs problèmes se posent alors :
+
+ * Comment compiler un programme alors que nous ne connaissons pas son
+ emplacement en mémoire?
+ * Comment éviter les problèmes de fragmentation de la mémoire?
+ * Comment gérer l'expansion des processus en mémoire?
+ * Comment gérer la protection de la mémoire.
+
+## La compilation d'un programme multi tâche
+
+Il est du coup impossible pour le compilateur de connaitre à l'avance les
+adresses mémoires utilisées par notre programme. Il faut donc trouver un
+mécanisme pour gérer ce problème.
+
+Le compilateur fournit alors une liste d'emplacement des pointeurs. Il génère
+contient alors **le code plus une table de correspondance** à l'étape de
+liaison. Ces informations seront positionnée dans la section ELF `.rel.text`
+(x86_32) ou `.rela.text` (x86_64) du binaire.
+
+Le problème est donc réglé non seulement au moment **de la compilation** mais
+aussi **de son chargement** par le système.
+
+## Fragmentation mémoire et expansion des processus
+
+Il arrive forcément un moment ou le système doit copier et / ou déplacer
+des elements en mémoire. Ce sont des opérations coûteuses.
+
+Lorsque un processus demande beaucoup de mémoire à l'aide de `malloc()`, le
+système doit prendre une décision : **mettre le processus ailleurs ou en bouger
+d'autres afin de faire de la place?**
+
+La fragmentation est un problème complexe, on l'abordera tout au long de ce
+chapitre.
+
+## Protection de la mémoire
+
+Afin de s'assurer qu'un processus *P1* ne puisse pas accéder à l'espace mémoire
+de *P2* nous devons mettre en place des protections. Doit-on le faire au moment
+de la compilation? Mais que faire des accès indirects?
+
+Demander au compilateur de **lancer des vérifications lors de chaque accès
+mémoire**? En plus d'être très coûteuse, cette solution est inefficace.
+
+Encore une fois la solution **vient des fabricants des microprocesseurs** pour
+nous permettre deux choses:
+
+ * **contrôle** des accès.
+ * **relocation efficace**.
+
+Seul deux registres sont nécessaires : *limit* `<` et *base* `+`. Ceux-ci sont
+renseignés à chaque changement de contexte. Le systèmes initialise l'adresse de
+base et la limite du processus en cours.
+
+Une interruption *SegFault* est renvoyée si le processus ne respecte pas ces
+deux valeurs.
+
+
+
+Le schema ci-dessus montre l'utilisation de ces deux registres afin de
+déterminer l'adresse mémoire physique à partir de l'adresse logique fournie par
+le processus. Le second accès est invalide: l'adresse demandée dépasse la
+limite, une **erreur de segmentation** est alors envoyée.
+
+La conversion d'une *adresse logique* en *adresse physique* ne demande aucun
+effort supplémentaire à l'unité centrale. L'isolation mémoire des processus est
+maintenant garantie par le matériel. Mais ce mécanisme empêche tout de même le
+partage de segments mémoire entre processus (et nos librairie partagées alors?).
+
+### Base et Limit par segment, gestion des droits
+
+Ceci étant dit, pourquoi garder les segments mémoire (code, data, tas, pile)
+ensemble? Nous pouvons les séparer, mais pout ça il va nous falloir plusieurs
+*base* et *limit*. Du coup, lors d'un accès à la mémoire nous **devons connaitre
+le segment concerné**.
+
+
+
+Le compilateur préfixe l'adresse mémoire par le segment.
+
+Ce mode de fonctionnement permet plus de souplesses dans l'allocation de la
+mémoire. Il permet aussi le partage d'espace de segments entre processus : il
+suffit qu'ils aient les **même *base* et *limit***. Il peuvent même partager le
+code, utile pour économiser de la RAM.
+
+### Droits sur les segments
+
+Il est du coup intéressant d'ajouter un peu plus de protection. Un registre
+supplémentaire, *mode* : il permet de spécifier les droits en lecture, écriture
+et execution.
+
+
+
+Ces droits sont positionnés par le CPU
+
+Mais tous ces mécanismes ne permettent pas de régler le problème de
+fragmentation. pour ça il faut trouver autre chose.
+
+## La pagination mémoire
+
+Son principe est simple: créer des espaces mémoire de taille fixe, **une page**
+(ou frame en anglais -- 4ko sur x86). Du point de vue du système, une page est
+soit libre, soit occupée. Lors de l'allocation, le système arrondi la taille
+demandée pour déterminer le nombres de pages à allouer.
+
+Il n'est pas garanti que des pages contigües soient allouées **de façon contigüe
+en mémoire**.
+
+### Adresses virtuelles
+
+Lorsque le processeur execute du code en espace utilisateur, il ne voit que les
+adresses memoires virtuelles. Il faut donc traduire ces adresses virtuelles en
+adresse physique (RAM).
+
+Prenons l'exemple d'une variable `i` où `&i=8320`. Pour des pages de *4Ko* notre
+variable se trouvera dans la troisième (2 x 4096) page avec un décalage de 128
+bits. On utilisera ce décalage pour trouver `i` comme dans le schéma
+ci-dessous:
+
+
+
+Les adresses mémoire sont stockée sur 32bits. La représentation binaire notre
+adresse mémoire virtuelle correspond au schema ci-dessous:
+
+
+
+ 1. Cette partie de l'adresse (20 bits) nous permet de connaitre la page
+ mémoire. Comme nous le verrons plus tard, cette partie servira pour la table
+ de pagination.
+ 2. Cette partie fait office de décalage, une fois la page physique trouvée on
+ se sert de ce dernier comme indique sur le schéma précédent.
+
+
+### Table de pagination
+
+Nous avons besoin de traduire les adresses virtuelles en adresses physiques,
+comment pouvons nous procéder? L'utilisation d'une **table de correspondance**
+est toute indiquée.
+
+Une table par processus est indiquée avec une capacité de 2^20 entrées. Chaque
+entrée de ces tables a une taille de 20bits, arrondi à 32 - 4 octets. Un des
+bits est utilisé pour indiquer si la page est allouée ou non (champ *valid*)
+comme le tableau ci-dessous:
+
+
+page viruelle| page physique | valide |
+:---: | :---:
+ 0 | 4 | 1
+ 1 | 18| 1
+ 2 | | 0
+ 3 | 2 | 1
+ 4 | | 0
+ 5 | | 0
+ 6 | 23 |1
+ ... | ...
+
+Mais nous verrons plus tard que d'autres bits seront utilisés...
+
+Problème cependant, **chaque table prends donc 4Mo de place**, c'est beaucoup!
+Le CPU a besoin de connaitre la table des pages en cours. Pour cela il faut un
+**registre spécial** mis à jour à chaque changement de contexte. La table a
+juste besoin d'être référencée par un pointeur.
+
+## MMU - Memory Management Unit
+
+La MMU est un élément du microprocesseur, son rôle est de transformer les
+adresses logiques en adresse physique. La MMU se compose d'un registre contenant
+la page des tables (aussi appelée table de translation) et d'un circuit
+permettant la conversion.
+
+
+
+Voici un premier schéma de fonctionnement de la MMU. Les 20 premiers bits de
+l'adresse virtuelles permettent de vérifier la table physique. lors de la
+translation, la MMU vérifie au passage **la validité de la page**.
+
+Mais il faut tout de même ajouter un peu de sécurité à tout ça comme vu lors du
+chapitre sur les registres *limit*, *base* et *mode*.
+
+
+
+Dans l'exemple ci-dessous, le processeur fait un accès mémoire en écriture. Lors
+de la traduction de l'adresse, la page est noté en lecture et exécution: **une
+exception** et lancée.
+
+La MMU est un circuit matériel, il n'empêche que son fonctionnement introduit
+des accès mémoires supplémentaire et donc **penalise les performances**. Sachant
+qu'un accès mémoire consomme une centaine de cycles! Nous avons donc deux
+problème majeurs:
+
+ 1. Les performances pénalisée
+ 2. L'empreinte mémoire des tables de pages
+
+### Réduire l'empreinte mémoire
+
+Dans les fait, une table des pages contient généralement une majorité de **pages
+invalides**. Il serait possible de la compresser, mais on perdrait l'indexation
+-- et donc les avantages d'un tableau. La solution: utiliser plusieurs niveau de
+tables organisées de façon hiérarchique. Les processeurs et systèmes modernes
+comportent en général 4 niveaux.
+
+Avec cette technique, lorsqu'il n'y a que des entrées valides dans une table de
+niveau n, on pose `NULL` dans l'entrée correspondante de la table n-1. Le noyau
+alloue des tables de niveau n (ou n > 1) seulement lorsqu'il en a besoin.
+
+
+
+On économise de la mémoire, mais au prix encore une fois de **perte de
+performances**. Il faut maintenant trois accès mémoire : un premier accès pour
+lire une entrée dans un répertoire de table de pages (page directory), un second
+pour lire une entrée dans une table de pages (page table), et un dernier pour
+accéder à la page physique.
+
+### TLB -- Translation Lookaside Buffer
+
+Pour éviter le problème, la MMU intègre un cache permettant de stocker les
+dernières opérations effectuées. Celui-ci mémorise *l'adresse de la page
+virtuelle*, *l'adresse de pa page physique* et *le mode*.
+
+Le TLB est un cache associatif rapide, de type LRU -- *Last Recently Used* --
+lorsqu'il est plein, la ligne la plus ancienne est évincée et remplacée. Il est
+en général limité à 32 / 64 entrées (c'est un type de cache coûtant cher en
+fabrication).
+
+Afin de permettre la cohabitation de plusieurs processus dans le cache, il
+enregistre une autre information : le *tag* -- un pointeur vers la page de table
+concerné par l'entrée.
+
+Les processeurs modernes comporte en général deux TLB de premier niveau : un
+pour les **instructions** (iTBL) et un autre pour **les données** (dTLB). Ils
+contiennent aussi plusieurs niveau : un TLB de premier niveau privée et rapide
+puis un de second niveau partagé et plus lent.
+
+## Côté du noyau
+
+Nous avons vu comment la memoire est utilisée en espace utilisateur, mais
+comment tout ça se passe en espace noyau? Particulièrement lors d'un appel
+système? Car le noyau doit avoir accès **aux deux espaces**
+
+Comme on pouvait se douter, le noyau utilise aussi la pagination. Il suffit de
+rajouter un bit afin de déterminer si la page appartient à l'utilisateur *(1)*
+ou au noyau *(0)*. Les entrées noyau sont positionnée en bas, comme sur
+l'exemple ci-dessous.
+
+
+
+Les entrées pagination du noyau sont partagées entre toutes les pages via un de
+simple pointeurs depuis la page de niveau 1, ainsi tous les processus **"voient"
+les pages mêmes pages du noyau**. Sur Linux 32bits, 3Go sont allouées aux
+processus et 1Go au noyau. En version 64bits toute la mémoire physique est
+alloué à l'espace d'adressage virtuel du noyau.
+
+## Meltdown - faille de sécurité matérielle
+
+Cette faille, largement médiatisée et documentée touche les processeurs Intel,
+IBM Power et certains ARM. Elle tire parti des conditions d'execution
+particulières de code dans les processeurs modernes : ils sont capable
+d'exécuter les instructions dans le désordres ([out of order
+execution](l_woutoforder)) et même faire de l'exécution spéculative
+
+L'exécution spéculative, correspondant au lancement anticipé d'instructions.
+Mais celles-ci ne doivent **pas être validée en cas d'erreur de prédiction**.
+Mais est-ce vraiment le cas?
+
+### Un petit programme de test.
+
+Pour bien comprendre comment fonctionne l'exécution, prenons un code d'exemple:
+
+```c
+char array [N * 4096];
+// mais que vaut data?
+int data = <...>;
+char c;
+*((int *)NULL) = 12;
+
+// Nous n'arriverons jamais jusqu'ici, nous
+// aurons droit a un segfault!
+c = data[data *4096];
+```
+
+La première instruction va forcément produire un *segfault*, `c` ne sera pas
+modifié. Mais a cause de l'execution spéculative, la seconde instruction a été
+exécutée avant le lancement de l'exception.
+
+Donc la zone mémoire `array[data * 4096]` a été lue, et son adresse est présente
+dans le cache. En mesurant les temps d'accès pour chacun des `array[ i * 4096]`,
+il nous est possible de deviner la caleur de `data`.
+
+### Lire une donnée du noyau
+
+Maintenant comment utiliser tout cela pour lire une donnée du noyau? voici un
+petit exemple en assembleur:
+
+```asm {linenos=table}
+;rxc: adresse noyau
+;rbx: adresse de base de notre tableau
+retry:
+ mov al, byte rxc ;al: partue de rax (8bits)
+ shl rax, 0xcc ;decallage de 12bits - 4096
+ jz retry ;reessayer l'execution spéculative
+ mov rbx, qword[rbx + rax]
+```
+
+La première instruction ligne 4 va générer une exeption, mais parrallèllement
+l'instruction ligne 7 sera exécutée puis annulée. Top tard, les adresses
+memoires seront dans le cache.
+
+La répetition ce des commandes pour les adresses noyau permet done de lire
+**toute la memoire physique** (démarre à `0xffff 8800 0000 0000` sous Linux sans
+*Kernel Address Space Layour Ransomization*). Les auteurs de la découverte ont
+réussi à lire la memoire à une vitesse de 503 Ko/s.
+
+### Et comment l'éviter?
+
+Il n'est bien entendu **pas question de désactiver l'exécution spéculative**,
+les fabrivcant de puces s'y refusent. Et pour cause, l'impact sur les
+performances est trop importante.
+
+Mais si on y réfléchit, Meltdown pose problème parceque **les pages du noyau
+sont projetées dans la table des processus** comme nous l'avons vu plus haut. La
+solution alors: *Kernel Page Table Isolation* ou KPTI. La table des pages du
+noyau n'est accessibles qu'en mode noyau.
+
+
+
+Comme on peu le voir sur le schema ci-dessus, les pages noyau sont invisible en
+mode utilisateur, ainsi il est impossible de lire des pages mémoire noyau en
+utilisant Meltdown. Conéquence directe : **une perte de performance entre 5 et
+25%** msesuré sur une architecture Inter Haswell / Skylake.
+
+## Optimiser la pagination
+
+Dans le but d'accélerer l'allocation mémoire qui console beaucoup de cycle CPU,
+les systèmes utilisent des optimisations. Dans cette partie, nous allons
+explorer deux pistes:
+
+ * l'allocation paresseuse / à la demande
+ * le copy on write
+
+### L'allocation à la demande
+
+Son principe est simple: **reporter à plus tard** l'allocation des pages qu'un
+processus y accèdera pour la première fois.
+
+Il est rare qu'un processus utilise les sements de pile qui luis sont allouées
+par défaut sous *Linux*. Il est aussi possible que certain tableaux alloués
+statiquements ne soient pas utilisés. Dans le même ordre d'idée il y a des
+fonctions d'un programme que l'on utilise pas.
+
+Bien entendu un minimum de pages sont allouées au départ afin de permettre le
+bon fonctionnement du processus. Mais alors **comment distinguer les erreur de
+segmentation d'une page non encore allouée?** Car une page non allouée est
+marquée **invalide** par la MMU.
+
+Le cheminement doit être le suivant:
+
+ 1. On accède à une page non encore allouée
+ 2. Une exception **page invalide**
+ 3. La MMU positione l'adresse mémoire dans un registre
+ 4. Le noyau vérifie si l'adresse virtuelle est présent dans une structure VMA
+ 5. Si c'est le cas lancement d'un `get_free_page()` et correction de la table
+ des page. Sinon on envoi un `SIGSERV` au processus.
+
+#### la structure Virtual Memory Area
+
+Elle est évoquée ci-dessus, cette struture permet au noyau de garder une trace
+des pages virtuelles qui lui sont allouées, elle prend la forme suivante:
+
+```c
+struct vm_area_struct {
+ unsigned lzong vm_start;
+ unsigned long vm_end;
+ pgprot_t vm_page_prot;
+ unsigned short vm_flags;
+struct file * vm_file;
+```
+#### conséquences
+
+Les larges allocations mémoires sont faites à la demande, une page à la fois,
+évitant de nombreux cycle CPU au démarrage du processus.
+
+### Copy on write
+
+Le fonctionnement historique de la création de processus sous Unix (encore en
+place aujourd'hui) se fait à base de `fork()` et `exec()`:
+
+ * `fork()` créer un espace d'adressage contenant une copie de son processus
+ père, un clone en fait.
+ * `exec()` charge un nouveau programme (dans le segment code).
+
+Le fonctionnent du `fork` et du `exec` a déjà été abordé lors du [chapitre des
+processus]({{< ref "../../progsys/5_les-processus_legers/index.md#création-de-processus">}}
+des cours le Lpro. en voici un exemple en C:
+
+```c
+int main (int argc, char *argv[])
+{
+ pid_t pid = fork ();
+ if (pid) { // Parent
+ wait (NULL);
+ } else { // Child
+ execl ("/bin/ls", "ls", "-l", NULL);
+ perror ("ls");
+ exit (EXIT_FAILURE);
+ }
+ return 0;
+}
+```
+
+La séquence formée de ces deux fonctions est coûteuse (surtout la copie des
+espaces d'adressage), y-a-t-il un moyen de faire mieux. Surtout qu'après le
+`fork()`, `exec()` va reinitialiser la plupart des pages, c'est donc en prime
+inefficace.
+
+La table des pages n'est que pointeurs, alors pourquoi ne pas la copier du
+processus père vers le fils? C'est là que le *Copy on Write* entre en jeu :
+
+ 1. La table des pages est dupliquée
+ 2. Les pages passent en lecture seule
+ 3. Lorque'un processus veux accéder à une page **en écriture**, le noyau lui
+ donne sa copie à lui.
+ 4. Le noyau corrige la table des pages.
+
+Pour s'assurer que l'accès est autorisé en écriture, le système utilise la
+structure *Virtual Memory Area*.
+
+[l_woutoforder]:https://fr.wikipedia.org/wiki/Ex%C3%A9cution_dans_le_d%C3%A9sordre