L'adressage mémoire et son allocation
Contents
Software version | Kernel 2.6.32+ |
---|---|
Operating System | Red Hat 6.3 Debian 7 |
Website | Kernel Website |
Last Update | 11/09/2012 |
Others |
1 L'adressage mémoire
Pour plus d'efficacité, la mémoire d'un ordinateur est découpé en blocks (chunks) appelé pages. La taille des pages peut varier en fonction de l'architecture du processeur (32 ou 64 bits). La RAM est découpée en page frames. 1 page frame peut contenir 1 page. Lorsqu'un process souhaites accéder à une adresse mémoire, une translation de la page vers la page frame doit être faite. Si ces informations ne sont pas déjà en mémoire, alors le kernel doit faire une recherche afin de charger manuellement cette page dans la page frame.
Lorsqu'un programme a besoin d'utiliser de la mémoire, il utilise ne fait une 'linear address'. Sur les systèmes 32 bits, on peut adresser que 4G de RAM. Il est possible de faire sauter cette limite avec une option kernel appelée PAE[1] permettant de monter jusqu'à 64G de RAM. Si vous souhaitez en mettre plus, il faut obligatoirement passer sur un système 64 bits qui vous permettra de monter jusqu'à 1T.
Chaque process a sa propre table de pages. Chaque PTE (Page Table Entry ou entrée de page) contient des informations sur la page frame qui est assignée à un process.
2 L'espace virtuel d'adressage
La mémoire d'un processus sous Linux est découpé en plusieurs secteurs :
- text : c'est le code du process qui est en cours d'exécution, aussi connu sous le nom de 'text area'
- data : les données utilisées par le programme. Les données initialisées seront en tête, suivit de celles qui ne le sont pas
- arguments : les arguments qui sont passés au programme
- environment : les variables d'environnements dont le programme bénéficie
- heap : utilisé pour l'allocation dynamique de mémoire (connu aussi sous le nom de brk)
- stack : utilisé pour le passage d'arguments entre les procédures et la mémoire dynamique
Certains process ne gèrent pas directement leur adressage mémoire laissant 2 solutions possibles :
- La heap et la stack peuvent grandir l'une dans l'autre
- Pus d'espace disponible, le process n'arrive pas à libérer de la mémoire qui a été allouée (Memory leaks)
La mémoire virtuelle (VMA) des process peut être vue de cette façon (ici le PID 1) :
cat |
> cat /proc/1/statm 5361 386 306 35 0 104 0 |
Voici une autre vue encore un peu plus détaillée (ici le PID 1) :
Vous pouvez également avoir une vue de la VMA avec pmap encore plus détaillée (ici le PID 1) :
Les working set de process correspondent à un groupe de pages d'un process en mémoire. Le working set d'un process change continuellement tout du long de la vie du programme du fait que l'attribution des espaces mémoire varie tout le temps. Pour l'attribution en mémoire des pages, le kernel s'assure en permanence des pages qui ne sont pas utilisées dans le working set. Si des pages contiennent des données modifiées, ses pages seront écrites sur le disque. S'il n'y a pas de données dans les pages, elles seront réallouées à d'autres process dont le besoin de mémoire est plus urgent.
Le vidage mémoire apparait lorsque le système passe plus de temps à déplacer des pages à l'intérieur et en dehors du working set d'un process, que de travailler avec tout simplement.
3 Ulimits
Il existe des limitations sur un système. j'ai déjà fait un article dessus[3]. Il est possible de limiter l'utilisation de la mémoire avec les ulimits. On peut par exemple définir la limite maximum d'espace d'adressage.
4 L'espace physique d'adressage
La plus part des architectures processeurs supportent différentes tailles de pages. Par exemple :
- IA-32 : 4Kib, 2MiB et 4MiB.
- IA-64 : 4KiB, 8KiB, 64KiB, 256KiB, 1MiB, 4MiB, 16MiB et 256MiB.
Le nombre d'entrées TLB est fixe mais peut être agrandit en modifiant la taille des pages.
5 Le mapping d’adresses virtuelles
Lorsque le kernel a besoin d'accéder à un espace mémoire particulier dans une page frame, il fait référence à une adresse virtuelle. Celle ci sera envoyée au MMU (Memory Managment Unit) sur le processeur faisant référence à une table de pages du processus. Cette adresse virtuelle va pointer à un PTE dans la table des pages. Le MMU utilise les informations transmises par le PTE pour localiser la page mémoire physique sur laquelle la virtuelle pointe. Chaque PTE contient un bit pour indiquer si la page est actullement en mémoire ou a été swappée sur le disque.
Une table de pages peut être assimilée à un dossier de pages. Le paging se fait en découpant les 32 bits d'adresses linéaires qui sont utilisées comme référence à des positions mémoires dans plusieurs endroits également connu sous le nom de 'page branching structure'. Les derniers 12 bits référencent l'offset mémoire dans laquelle se trouve la page mémoire. Les bits restant sont utilisés pour spécifier les tables de pages. Sur un système 32 bts, les 20 bits demanderont une large table de pages. L'adresse linéaire sera alors découpée en 4 segments :
- PGD : La Page Global Directory
- PMD : La Page Middle Directory
- La page table
- L'offset
La PGD et la PMD peuvent être vues comme des tables de pages qui pointent vers d'autres tables de pages.
Pour transformer des adresses linéaires en physiques, cela peut prendre un peu de temps, c'est pour cela que les processeurs sont dotés de petits cache aussi connu sous le nom de TLB (Translation lookaside buffer) qui stockent les adresses physiques récemment associées aux virtuelles. La taille d'un cache TLB est le produit du nombre de TLB et de la taille d'une page d'un processeur. Par exemple pour un processeur de 64 TLB et des pages de 4KiB, le cache TLB sera de 256KiB (64*4).
6 UMA
Sur un système 32 bits, le kernel map toute la mémoire jusqu'à 896MiB sur les 4GiB d'espace d'adressage linéaire. Cela permet au kernel d'avoir un accès directe en mémoire en dessous de 896MiB en regardant l'adressage linéaire présent dans les tables de pages kernel. Le kernel map directement toute la mémoire jusqu'à 869KiB sauf pour certaines régions réservées :
- 0KiB -> 4KiB : région réservée pour le BIOS (ZONE_DMA)
- 4KiB -> 640KiB : région mappées dans les tables de pages kernel (ZONE_DMA)
- 640KiB -> 1MiB : région réservée pour le BIOS et les périphériques de type IO (ZONE_DMA)
- 1MiB -> fin de la structure de données du kernel : pour le kernel et ses structures de données (ZONE_DMA)
- fin de la structure de données du kernel -> 869MiB : région mappées dans les tables de pages kernel (ZONE_NORMAL)
- 896MiB -> 1GiB : pour que le kernel puisse faire son mapping d'adressées linéaires réservées en ZONE_HIGHMEM (PAE)[1]
Sur un système 64 bits, c'est cependant beaucoup plus simple comme vous pouvez le voir !
7 L'allocation mémoire
Le COW (Copy on Write) est une forme de demande de pagination. Lorsqu'un process fork un enfant, le processus enfant hérite de l'adressage mémoire du parent. Au lieu de perdre du temps sur des cycles CPU à copier les espaces d'adressage du parent au fils, COW sait faire en sorte que le parent et l'enfant partagent le même espace d'adressage. COW est appelé de la sorte car à partir du moment où le fils ou l'enfant essaye d'écrire dans une page, le kernel va créer une copie de cette page et l'assigner à l’espace d'adressage du processus qui essaye d'écrire. Cette technologie permet de gagner beaucoup de temps.
Lorsqu'un processus fait référence à une adresse virtuelle, plusieurs choses se font avant l'approbation d'accès à l'espace mémoire demandé :
- Vérification que l'adresse mémoire est valide
- Chaque référence qu'un process fait pour une page en mémoire, ne donne pas nécessairement un access immédiat à une page frame. Cette vérification et également faite
- Chaque PTE d'un process dans une table de pages qui contient le drapeau de bit qui spécifie si la page est présente en mémoire ou non est vérifié
- L'accès aux pages non résidentes en mémoire génèrera des pages faults
- Ces pages faults peuvent être due aux erreurs de programmation (comme dans bien des cas) et représenteront des accès en mémoire qui n'ont pas encore été allouées par un process ou déjà swappée sur disque.
Il est important de savoir que :
- A partir du moment ou un process requiert une page qui n'est pas présente en mémoire, il recevra une erreur 'page fault'.
- Lorsque la mémoire virtuelle doit allouer une nouvelle page page frame pour un process, un 'minor page fault' arrivera (avec l'aide du MMU).
- Lorsque le kernel devra bloquer un process lorsqu'il est en cours de lecture sur disque, un 'major page fault' arrivera
Vous pouvez voir les pages fault actuels (ici le PID est 1) :
ps |
> ps -o minflt,majflt 1 MINFLT MAJFLT 1297 7 |
8 Les différentes RAM
Le cache mémoire est composé d'accès statiques aléatoires (SRAM). La SRAM est la mémoire la plus rapide de toutes les RAM. L'avantage de la SRAM (statique) par rapport à la dynamique (DRAM) et qu'elle a des cycles de temps plus court et elle n'a pas besoin d'être rafraichie après qu'elle vienne d'être lue. Cependant, la SRAM reste très cher.
Il existe également :
- La SDRAM (Synchronous Dynamic) utilise l'horloge du processeur pour synchroniser les signaux IO. En coordonnant les accès mémoires, le temps de réponse est réduit. En gros, une SDRAM de 100Mhz équivaut à 10ns de temps d'accès.
- La DDR (Double Data Rate) est une variante de la SDRAM et permet de lire dès qu'elle reçoit des signaux montant/descendant de l'horloge.
- La RDRAM (Rambus Dynamic) utilisent des bus étroits pour allez très vite et augmenter les débits.
- La SRAM que nous vennons de voir
Les RAM décrites ci dessus vont de la plus lente à la plus rapide.
9 NUMA
La technologie NUMA permet d'augmenter les performances du MMU. Il faut obligatoirement un processeur 64 bits pour pouvoir utiliser cette technologie. Vous pouvez vérifier que c'est présent au niveau de votre kernel :
grep |
> grep -i numa /boot/config-`uname -r` CONFIG_NUMA=y CONFIG_AMD_NUMA=y CONFIG_X86_64_ACPI_NUMA=y CONFIG_NUMA_EMU=y CONFIG_USE_PERCPU_NUMA_NODE_ID=y CONFIG_ACPI_NUMA=y |
Suivant les fondeurs, la technologie NUMA peut être différente. C'est par exemple le cas entre AMD et Intel. Intel a par exemple un HUB controleur (MCH) où tous les accès mémoires sont routés. Cela simplifie la gestion du cache snooping mais peut potentiellement provoquer des goulets d'étranglements pour les accès mémoires. La latence varie également en fonction de la fréquence et l'utilisation. Par contre AMD met des ports mémoires différents directement au niveau de ces CPUs, ce qui surpasse toute autre technologie SMP. Avec cette solution, pas de goulets d'étranglements mais cela complexifie la gestion du cache snooping qui doit être géré par tous les CPU.
Il est possible d'avoir plus d'information sur la gestion du NUMA sur un PID (ici 1) :
Si vous souhaitez avoir une utilisation plus fine de NUMA et décider des assignations sur les processeurs, il faut regarder cpuset[7]. C'est très utilisé pour les applications qui ont besoin de faible latence.
9.1 numactl
Vous pouvez également utiliser la commande numactl pour forcer certains cpu à utiliser une certaine mémoire afin de gagner des performances. Pour l'installer sous Red Hat :
yum |
yum install numactl |
Sous Debian :
aptitude |
aptitude install numactl |
Pour récupérer les informations d'une machine :
et :
numactl |
> numactl --show policy: default preferred node: current physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 cpubind: 0 1 2 3 nodebind: 0 1 2 3 membind: 0 1 2 3 |
9.1.1 Allouer un PID à certains processeurs
Vous pouvez binder un processur à un CPU :
numactl |
numactl --physcpubind=0,1,2,3 <PID> |
Pour allouer la mémoire sur le même noeud NUMA que le processeur :
numactl |
numactl --physcpubind=0 --localalloc <PID> |
9.2 Désactivation
Si vous ne souhaitez désactiver NUMA il faut activer le "Node Interleaving" au niveau du BIOS. Sinon au niveau de grub ajoutez (numa=off) :
10 Améliorer les performances du TLB
Le kernel alloue et vide sa mémoire en utilisant l'algorithme "Buddy System". Le but de cet algorithme est d'éviter les fragmentations externes de la mémoire. Ces fragmentations arrivent lorsqu'il y a de multiples allocations et désallocation de différentes tailles des pages frame. La mémoire devient alors fragmentée en petits blocks de pages libre entrecoupés par des blocks de mémoire alloués. Lorsque le kernel reçoit une demande l'allocation de blocks d'une page frame d'une taille N, il va d'abord regarder les blocks disponible qui peuvent contenir cette taille. S'il n'y en a pas de disponible, il essayera de trouver N/2 blocks disponible.
L'algorithme "Buddy System" essaye de réordonner de façon le plus contigu possible. Il est possible de voir la mémoire disponible comme ceci :
cat |
> cat /proc/buddyinfo Node 0, zone DMA 5 2 2 2 2 2 2 1 2 2 2 Node 0, zone DMA32 4326 15892 6613 1219 554 188 109 7 0 1 1 Node 0, zone Normal 2688 0 0 0 0 0 0 0 0 0 1 |
Le kernel supporte également les 'larges pages' grâce au mechanisme de 'hugepages' (aussi connu sous le nom de bigpages, largepages ou hugetlbfs). A chaque context switch rencontré, le kernel vide les entrées du TLB pour le process qui sort.
Pour déterminer la taille des hugepages :
grep |
> grep -i huge /proc/meminfo AnonHugePages: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB |
Pour choisir la taille des hugepages, il y a 2 solutions :
- Avec le sysctl :
/etc/sysctl.conf |
vm.nr_hugepages=<value> |
- Avec grub en ajoutant ce paramètre :
/etc/grub.conf |
[...] kernel /vmlinuz-2.6.32-279.2.1.el6.....hugepages=<value> [...] |
WARNING |
Faire une demande d'allocation au delà de ce que la machine peut fournir aboutira par un kernel panic |
Pour que les applications puissent utiliser ces espaces, elles doivent utiliser les appels système de type mmap, shmat et shmget. Dans le cas de mmap, les large pages doivent être disponible en utilisant le filesystem hugetlbfs :
mkdir /mnt/largepage mount -t hugetlbfs none /mnt/largepage |
11 References
- ^ a b http://fr.wikipedia.org/wiki/Extension_d%27adresse_physique
- ^ http://en.wikipedia.org/wiki/Virtual_address_space
- ^ Ulimit : Utiliser les limites systèmes
- ^ http://www.liafa.jussieu.fr/~carton/Enseignement/Architecture/Cours/Virtual/
- ^ a b http://frankdenneman.nl/2010/12/node-interleaving-enable-or-disable/
- ^ http://www.myexception.cn/linux-unix/515530.html
- ^ Latence des process et kernel timing#cpuset