From b95fe8380b873a9c06f39f90d170793a1320443b Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Fri, 7 Jan 2022 00:06:56 +0100 Subject: [PATCH] Add memory section --- .../4-Memoire/images/base_limit.svg | 767 ++++++++ .../4-Memoire/images/base_limit_right.svg | 1169 +++++++++++ .../4-Memoire/images/base_limit_segments.svg | 1011 ++++++++++ .../4-Memoire/images/bits_adresse_memoire.svg | 82 + .../images/memoire_paginee_correspondance.svg | 470 +++++ .../4-Memoire/images/mmu.svg | 1374 +++++++++++++ .../4-Memoire/images/mmu_right_exception.svg | 1195 ++++++++++++ .../4-Memoire/images/mmu_simple.svg | 1119 +++++++++++ .../4-Memoire/images/mmu_table_2.svg | 1712 +++++++++++++++++ .../4-Memoire/images/page_noyau.svg | 938 +++++++++ .../4-Memoire/images/page_noyau_meltdown.svg | 1193 ++++++++++++ .../systemes_exploitation/4-Memoire/index.md | 470 +++++ 12 files changed, 11500 insertions(+) create mode 100644 content/systemes_exploitation/4-Memoire/images/base_limit.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/base_limit_right.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/base_limit_segments.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/bits_adresse_memoire.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/memoire_paginee_correspondance.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/mmu.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/mmu_right_exception.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/mmu_simple.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/mmu_table_2.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/page_noyau.svg create mode 100644 content/systemes_exploitation/4-Memoire/images/page_noyau_meltdown.svg create mode 100644 content/systemes_exploitation/4-Memoire/index.md 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 @@ + + + + + Registres base + limit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0x0f2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registres base + limit + 2021.11.26 + + + Yorick Barbanneau + + + Utilisation des registres base et limit dans la gestion de la mémoire + + + + + + + + + + + + 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 @@ + + + + + Registres base + limit + mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registres base + limit + mode + + + + + + + + + + + + + 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 @@ + + + + + Registres base + limit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registres base + limit + 2021.11.26 + + + Yorick Barbanneau + + + Registres base et limit pour chaque segments mémoires + + + + + + + + + + + + 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 @@ + + + + + Memoire paginée, correspondance mémoire virtuelle et réelle + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Memoire paginée, correspondance mémoire virtuelle et réelle + + + Yorick Barbanneau + + + + + + 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 @@ + + + + + + Registres base + limit + mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registres base + limit + mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page - 20 bits + décalage - 12bits + &table + + + + + + + + + + + + + + + + + + + + + + + + + page - 20 bits + décalage - 12bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RAM + MMU + CPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + r + w + x + + + + + + + + + + + write: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &soustable + + + 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 @@ + + + + + MMU avec mode et exception + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MMU avec mode et exception + + + Yorick Barbanneau + + + + + + + + + + + + + + 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 @@ + + + + + + Registres base + limit + mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Registres base + limit + mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + MMU deux niveaux de table de pagination + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MMU deux niveaux de table de pagination + + + Yorick Barbanneau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mode utilisateur + mode noyau + + + + + + + + + + + + + + + + + + + + + Yorick Barbanneau / ephase + + + + + + CC BY-SA + + + Français + + + + + + + + + + + + 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. + +![Exemple d'utilisation des registres *base* et *limit*](images/base_limit.svg) + +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é**. + +![registres *base* et *limit* pour chaque segments](images/base_limit_segments.svg) + +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. + +![L'ajout du registre mode](images/base_limit_right.svg) + +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: + +![Correspondance page virtuelle / memoire](images/memoire_paginee_correspondance.svg) + +Les adresses mémoire sont stockée sur 32bits. La représentation binaire notre +adresse mémoire virtuelle correspond au schema ci-dessous: + +![Composition de l'adresse virtuelle](images/bits_adresse_memoire.svg) + + 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. + +![Fonctionnemenmt de la MMU: conversion d'adresse](./images/mmu_simple.svg) + +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*. + +![Fonctionnemenmt de la MMU: exception liée aux +droits](./images/mmu_right_exception.svg) + +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. + +![Fonctionnemenmt de la MMU: deux niveau de tables](images/mmu_table_2.svg) + +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. + +![Pages noyau et pages utilisateur](./images/page_noyau.svg) + +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. + +![Pages noyau indisponible en mode utilisateur](./images/page_noyau_meltdown.svg) + +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