3 points par GN⁺ 2025-11-05 | 1 commentaires | Partager sur WhatsApp
  • Explique la structure mémoire des processus sous Linux au niveau du fonctionnement réel, avec une présentation étape par étape de la relation entre espace d’adressage virtuel et mémoire physique
  • Décrit concrètement comment un processus possède et accède à la mémoire, en se concentrant sur des mécanismes clés comme les tables de pages, VMA, mmap, page fault, CoW
  • Présente comment observer l’état mémoire de chaque processus via le système de fichiers /proc, ainsi que le rôle d’outils de diagnostic avancés comme pagemap et kpageflags
  • Traite de l’optimisation des performances et des techniques de dirty tracking en espace utilisateur grâce à des fonctionnalités récentes du noyau comme Transparent Huge Pages (THP), userfaultfd et PAGEMAP_SCAN
  • Explique aussi des principes de conception du noyau liés à la sécurité et aux performances, comme PTI contre Meltdown, flush du TLB et politique W^X, afin d’offrir une compréhension d’ensemble de la gestion mémoire sous Linux

Structure de base de la mémoire d’un processus

  • Lorsqu’un programme s’exécute, tout se passe comme s’il disposait d’une immense mémoire continue, mais en réalité le noyau Linux l’organise dynamiquement page par page
    • Le CPU consulte les tables de pages pour traduire les adresses virtuelles en cadres physiques
    • S’il n’existe pas de mapping, un défaut de page (page fault) se produit, et le noyau alloue une nouvelle page ou renvoie une erreur
  • Si la RAM physique manque, le noyau déplace sur disque les pages inutilisées ou supprime des pages de fichiers pour libérer de l’espace
  • /proc est un système de fichiers virtuel construit par le noyau en mémoire, qui expose sous forme de fichiers l’état des processus et du noyau

Espace d’adressage et VMA

  • Chaque processus possède un objet d’espace d’adressage, dont l’intérieur est composé de plusieurs VMA (Virtual Memory Area)
    • Une VMA est une plage d’adresses continue avec les mêmes droits (R/W/X) et le même backend (mémoire anonyme ou fichier)
  • La table de pages est la structure référencée par le matériel, qui stocke les informations de mapping (PTE) entre pages virtuelles et pages physiques
  • Les modifications de l’espace d’adressage s’effectuent via trois appels système
    • mmap : crée une nouvelle zone
    • mprotect : modifie les droits
    • munmap : supprime le mapping
  • La page est l’unité de base de 4 KiB, et certains systèmes prennent aussi en charge de grandes pages de 2 MiB et 1 GiB

Voir la disposition mémoire avec /proc/self/maps

  • La commande cat /proc/self/maps permet d’inspecter la carte mémoire du processus
    • On y voit le code, les données et la bss de l’exécutable, le heap, les mappings anonymes, les bibliothèques partagées, la pile, etc.
  • Les zones [vdso] et [vvar] contiennent du code et des données mappés par le noyau pour des appels système rapides

Principe de fonctionnement de mmap

  • mmap n’alloue pas immédiatement de la mémoire réelle, il enregistre une promesse dans l’espace d’adressage
    • Les pages sont allouées au premier accès
  • Pour un mapping de fichier, offset doit être aligné sur la taille de page, et un accès au-delà de la fin du fichier provoque SIGBUS
  • MAP_SHARED répercute directement les modifications dans le fichier, tandis que MAP_PRIVATE crée des pages indépendantes par Copy-on-Write (CoW)
  • MAP_FIXED_NOREPLACE améliore la sûreté en échouant si l’adresse demandée est déjà mappée

Premier accès et défaut de page

  • Lors du premier accès à un nouveau mapping, si le CPU ne trouve pas l’entrée correspondante dans la table de pages, un page fault se produit
    • Le noyau vérifie la validité de l’adresse, les droits d’accès et l’existence de la zone
    • Pour un mapping anonyme, il alloue une nouvelle page remplie de zéros ; pour un mapping de fichier, il lit depuis le page cache
  • Un minor fault se produit lorsque les données sont déjà en RAM, un major fault lorsqu’une E/S disque est nécessaire
  • La pile est protégée par une guard page ; un accès trop en dessous provoque SIGSEGV

fork() et le Copy-on-Write de MAP_PRIVATE

  • Lors d’un fork, le parent et l’enfant partagent les mêmes pages physiques, marquées en lecture seule pour les deux
    • Une nouvelle page n’est copiée qu’au moment de l’écriture afin de conserver leur indépendance
  • Les mappings de fichiers MAP_PRIVATE fonctionnent selon le même principe
  • Options associées
    • vfork : partage l’espace d’adressage du parent
    • clone(CLONE_VM) : crée un thread
    • MADV_DONTFORK, MADV_WIPEONFORK : excluent le mapping du processus enfant ou le réinitialisent à zéro

Changement de droits et invalidation du TLB

  • Lorsqu’on modifie les droits d’une page avec mprotect, le noyau scinde éventuellement la VMA et modifie la table de pages, puis effectue une invalidation du TLB
  • Selon la politique W^X, une page ne peut pas être à la fois inscriptible et exécutable
  • Le TLB (Translation Lookaside Buffer) est un cache des traductions d’adresses récentes ; son invalidation introduit un léger délai

Observation détaillée via /proc

  • /proc/<pid>/maps, smaps et smaps_rollup permettent de voir, par zone, les droits, la RSS et l’usage des HugePages
  • /proc/<pid>/pagemap fournit l’état page par page (présence, swap, PFN, etc.), mais les PFN ne sont pas accessibles aux utilisateurs ordinaires
  • /proc/kpagecount et /proc/kpageflags affichent, pour chaque PFN, le nombre de mappings et les propriétés de page (anonyme, fichier, dirty, etc.)
  • mincore et SEEK_DATA/SEEK_HOLE permettent d’identifier les zones de données et les trous dans les fichiers creux
  • En combinant PAGEMAP_SCAN et userfaultfd, il est possible d’implémenter un dirty tracking en espace utilisateur

Transparent Huge Pages (THP) et mTHP

  • THP regroupe automatiquement la mémoire fréquemment accédée en grandes pages (2 MiB, par exemple) afin d’améliorer l’efficacité du TLB
    • Le thread khugepaged fusionne les pages adjacentes
  • mTHP prend en charge des grandes pages variables (folio) de tailles diverses, comme 16 KiB ou 64 KiB
  • On peut vérifier leur utilisation via AnonHugePages et FilePmdMapped dans /proc/self/smaps
  • La configuration globale du système se gère dans /sys/kernel/mm/transparent_hugepage/
  • MADV_HUGEPAGE et MADV_NOHUGEPAGE permettent un contrôle par zone

Dirty tracking en espace utilisateur

  • userfaultfd et PAGEMAP_SCAN permettent de ne copier que les pages modifiées
    • Le noyau effectue en une seule opération atomique le scan et la protection en écriture
    • Cette approche est efficace pour les snapshots, la live migration, etc.

Mécanisme de flush du TLB

  • Sur x86, l’invalidation du TLB se fait de deux façons
    • INVLPG : invalide une seule page
    • rechargement de la racine des tables de pages pour un flush complet
  • PCID et INVPCID réduisent les flush inutiles grâce à une gestion des tags TLB par processus
  • tlb_single_page_flush_ceiling est le seuil utilisé par le noyau pour choisir entre flush page par page et flush complet

Réponse à Meltdown : Page Table Isolation (PTI)

  • Meltdown est une vulnérabilité où des données du noyau peuvent être exposées via le cache pendant l’exécution spéculative
  • Linux utilise PTI (Page Table Isolation) pour séparer les espaces d’adressage utilisateur et noyau
    • À l’entrée, un basculement de CR3 active une table de pages dédiée au noyau
    • PCID est utilisé pour minimiser les flush du TLB
  • Cette protection est activée par défaut et peut être désactivée avec nopti

Procédure sûre du noyau pour modifier les mappings

  • Lors d’une modification de mapping, l’ordre est le suivant
    1. traitement des règles de cache
    2. modification de la table de pages
    3. invalidation du TLB
  • Les mappings internes au noyau (vmap, vmalloc) synchronisent eux aussi cache et TLB avant et après les E/S
  • Certaines architectures nécessitent un flush du cache d’instructions après copie de code

Structure de pile et d’appel sur x86

  • En mode 64 bits, les registres RIP, RSP et RBP sont utilisés, et la pile croît vers le bas
  • Selon l’ABI System V AMD64, les arguments sont passés dans RDI, RSI, RDX, RCX, R8, R9, et la valeur de retour dans RAX
  • Le mode utilisateur fonctionne en ring 3, le noyau en ring 0, et les appels système ainsi que les interruptions passent par des gates

Situations d’erreur et diagnostic

  • mmapEINVAL : erreur d’alignement de l’offset du fichier
  • mmapENOMEM : manque d’espace virtuel ou limitation d’overcommit
  • accès à un mapping de fichier → SIGBUS : accès au-delà de l’EOF
  • mprotect(PROT_EXEC)EACCES : montage noexec ou politique W^X
  • augmentation de la RSS après fork() : copie de pages due au CoW
  • écrasement d’un mapping existant avec MAP_FIXEDMAP_FIXED_NOREPLACE recommandé

Checklist pratique

  • Obtenir immédiatement de la mémoire : mmap + PROT_READ|PROT_WRITE + MAP_PRIVATE|MAP_ANONYMOUS
  • Génération de code : conserver W^X, puis mprotect(PROT_READ|PROT_EXEC)
  • Mapping de fichier : aligner offset sur la taille de page, ne pas accéder au-delà de l’EOF
  • En cas de nombreux page faults : MADV_WILLNEED ou pré-accès
  • Analyse de l’usage mémoire : /proc/<pid>/smaps_rollup/proc/<pid>/maps
  • fork de gros processus : prendre en compte le CoW, utiliser exec dans l’enfant
  • Environnement sensible à la latence : observer THP/mTHP, mlock et le comportement du TLB

1 commentaires

 
GN⁺ 2025-11-05
Commentaires sur Hacker News
  • J’aime vraiment ce genre de courts billets explicatifs
    Même quand je connais déjà le sujet, ça m’aide à le revalider une fois de plus pendant la lecture

  • Quand je vois une formule comme « mmap, without the fog », j’ai l’impression que c’est un texte coécrit par un LLM, et ça me met mal à l’aise et m’agace pour rien

    • Le ton du texte donne l’impression qu’on a demandé à Gemini une explication simple
      Avec en plus une expression bizarre comme « without the fog », ça donne vraiment l’impression que chatgpt a participé à la rédaction
  • En voyant parler d’instruction pipelining, ça me donne envie de revenir à l’époque des architectures simples comme le 6502
    À l’époque, ça fonctionnait « tel quel », sans mappings compliqués ni proxies
    Avec des interconnexions rapides, on pourrait presque se remettre à rêver de cette simplicité

    • Bien sûr, je reconnais que ces « astuces » (cheats) ont contribué aux gains de performance
      Mais quand on voit des problèmes comme Meltdown ou Spectre, on voit aussi clairement le prix payé pour cette complexité
      Maintenant que la loi de Moore atteint ses limites, on peut se demander si ce compromis de complexité est vraiment optimal
    • En réalité, ce que l’article explique, c’est le concept de mémoire virtuelle (virtual memory), une technologie qui précède le 6502 d’au moins 10 ans
    • C’est vrai que la complexité a augmenté, mais ce qu’on y a gagné est considérable
      Je ne pense pas que la simplicité soit forcément meilleure
    • Cela dit, je me demande pourquoi tu éprouves une telle nostalgie pour cette simplicité
  • Le site est indiqué comme bloqué parce qu’il appartiendrait à un domaine dangereux ou non sûr

    • Tu utilises peut-être un ordinateur portable professionnel ? Le service sécurité de ton entreprise ne fait peut-être pas confiance aux domaines en .xyz
    • C’est probablement le logiciel de sécurité qui a mal réagi
      D’après le résultat d’analyse VirusTotal, il n’y a pas de problème
    • Ça ressemble à un simple faux positif (false alarm)
    • Je me demande quel navigateur l’a bloqué
    • C’est drôle lol
  • Je me demande ce que veut dire le fait que le rapport d’erreur ne soit que du « bruit » (noise)