Terminologie essentielle
- Page physique (Page Frame) : unité élémentaire de gestion de la mémoire physique
- Page virtuelle : unité d'adressage dans l'espace virtuel d'un processus
- Numéro de page physique (PFN) : identifiant attribué par le système à chaque cadre de page selon son ordre d'adressage physique
- MMU (Memory Management Unit) : composant matériel intégré au processeur chargé de la traduction d'adresses virtuelles en adresses physiques
- TLB (Translation Lookaside Buffer) : mémoire cache dédiée au stockage des correspondances récentes entre pages virtuelles et physiques
Vue d'ensemble de la mémoire physique
La mémoire, au sens strict, désigne la RAM. Toutefois, du point de vue du processeur, toute unité de stockage connectée au bus système est considérée comme accessible. La gestion mémoire de Linux concerne donc cette mémoire au sens large.
Le système d'exploition manipule deux espaces distincts :
- L'espace physique, perceptible depuis le CPU
- L'espace virtuel, tel qu'observé par les applications
L'espace physique englobe non seulement la RAM, mais également les zones d'entrées/sorties via le mécanisme MMIO (Memory-Mapped I/O). La cartographie complète est consultable via /proc/iomem :
$ sudo cat /proc/iomem
00000000-00000fff : Reserved
00001000-0002ffff : System RAM
00030000-0004ffff : Reserved
00050000-0009efff : System RAM
...
100000000-17fffffff : System RAM
117800000-118dfffff : Kernel code
118e00000-119bfefff : Kernel rodata
119c00000-11a056aff : Kernel data
11a561000-11a9fffff : Kernel bss
Cet espace présente généralement des discontinuités (holes) entre ses différentes régions.
Correspondance RAM vers espace d'adressage
La projection de la RAM vers l'espace mémoire n'est pas contiguë. Une barrette de 4 Go n'occupe pas nécessairement les adresses 0 à 4 Go. Sur architecture x86, cette cartographie est établie par le BIOS. La gestion mémoire repose sur la maintenance de trois entités : les supports physiques (RAM et MMIO), l'espace d'adressage physique et l'espace d'adressage virtuel.
Translation d'adresses et MMU
L'unité de gestion mémoire réalise la conversion entre adresses virtuelles et physiques. Son fonctionnement repose sur une chaîne de traitement spécifique, simulable également par voie logicielle pour les architectures dépourvues de ce composant matériel.
Gestion du cadre physique
Le processeur administre la mémoire physique par unités de pages. Chaque page est représentée par une instance de struct page dans le noyau.
Structure des métadonnées de page
La définition de cette structure se trouve dans les en-têtes du noyau :
struct page {
unsigned long drapeaux;
union {
struct {
union {
struct list_head liste_lru;
struct {
void *remplissage;
unsigned int compte_mlock;
};
struct list_head liste_buddy;
struct list_head liste_pcp;
};
struct address_space *espace_adressage;
union {
pgoff_t decalage;
unsigned long partages;
};
unsigned long prive;
};
struct {
unsigned long magie_pp;
struct page_pool *pool;
unsigned long _remplissage_mapping;
unsigned long adresse_dma;
union {
unsigned long adresse_dma_haut;
atomic_long_t compte_frag;
};
};
struct {
unsigned long tete_composee;
};
struct {
struct dev_pagemap *carte_pg;
void *donnees_peripherique;
};
struct rcu_head tete_rcu;
};
union {
atomic_t _compte_cartes;
unsigned int type_page;
};
atomic_t _compte_references;
#ifdef CONFIG_MEMCG
unsigned long donnees_memcg;
#endif
#if defined(WANT_PAGE_VIRTUAL)
void *adresse_virtuelle;
#endif
#ifdef CONFIG_KMSAN
struct page *ombre_kmsan;
struct page *origine_kmsan;
#endif
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _dernier_cpupid;
#endif
} _alignement_struct_page;
Le champ drapeaux combine des indicateurs d'état : les bits de poids faible définissent les propriétés de la page (voir include/linux/page-flags.h), tandis que les bits supérieurs conservent les identifiants de zone, de nœud NUMA et de section SPARSEMEM.
Allocation par système de buddies
Ce mécanisme constitue l'approche dominante pour l'attribution dynamique de blocs mémoire. Lors d'une demande d'allocation, le système identifie un bloc de taille appropriée. À la libération, les blocs contigus de même taille sont fusionnés pour former un bloc de niveau supérieur.
Mécanismes d'allocation par objets
Slab : Cette approche mise en place des réserves d'objets préinitialisés, divisées entre tampons locaux par processeur et tampon partagé global. Les tampons locaux éliminent la contention inter-cœurs en permettant à chaque CPU d'opérer sur sa propre réserve sans synchronisation.
Slob : Version allégée du slab, optimisée pour les systèmes à ressources limitées.
Slub : Évolution moderne du slab, simplifiant la structure de données et améliorant les performances sur les architectures contemporaines.
Mémoire virtuelle et ses structures
Chaque processus dispose d'une représentation de son espace d'adrsesage via mm_struct :
struct mm_struct {
struct {
atomic_t compte_mm;
} ____cacheline_aligned_in_smp;
struct maple_tree arbre_mm;
#ifdef CONFIG_MMU
unsigned long (*obtenir_zone_libre) (struct fichier *f,
unsigned long addr, unsigned long taille,
unsigned long decalage, unsigned long drapeaux);
#endif
unsigned long base_mmap;
unsigned long base_mmap_legacy;
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
unsigned long base_mmap_compat;
unsigned long base_mmap_compat_legacy;
#endif
unsigned long taille_tache;
pgd_t * pgd;
#ifdef CONFIG_MEMBARRIER
atomic_t etat_membarriere;
#endif
atomic_t utilisateurs_mm;
#ifdef CONFIG_SCHED_MM_CID
struct mm_cid __percpu *cid_pcpu;
unsigned long prochain_scan_cid;
#endif
#ifdef CONFIG_MMU
atomic_long_t octets_tables_pages;
#endif
int nombre_cartes;
spinlock_t verrou_tables_pages;
struct rw_semaphore verrou_mmap;
struct list_head liste_mm;
#ifdef CONFIG_PER_VMA_LOCK
int sequence_verrou_mm;
#endif
unsigned long pic_rss;
unsigned long pic_vm;
unsigned long total_vm;
unsigned long vm_verrouillees;
atomic64_t vm_epinglees;
unsigned long vm_donnees;
unsigned long vm_exec;
unsigned long vm_pile;
unsigned long drapeaux_defaut;
seqcount_t sequence_protection_ecriture;
spinlock_t verrou_arguments;
unsigned long debut_code, fin_code, debut_donnees, fin_donnees;
unsigned long debut_brk, brk, debut_pile;
unsigned long debut_args, fin_args, debut_env, fin_env;
unsigned long auxv_sauve[AT_VECTOR_SIZE];
struct percpu_counter statistiques_rss[NR_MM_COUNTERS];
struct linux_binfmt *format_binaire;
mm_context_t contexte;
unsigned long drapeaux;
#ifdef CONFIG_AIO
spinlock_t verrou_ioctx;
struct kioctx_table __rcu *table_ioctx;
#endif
#ifdef CONFIG_MEMCG
struct task_struct __rcu *proprietaire;
#endif
struct user_namespace *espace_utilisateur;
struct fichier __rcu *fichier_exe;
#ifdef CONFIG_MMU_NOTIFIER
struct mmu_notifier_subscriptions *abonnements_notificateur;
#endif
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
pgtable_t pte_geante_pmd;
#endif
#ifdef CONFIG_NUMA_BALANCING
unsigned long prochain_scan_numa;
unsigned long decalage_scan_numa;
int sequence_scan_numa;
#endif
atomic_t invalidations_tlb_en_attente;
#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
atomic_t invalidations_tlb_batch;
#endif
struct uprobes_state etat_uprobes;
#ifdef CONFIG_PREEMPT_RT
struct rcu_head liberation_differee;
#endif
#ifdef CONFIG_HUGETLB_PAGE
atomic_long_t utilisation_hugetlb;
#endif
struct work_struct travail_liberation_async;
#ifdef CONFIG_IOMMU_SVA
u32 pasid;
#endif
#ifdef CONFIG_KSM
unsigned long pages_fusion_ksm;
unsigned long elements_rmap_ksm;
atomic_long_t pages_zero_ksm;
#endif
#ifdef CONFIG_LRU_GEN
struct {
struct list_head liste;
unsigned long bitmap;
#ifdef CONFIG_MEMCG
struct mem_cgroup *memcg;
#endif
} generation_lru;
#endif
} __randomize_layout;
unsigned long bitmap_cpu[];
};
Depuis la version 6.1, l'arbre_mm de type maple_tree remplace les listes chaînées et arbres rouge-noir pour l'organisation des régions mémoires virtuelles, offrant des performances supérieures pour les opérations de recherche et d'insertion.
Description des régions virtuelles
Les zones contiguës au sein de l'espace virtuel sont représentées par vm_area_struct :
struct vm_area_struct {
union {
struct {
unsigned long debut_vm;
unsigned long fin_vm;
};
#ifdef CONFIG_PER_VMA_LOCK
struct rcu_head rcu_vma;
#endif
};
struct mm_struct *mm;
pgprot_t protection_pages;
union {
const vm_flags_t drapeaux_vm;
vm_flags_t __private __drapeaux_vm;
};
#ifdef CONFIG_PER_VMA_LOCK
int sequence_verrou_vm;
struct vma_lock *verrou_vm;
bool detachee;
#endif
struct {
struct rb_node noeud_rb;
unsigned long dernier_sous_arbre_rb;
} partage;
struct list_head chaine_vma_anon;
struct anon_vma *vma_anon;
const struct vm_operations_struct *operations;
unsigned long decalage_pg;
struct fichier * fichier_vm;
void * donnees_privees_vm;
#ifdef CONFIG_ANON_VMA_NAME
struct anon_vma_name *nom_anon;
#endif
#ifdef CONFIG_SWAP
atomic_long_t info_prefetch_swap;
#endif
#ifndef CONFIG_MMU
struct vm_region *region_vm;
#endif
#ifdef CONFIG_NUMA
struct mempolicy *politique_vm;
#endif
#ifdef CONFIG_NUMA_BALANCING
struct vma_numab_state *etat_numab;
#endif
struct vm_userfaultfd_ctx contexte_userfaultfd;
} __randomize_layout;
Gestion des défauts de page
Les défauts de page constituent le mécanisme central d'établissement des correspondances entre mémoire virtuelle et physique. Cette procédure, fortement dépendante de l'architecture, doit traiter divers cas : pages anonymes, pages KSM, cache de pages, copie sur écriture, ainsi que les mappings privés et partagés.
Fonction centrale de traitement
La fonction do_page_fault (ou son équivalent architecturel) constitue le point d'entrée pour la résolution des défauts de page.
Catégories de défauts
Pages anonymes : se produit lors du premier accès à une zone mémoire allouée via malloc ou mmap sans fichier sous-jacent.
Mapping fichier : déclenché lors de l'accès initial à une région mappée sur un fichier, nécessitant le chargement depuis le stockage.
Copie sur écriture (COW) : activé lors d'une tentative d'écriture sur une page partagée entre plusieurs prcoessus, entraînant la duplication de la page pour l'initiateur de l'écriture.
Stratégies de récupération de pages
Algorithmes de remplacement
LRU (Least Recently Used) : les pages les moins récemment accédées sont candidates à l'éviction. Cette approche repose sur l'hypothèse de localité temporelle.
Algorithme de la seconde chance : variante du LRU où chaque page dispose d'un bit de référence. Lors du parcours, les pages référencées récemment se voient accorder un sursis, évitant l'élimination trop rapide de pages fréquemment utilisées.
Mécanisme OOM Killer
En situation d'épuisement critique des ressources, le sous-système OOM (Out-Of-Memory) identifie et termine le processus le plus approprié selon un score calculé à partir de critères tels que la consommation mémoire, la priorité et le temps d'exécution.
Outils de diagnostic
top
Cette commande affiche en temps réel l'activité des processus avec leurs métriques mémoire :
| Colonne | Signification |
|---|---|
| PID | Identifiant du processus |
| VIRT | Mémoire virtuelle totale (Ko) |
| RES | Mémoire résidente physique (Ko) |
| SHR | Mémoire partagée (Ko) |
| %MEM | Pourcentage de mémoire physique utilisée |
vmstat
Utilitaire de surveillance globale du sous-système mémoire :
$ vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 725336 94036 1956992 0 0 366 494 261 451 3 1 95 0 0
Les colonnes mémoire révèlent : swpd (mémoire swap utilisée), free (quantité libre), buff (tampons système), cache (cache de pages). Les colonnes si/so indiquent l'activité d'échange avec le disque, tandis que bi/bo mesurent les transferts sur les périphériques de blocs.