Maison  >  Article  >  Tutoriel système  >  Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

王林
王林avant
2024-02-13 15:15:13655parcourir

Avez-vous déjà rencontré divers problèmes de mémoire dans les systèmes Linux ? Tels que les fuites de mémoire, la fragmentation de la mémoire, etc. Ces problèmes peuvent être résolus grâce à une compréhension approfondie du modèle de mémoire Linux.

1. Avant-propos

Le noyau Linux prend en charge trois modèles de mémoire, à savoir le modèle de mémoire plate, le modèle de mémoire discontinue et le modèle de mémoire clairsemée. Le modèle de mémoire fait en réalité référence à la répartition de la mémoire physique du point de vue du processeur et à la méthode utilisée pour gérer ces mémoires physiques dans le noyau Linux. De plus, il convient de noter que cet article se concentre principalement sur les systèmes à mémoire partagée, ce qui signifie que tous les processeurs partagent un espace d'adressage physique.

Le contenu de cet article est organisé comme suit : Afin d'analyser clairement le modèle de mémoire, nous décrivons quelques termes de base, qui se trouvent au chapitre 2. Le troisième chapitre explique les principes de fonctionnement des trois modèles de mémoire. Le dernier chapitre est l'analyse du code. Le code provient du noyau 4.4.6. Pour les codes liés à l'architecture, nous utilisons ARM64 pour l'analyse.

2. Termes liés au modèle de mémoire

1. Qu'est-ce que le cadre de page ?

L'une des fonctions les plus importantes du système d'exploitation est de gérer diverses ressources du système informatique. En tant que ressource la plus importante : la mémoire, nous devons la gérer. Dans le système d'exploitation Linux, la mémoire physique est gérée en fonction de la taille de la page. La taille spécifique de la page est liée au matériel et à la configuration du système Linux 4k est le paramètre le plus classique. Par conséquent, pour la mémoire physique, nous la divisons en pages classées par taille de page. La zone mémoire de taille de page dans chaque mémoire physique est appelée cadre de page. Nous établissons une structure de données de page struct pour chaque cadre de page physique afin de suivre l'utilisation de chaque page physique : est-elle utilisée pour le segment de texte du noyau ? Ou s'agit-il d'un tableau de pages pour un processus ? Est-il utilisé pour divers caches de fichiers ou est-il dans un état libre...

Chaque cadre de page a une structure de données de page correspondante. Le système définit les macros page_to_pfn et pfn_to_page pour convertir entre le numéro de cadre de page et la structure de données de page. La méthode de conversion spécifique est liée au module de mémoire. , les trois modèles de mémoire du noyau Linux sont décrits en détail.

2. Qu'est-ce que le PFN ?

Pour un système informatique, l'intégralité de son espace d'adressage physique doit être un espace d'adressage commençant à 0 et se terminant par l'espace physique maximum que le système réel peut prendre en charge. Dans le système ARM, en supposant que l'adresse physique est de 32 bits, alors l'espace d'adressage physique est de 4G. Dans le système ARM64, si le nombre de bits d'adresse physique pris en charge est de 48, alors l'espace d'adressage physique est de 256T. Bien sûr, en fait, tout un espace d'adressage physique aussi grand n'est pas utilisé pour la mémoire, certains appartiennent également à l'espace d'E/S (bien sûr, certaines arches de CPU ont leur propre espace d'adressage IO indépendant). Par conséquent, l'espace d'adressage physique occupé par la mémoire doit être un intervalle limité et il est impossible de couvrir la totalité de l'espace d'adressage physique. Cependant, maintenant que la mémoire devient de plus en plus grande, pour les systèmes 32 bits, l'espace d'adressage physique 4G ne peut plus répondre aux besoins en mémoire, il existe donc le concept de mémoire élevée, qui sera décrit en détail plus tard.

PFN est l'abréviation du numéro de cadre de page. Le soi-disant cadre de page fait référence à la mémoire physique. La mémoire physique est divisée en zones de taille de page et chaque page est numérotée. En supposant que la mémoire physique commence à l'adresse 0, alors le cadre de page avec PFN égal à 0 est la page qui commence à l'adresse 0 (adresse physique). En supposant que la mémoire physique commence à l'adresse x, le numéro de la première image de page est (x>>PAGE_SHIFT).

3. Qu'est-ce que NUMA ?

Il existe deux options lors de la conception d'une architecture de mémoire pour les systèmes multiprocesseurs : L'une est l'UMA (accès uniforme à la mémoire). Tous les processeurs du système partagent un espace mémoire physique unifié et cohérent, quel que soit le processeur qui initie l'accès à l'adresse mémoire. est le même. NUMA (Non-Uniform Memory Access) est différent de UMA. L'accès à une certaine adresse mémoire est lié à la position relative entre la mémoire et le processeur. Par exemple, pour un processeur sur un nœud, l'accès à la mémoire locale prend plus de temps que l'accès à la mémoire distante.

3. Trois modèles de mémoire dans le noyau Linux

1. Qu'est-ce que le modèle de mémoire FLAT ?

Si du point de vue d'un processeur du système, lorsqu'il accède à la mémoire physique, l'espace d'adressage physique est un espace d'adressage continu sans trous, alors le modèle de mémoire de ce système informatique est une mémoire plate. Dans ce modèle de mémoire, la gestion de la mémoire physique est relativement simple. Chaque cadre de page physique aura une structure de données de page pour l'abstraire. Par conséquent, il existe un tableau de pages de structure (mem_map) dans le système, et chaque entrée du tableau pointe vers. une page physique réelle (cadre de page). Dans le cas d'une mémoire plate, la relation entre PFN (page frame number) et l'index du tableau mem_map est linéaire (il y a un décalage fixe, si l'adresse physique correspondant à la mémoire est égale à 0, alors PFN est l'index du tableau). Par conséquent, il est très simple de passer de PFN à la structure de données de page correspondante, et vice versa. Pour plus de détails, veuillez vous référer aux définitions de page_to_pfn et pfn_to_page. De plus, pour le modèle de mémoire plate, il n'y a qu'un seul nœud (struct pglist_data) (afin d'utiliser le même mécanisme que le modèle de mémoire discontinue). L'image ci-dessous décrit la situation de mémoire plate :

Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

Il convient de souligner que la mémoire occupée par struct page est dans la plage directement mappée, le système d'exploitation n'a donc pas besoin de créer une table de pages pour celle-ci.

2. Qu'est-ce que le modèle de mémoire discontinue ?

Si l'espace d'adressage du processeur présente des trous et est discontinu lors de l'accès à la mémoire physique, alors le modèle de mémoire de ce système informatique est une mémoire discontinue. De manière générale, le modèle de mémoire d'un système informatique basé sur NUMA est la mémoire discontinue. Cependant, les deux concepts sont en réalité différents. NUMA met l'accent sur la relation de position entre la mémoire et le processeur, qui n'a rien à voir avec le modèle de mémoire. Cependant, comme la mémoire et le processeur sur le même nœud ont une relation de couplage plus étroite (accès plus rapide), plusieurs nœuds sont nécessaires pour la gérer. La mémoire discontinue est essentiellement une extension du modèle de mémoire à mémoire plate. La majeure partie de l'espace d'adressage de la mémoire physique entière est une grande partie de la mémoire, avec quelques trous au milieu. Chaque partie de l'espace d'adressage de la mémoire appartient à un nœud (le cas échéant). est limité à un. En interne, le modèle de mémoire du nœud est une mémoire plate). L'image ci-dessous décrit la situation de la mémoire discontinue :

Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

Par conséquent, sous ce modèle de mémoire, il existe plusieurs données de nœuds (struct pglist_data), et la définition de macro NODE_DATA peut obtenir la structure pglist_data du nœud spécifié. Cependant, la mémoire physique gérée par chaque nœud est stockée dans le membre node_mem_map de la structure de données struct pglist_data (le concept est similaire à mem_map en mémoire plate). À ce stade, la conversion de PFN en une page de structure spécifique sera un peu plus compliquée. Nous devons d'abord obtenir l'ID de nœud de PFN, puis trouver la structure de données pglist_data correspondante basée sur cet ID, puis trouver le tableau de pages correspondant. La méthode suivante est une mémoire plate similaire.

3. Qu'est-ce que le modèle de mémoire clairsemée ?

Le modèle de mémoire est également un processus évolutif. Au début, la mémoire plate était utilisée pour abstraire un espace d'adressage mémoire continu (mem_maps[]). Après l'émergence de NUMA, l'ensemble de l'espace mémoire discontinu était divisé en plusieurs nœuds, et chaque nœud. Il s'agit d'un espace d'adressage mémoire continu, c'est-à-dire que l'unique mem_maps[] d'origine est devenu plusieurs mem_maps[]. Tout semble parfait, mais l'émergence du hotplug de mémoire rend la conception originale parfaite imparfaite, car même mem_maps[] dans un nœud peut être discontinu. En fait, après l'émergence de la mémoire fragmentée, le modèle de mémoire discontinue n'est plus aussi important. Il va de soi que la mémoire fragmentée peut éventuellement remplacer la mémoire discontinue. Ce processus de remplacement est encore en cours. Le noyau 4.4 dispose encore de trois modèles de mémoire. choisissez parmi.

Pourquoi dit-on que la mémoire clairsemée peut éventuellement remplacer la mémoire discontinue ? En fait, dans le modèle de mémoire clairsemée, l'espace d'adressage continu est divisé en sections selon SECTION (par exemple, 1G), et chaque section est connectée à chaud. Par conséquent, sous la mémoire clairsemée, l'espace d'adressage mémoire peut être divisé en plusieurs. sections détaillées. , prend en charge une mémoire discontinue plus discrète. De plus, avant l'apparition de la mémoire clairsemée, NUMA et mémoire discontinue étaient toujours dans une relation confuse : NUMA ne stipulait pas la continuité de sa mémoire, et le système de mémoire discontinue n'était pas nécessairement un système NUMA, mais ces deux configurations sont toutes multi- nœud. Avec la mémoire clairsemée, nous pouvons enfin séparer le concept de continuité de mémoire de NUMA : un système NUMA peut être une mémoire plate ou une mémoire clairsemée, et un système de mémoire clairsemée peut être NUMA ou UMA.

L'image ci-dessous illustre comment la mémoire clairsemée gère les cadres de page (SPARSEMEM_EXTREME est configuré) :

Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

(Remarque : un pointeur mem_section dans l'image ci-dessus doit pointer vers une page, et il y a plusieurs unités de données struct mem_section dans une page)

L'ensemble de l'espace d'adressage physique continu est découpé section par section. À l'intérieur de chaque section, sa mémoire est continue (c'est-à-dire qu'elle est conforme aux caractéristiques de la mémoire plate. Par conséquent, le tableau de pages de mem_map est attaché à la structure de section (). struct mem_section) Au lieu de la structure des nœuds (struct pglist_data). Bien entendu, quel que soit le modèle de mémoire utilisé, la correspondance entre PFN et page doit être traitée. Cependant, la mémoire clairsemée a une notion supplémentaire de section, effectuant la conversion en PFNSectionpage.

Voyons d'abord comment convertir un PFN en structure de page : un tableau de pointeurs mem_section est défini statiquement dans le noyau. Une section comprend souvent plusieurs pages, il est donc nécessaire de convertir le PFN en numéro de section par décalage vers la droite, en utilisant le numéro de section. Pour l'index, la structure de données de section correspondant au PFN peut être trouvée dans le tableau de pointeurs mem_section. Après avoir trouvé la section, vous pouvez trouver la structure de données de la page correspondante le long de sa section_mem_map. À propos, au début, la mémoire clairsemée utilisait un tableau memory_section unidimensionnel (pas un tableau de pointeurs). Cette implémentation gaspille beaucoup de mémoire pour les systèmes particulièrement clairsemés (CONFIG_SPARSEMEM_EXTREME). De plus, il est plus pratique de sauvegarder le pointeur pour la prise en charge du hotplug. Si le pointeur est égal à NULL, cela signifie que la section n'existe pas. L'image ci-dessus décrit la situation d'un tableau de pointeurs mem_section unidimensionnel (SPARSEMEM_EXTREME est configuré). Pour une configuration non-SPARSEMEM_EXTREME, le concept est similaire. Vous pouvez lire le code pour l'opération spécifique.

C'est un peu gênant de passer d'une page à un PFN. En fait, PFN est divisé en deux parties : une partie est l'index de la section, et l'autre partie est le décalage de la page dans la section. Nous devons d'abord obtenir l'index de section de la page, puis obtenir la section_mémoire correspondante. Connaître la section_mem_map signifie que la page est dans la section_mem_map, et nous connaissons également le décalage de la page dans la section. Enfin, nous pouvons synthétiser le PFN. . Pour la conversion d'une page en index de section, la mémoire clairsemée propose deux solutions. Regardons d'abord la solution classique, qui est enregistrée dans page->flags (SECTION_IN_PAGE_FLAGS est configuré). Le plus gros problème avec cette méthode est que le nombre de bits dans page->flags n'est pas nécessairement suffisant, car cet indicateur contient trop d'informations. Divers indicateurs de page, identifiants de nœuds et identifiants de zone ajoutent désormais un identifiant de section que les algorithmes ne peuvent pas atteindre. cohérence dans différentes architectures. Existe-t-il un algorithme universel ? Il s'agit de CONFIG_SPARSEMEM_VMEMMAP. Pour l'algorithme spécifique, veuillez vous référer à la figure ci-dessous :

Modèle de mémoire Linux : une compréhension plus approfondie de la gestion de la mémoire

(Il y a un problème avec l'image ci-dessus. vmemmap ne pointe vers le premier tableau de page de structure que lorsque PHYS_OFFSET est égal à 0. De manière générale, il devrait y avoir un décalage, mais je suis trop paresseux pour le changer, haha)

Pour le modèle de mémoire clairsemée classique, la mémoire occupée par le tableau de pages struct d'une section provient de la zone directement mappée. La table des pages est établie lors de l'initialisation, et le cadre de page est alloué, ce qui signifie que l'adresse virtuelle est allouée. Cependant, pour SPARSEMEM_VMEMMAP, l'adresse virtuelle est allouée depuis le début. Il s'agit d'un espace d'adressage virtuel continu à partir de vmemmap. Chaque page a une page de structure correspondante. Bien entendu, il n'y a qu'une adresse virtuelle et aucune adresse physique. Par conséquent, lorsqu'une section est découverte, l'adresse virtuelle de la page de structure correspondante peut être trouvée immédiatement. Bien entendu, il est également nécessaire d'attribuer un cadre de page physique, puis d'établir une table de pages. Par conséquent, pour ce type de mémoire clairsemée, la surcharge sera légèrement plus importante (un processus supplémentaire d'établissement du mappage).

4. Analyse du code

Notre analyse de code est principalement effectuée via include/asm-generic/memory_model.h.

1. mémoire plate. Le code est le suivant :

«

\#define __pfn_to_page(pfn)  (mem_map + ((pfn) - ARCH_PFN_OFFSET)) 
\#define __page_to_pfn(page)  ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)

Il ressort du code que l'index PFN et le tableau de pages struct (mem_map) sont liés de manière linéaire, et il existe un décalage fixe appelé ARCH_PFN_OFFSET. Ce décalage est lié à l'architecture estimée. Pour ARM64, elle est définie dans le fichier arch/arm/include/asm/memory.h. Bien entendu, cette définition est liée à l'espace d'adressage physique occupé par la mémoire (c'est-à-dire liée à la définition de PHYS_OFFSET).

2. Modèle de mémoire discontinue. Le code est le suivant :

\#define __pfn_to_page(pfn)      \ 
({  unsigned long __pfn = (pfn);    \ 
unsigned long __nid = arch_pfn_to_nid(__pfn); \ 
NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\ 
})

\#define __page_to_pfn(pg)            \ 
({  const struct page *__pg = (pg);          \ 
struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg));  \ 
(unsigned long)(__pg - __pgdat->node_mem_map) +      \ 
__pgdat->node_start_pfn;          \ 
})

Discontiguous Memory Model需要获取node id,只要找到node id,一切都好办了,比对flat memory model进行就OK了。因此对于__pfn_to_page的定义,可以首先通过arch_pfn_to_nid将PFN转换成node id,通过NODE_DATA宏定义可以找到该node对应的pglist_data数据结构,该数据结构的node_start_pfn记录了该node的第一个page frame number,因此,也就可以得到其对应struct page在node_mem_map的偏移。__page_to_pfn类似,大家可以自己分析。

3、Sparse Memory Model。经典算法的代码我们就不看了,一起看看配置了SPARSEMEM_VMEMMAP的代码,如下:

\#define __pfn_to_page(pfn)  (vmemmap + (pfn)) 
\#define __page_to_pfn(page)  (unsigned long)((page) - vmemmap)

简单而清晰,PFN就是vmemmap这个struct page数组的index啊。对于ARM64而言,vmemmap定义如下:

\#define vmemmap      ((struct page *)VMEMMAP_START - \ 
      SECTION_ALIGN_DOWN(memstart_addr >> PAGE_SHIFT))

毫无疑问,我们需要在虚拟地址空间中分配一段地址来安放struct page数组(该数组包含了所有物理内存跨度空间page),也就是VMEMMAP_START的定义。

总之,Linux内存模型是一个非常重要的概念,可以帮助你更好地理解Linux系统中的内存管理。如果你想了解更多关于这个概念的信息,可以查看本文提供的参考资料。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer