Maison >Opération et maintenance >exploitation et maintenance Linux >Dans quel espace s'exécute le pilote Linux ?
Les pilotes Linux s'exécutent dans l'espace "noyau". Généralement, les pilotes appellent kmalloc() pour allouer de la mémoire aux structures de données, appellent vmalloc() pour allouer des structures de données aux zones d'échange actives, allouent des tampons pour certains pilotes d'E/S ou allouent de l'espace pour les modules ;
L'environnement d'exploitation de ce tutoriel : système linux7.3, ordinateur Dell G3.
Les pilotes Linux s'exécutent dans l'espace "noyau".
Pour les programmes de microcontrôleurs généralement écrits, les applications et les pilotes sont souvent mélangés. Les programmeurs de microcontrôleurs ayant un certain niveau de capacité peuvent réaliser la superposition d'applications et de pilotes. Dans les systèmes Linux, les applications et les pilotes ont été forcés d'être superposés.
Dans le programme du microcontrôleur, l'application peut exploiter directement les registres sous-jacents. Cependant, un tel comportement est interdit dans les systèmes Linux. Par exemple : l'auteur d'une application Linux appelle délibérément le pilote de gestion de l'alimentation dans l'application et arrête le système. Cela n'en vaut-il pas la peine ?
L'application Linux spécifique appelle le pilote comme indiqué sur la figure :
L'application s'exécute dans l'espace utilisateur et le pilote s'exécute dans l'espace noyau. Si une application dans l'espace utilisateur souhaite faire fonctionner le noyau, elle doit utiliser une méthode « d'appel système » pour entrer dans l'espace noyau depuis l'espace utilisateur et faire fonctionner la couche sous-jacente.
Le noyau est également un programme et doit également avoir son propre espace mémoire virtuelle. Cependant, en tant que programme au service des programmes utilisateur, l'espace noyau a ses propres caractéristiques.
La relation entre l'espace noyau et l'espace utilisateur
Dans un système 32 bits, l'espace virtuel maximum d'un programme peut être de 4 Go, alors la manière la plus directe est de considérer le noyau comme un programme afin qu'il Comme d’autres programmes, il dispose également de 4 Go d’espace. Cependant, cette approche amènera le système à basculer en permanence la table des pages du programme utilisateur et la table des pages du noyau, affectant ainsi l'efficacité de l'ordinateur. La meilleure façon de résoudre ce problème est de diviser l'espace de 4 Go en deux parties : une partie est l'espace utilisateur et l'autre partie est l'espace noyau. Cela peut garantir que l'espace noyau est fixe et inchangé, et lorsque le programme change, seul le. changements de programme. Le seul inconvénient de cette approche est que l’espace noyau et l’espace utilisateur deviennent plus petits.
Par exemple : sur une plate-forme matérielle 32 bits comme i386, Linux définit une constante PAGE_OFFSET dans le fichier page.h :#ifdef CONFIG_MMU #define __PAGE_OFFSET (0xC0000000) //0xC0000000为3GB #else #define __PAGE_OFFSET (0x00000000) #endif #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
Linux utilise PAGE_OFFSET comme limite pour diviser l'espace mémoire virtuelle de 4 Go en deux parties : adresse L'espace d'adressage faible de 0 à 3G-1 est l'espace utilisateur, d'une taille de 3 Go ; l'espace d'adressage élevé de 3 Go à 4 Go-1 est l'espace du noyau, d'une taille de 1 Go.
Lorsque plusieurs programmes sont exécutés dans le système, la relation entre plusieurs espaces utilisateur et l'espace noyau peut être exprimée comme suit : Comme le montre la figure, les programmes 1, 2...n partagent l'espace noyau . Bien entendu, le partage fait ici référence au temps partagé, car à tout moment, pour un système à processeur monocœur, un seul programme peut être exécuté.La disposition globale de l'espace noyau
Dans le processus de développement de Linux, avec la mise à jour de l'équipement matériel et l'amélioration du niveau technique, le développement de la disposition de son espace noyau est également un moyen de patch continu. La conséquence de ceci est que l'espace du noyau est divisé en plusieurs zones différentes, et différentes zones ont des méthodes de mappage différentes.Habituellement, les gens pensent que l'espace du noyau Linux comporte trois zones, à savoir la zone DMA (ZONE_DMA), la zone normale (ZONE_NORMAL) et la zone mémoire haut de gamme (ZONE_HIGHMEM).
Afin d'améliorer la vitesse d'accès du noyau à l'adresse physique mémoire via des adresses virtuelles, virtualisation de l'espace noyau L'adresse et l'adresse mémoire physique adoptent une méthode de mappage fixe qui correspond une à une de l'adresse basse à l'adresse haute :
Comme vous pouvez. voir,
Cette méthode de mappage fixe fait correspondre l'adresse virtuelle et l'adresse mémoire physique une par une. La relation entre les adresses devient très simple, c'est-à-dire que l'adresse virtuelle du noyau et l'adresse physique réelle ne diffèrent en valeur que d'un. décalage fixe PAGE_OFFSET, donc lorsque le noyau utilise l'adresse virtuelle pour accéder au cadre de page physique, il lui suffit de soustraire PAGE_OFFSET de l'adresse virtuelle. Vous pouvez obtenir l'adresse physique réelle, ce qui est beaucoup plus rapide que d'utiliser la table des pages !Étant donné que cette méthode utilise presque directement des adresses physiques, ce type d'espace noyau basé sur un mappage fixe est également appelé « espace mémoire physique », ou mémoire physique en abrégé. De plus, La méthode de cartographie fixe étant une cartographie linéaire, cette zone est également appelée zone de cartographie linéaire. Bien sûr, dans ce cas (lorsque la mémoire physique réelle de l'ordinateur est petite), l'espace de mappage fixe du noyau n'occupe qu'une partie de l'espace total de 1 Go du noyau. Par exemple : lors de la configuration d'un système informatique x86 avec 32 Mo de mémoire physique réelle, la zone de mappage fixe du noyau est l'espace de 32 Mo de PAGE_OFFSET~ (PAGE_OFFSET+0x02000000). Alors, que faire de l'espace virtuel restant du noyau dans l'espace du noyau ? Bien entendu, la mémoire physique est toujours utilisée dans le mappage non linéaire des tables de pages selon la méthode ordinaire de gestion de l'espace virtuel. Plus précisément, la zone de mappage fixe est supprimée de la totalité de l'espace noyau de 1 Go, puis une zone d'isolation de 8 Mo au début est supprimée de la partie restante. Le reste est une zone de mappage de mémoire virtuelle ordinaire qui est mappée de la même manière que l'espace utilisateur. . Dans cette zone, non seulement il n'y a pas de relation de mappage fixe entre les adresses virtuelles et les adresses physiques, mais la mémoire dynamique est également obtenue en appelant la fonction du noyau vmalloc(), cette zone est donc appelée zone d'allocation vmalloc, comme le montre la figure ci-dessous. : Le mappage fixe entre l'espace du noyau et les cadres de page physiques mentionné ici est essentiellement une "réservation" des pages du noyau aux cadres de page physiques. Cela ne signifie pas que ces pages "occupent" ces cadres de page physiques. Autrement dit, la page virtuelle est liée au cadre de page physique uniquement lorsque la page virtuelle a réellement besoin d'accéder au cadre de page physique. Normalement, lorsqu'un cadre de page physique n'est pas utilisé par sa page virtuelle correspondante, le cadre de page peut être utilisé par l'espace utilisateur et la zone d'allocation kmalloc du noyau introduite plus tard. Pour tout l'espace du noyau de 1 Go, les gens appellent également les 16 Mo en tête de l'espace la zone DMA, c'est-à-dire la zone ZONE_DMA, car dans le passé, le matériel correction de l'espace DMA dans la mémoire physique. L'espace inférieur de 16 Mo et les zones restantes sont appelées zones normales, c'est-à-dire ZONE_NORMAL. Mémoire haut de gamme de l'espace du noyau Avec le développement de la technologie informatique, la mémoire physique réelle de l'ordinateur devient de plus en plus grande, ce qui agrandit la zone de cartographie fixe du noyau (zone linéaire) et plus grand. Évidemment, si aucune restriction n'est imposée, la zone d'allocation vmalloc (zone non linéaire) cessera d'exister lorsque la mémoire physique réelle atteindra 1 Go. Par conséquent, le code du noyau précédemment développé qui appelait vmalloc() n'est plus disponible. Évidemment, afin d'être compatible avec le code du noyau antérieur, cela n'est pas autorisé. La raison du problème ci-dessus est qu'il n'était pas prévu que la mémoire physique réelle puisse dépasser 1 Go, donc la limite du noyau a été fixée la zone de mappage n'a pas été définie. Définissez une limite et laissez-la augmenter à mesure que la mémoire physique réelle augmente. Limiter la limite supérieure de la zone de mappage fixe de l'espace du noyau afin qu'elle ne puisse pas augmenter arbitrairement avec l'augmentation de la mémoire physique. Linux stipule que la valeur limite supérieure de la zone de mappage du noyau ne peut pas être supérieure à une valeur high_menory constante inférieure à 1 Go. Lorsque la mémoire physique réelle est grande, 3G + high_memory est utilisé comme limite pour déterminer la zone de mémoire physique. En d'autres termes, l'idée la plus basique de la mémoire haut de gamme : emprunter une section d'espace d'adressage, établir un mappage d'adresses temporaire et le libérer après utilisation. Cet espace d'adressage peut être recyclé pour accéder à toute la mémoire physique. Lorsque l'ordinateur dispose d'une grande mémoire physique, le schéma schématique de l'espace noyau est le suivant :
Zone de mappage vmalloc Zone de mappage du noyau persistant Zone de cartographie temporaire . Modificateur d'allocation de mémoire du noyau gfp Afin de fournir les explications nécessaires pour les requêtes dans la fonction de demande de mémoire du noyau, Linux définit une variété de modificateurs d'allocation de mémoire gfp. Ce sont des modificateurs de comportement, des modificateurs de zone et des modificateurs de type. Modificateurs de comportement Modificateur de zone d'allocation de mémoire du noyau principal de Linux 类型修饰符实质上是上述所述修饰符的联合应用。也就是:将上述的某些行为修饰符和区修饰符,用“|”进行连接并另外取名的修饰符。这里就不多介绍了。 内核常用内存分配及地址映射函数 函数vmalloc()在vmalloc分配区分配内存,可获得虚拟地址连续,但并不保证其物理页框连续的较大内存。与物理空间的内存分配函数malloc()有所区别,vmalloc()分配的物理页不会被交换出去。函数vmalloc()的原型如下: 其中,参数size为所请求内存的大小,返回值为所获得内存虚拟地址指针。 与vmalloc()配套的释放函数如下: 其中,参数addr为待释放内存指针。 kmalloc()是内核另一个常用的内核分配函数,它可以分配一段未清零的连续物理内存页,返回值为直接映射地址。由kmalloc()可分配的内存最大不能超过32页。其优点是分配速度快,缺点是不能分配大于128KB的内存页(出于跨平台考虑)。 在linux/slab.h文件中,该函数的原型声明如下: 其中,参数size为以字节为单位表示的所申请空间的大小;参数flags决定了所分配的内存适合什么场合。 与函数kmalloc()对应的释放函数如下: 小结一下,kmalloc、vmalloc、malloc的区别: 也就是说:kmalloc、vmalloc这两个函数所分配的内存都处于内核空间,即从3GB~4GB;但位置不同,kmalloc()分配的内存处于3GB~high_memory(ZONE_DMA、ZONE_NORMAL)之间,而vmalloc()分配的内存在VMALLOC_START~4GB(ZONE_HIGHMEM)之间,也就是非连续内存区。一般情况下在驱动程序中都是调用kmalloc()来给数据结构分配内存,而vmalloc()用在为活动的交换区分配数据结构,为某些I/O驱动程序分配缓冲区,或为模块分配空间。 可参考文章:Kmalloc和Vmalloc的区别。 与上述在虚拟空间分配内存的函数不同,alloc_pages()是在物理内存空间分配物理页框的函数,其原型如下: 其中,参数order表示所分配页框的数目,该数目为2^order。order的最大值由include/Linux/Mmzone.h文件中的宏MAX_ORDER决定。参数gfp_mask为说明内存页框分配方式及使用场合。 函数返回值为页框块的第一个页框page结构的地址。 调用下列函数可以获得页框的虚拟地址: 使用函数alloc_pages()获得的内存应该使用下面的函数释放: kmap()是一个映射函数,它可以将一个物理页框映射到内核空间的可持久映射区。这种映射类似于内核ZONE_NORMAL的固定映射,但虚拟地址与物理地址的偏移不一定是PAGE_OFFSET。由于内核可持久映射区的容量有限(总共只有4MB),因此当内存使用完毕后,应该立即释放。 函数kmap()的函数原型如下: 小结 由于CPU的地址总线只有32位, 32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间(232=4Gbit),在物理上理论上最多拥有4G内存(除了IO地址空间,实际内存容量小于4G),逻辑空间也只能描述4G的线性地址空间。 为了合理的利用逻辑4G空间,Linux采用了3:1的策略,即内核占用1G的线性地址空间,用户占用3G的线性地址空间。所以用户进程的地址范围从0~3G,内核地址范围从3G~4G,也就是说,内核空间只有1G的逻辑线性地址空间。 Si la mémoire physique Linux est inférieure à 1 Go d'espace, le noyau mappe généralement linéairement la mémoire physique sur son espace d'adressage, c'est-à-dire un mappage un à un, ce qui peut améliorer la vitesse d'accès. Cependant, lorsque la mémoire physique Linux dépasse 1 Go, le mécanisme d'accès linéaire ne suffit pas, car seul 1 Go de mémoire peut être mappé et la mémoire physique restante ne peut pas être gérée par le noyau. Par conséquent, afin de résoudre ce problème, Linux L'adresse. est divisée en deux parties, la zone linéaire et la zone non linéaire. La zone linéaire doit être d'un maximum de 896 M, et les 128 M restants sont la zone non linéaire. Par conséquent, la mémoire physique mappée par la zone linéaire devient une mémoire bas de gamme et la mémoire physique restante est appelée mémoire haut de gamme. Contrairement aux régions linéaires, les régions non linéaires ne sont pas mappées en mémoire à l'avance, mais mappées dynamiquement lorsqu'elles sont utilisées. La mémoire bas de gamme est divisée en deux parties : ZONE_DMA : 16 Mo au début de l'espace noyau, ZONE_NORMAL : 16 Mo ~ 896 Mo de l'espace noyau (mappage fixe). Le reste est de la mémoire haut de gamme : ZONE_HIGHMEM : espace noyau 896 Mo ~ fin (1G). Selon les différents objectifs de l'application, la mémoire haut de gamme est divisée en trois parties : la zone vmalloc, la zone de mappage persistant et la zone de mappage temporaire. La zone vmalloc est allouée à l'aide de la fonction vmalloc() ; la zone de mappage persistante utilise allc_pages() pour obtenir la page correspondante, et est directement mappée à l'aide de la fonction kmap() ; la zone de mappage temporaire est généralement utilisée pour des besoins particuliers. "Espace utilisateur et espace noyau" Oui Zone de cartographie persistante Tutoriel vidéo LinuxZONE_DMA et ZONE_NORMAL
La zone de mappage vmalloc est la partie principale de la mémoire haut de gamme. espace entre la tête de cet intervalle et l'espace de mappage linéaire du noyau. Une zone d'isolement de 8 Mo et une zone d'isolement de 4 Ko à la fin et la zone de mappage persistante suivante.
La méthode de mappage de la zone de mappage vmalloc est exactement la même que celle de l'espace utilisateur. Le noyau peut obtenir de la mémoire dans cette zone en appelant la fonction vmalloc(). La fonction de cette fonction est équivalente à malloc() dans l'espace utilisateur. L'espace mémoire fourni est continu à l'adresse virtuelle (notez que l'adresse physique n'est pas garantie d'être continue).
Si la page correspondant à la mémoire haut de gamme est obtenue via alloc_page(), comment lui trouver un espace linéaire ? Le noyau réserve spécialement un espace linéaire à cet effet, à partir de PKMAP_BASE, qui est utilisé pour mapper la mémoire haut de gamme, qui est la zone de mappage persistante du noyau.
Dans la zone de mappage persistant du noyau, vous pouvez établir un mappage à long terme entre le cadre de page physique et la page virtuelle du noyau en appelant la fonction kmap(). Cet espace est généralement de 4 Mo et peut mapper jusqu'à 1024 cadres de page. Par conséquent, afin d'améliorer la rotation des cadres de page, la fonction kunmap() doit être appelée à temps pour libérer les cadres de page physiques qui sont. n'est plus utilisé.
La zone de cartographie temporaire est également appelée zone de cartographie fixe et zone réservée. Cette zone est principalement utilisée dans les systèmes multiprocesseurs. L'espace mémoire obtenu dans cette zone n'étant pas protégé, la mémoire obtenue doit être utilisée à temps, sinon, dès qu'il y aura une nouvelle demande, le contenu du cadre de page sera écrasé ; , cette zone est donc appelée zone de cartographie temporaire. Un très bon article sur la zone de mémoire haut de gamme :
Explication détaillée de l'espace utilisateur Linux et de l'espace noyau de la mémoire haut de gamme Les modificateurs de comportement dans les fonctions d'allocation de mémoire décrivent comment le noyau doit allouer la mémoire. Les principaux modificateurs de comportement sont les suivants :
Modificateurs de comportement d'allocation de mémoire du noyau principal pour Linux
L'allocateur peut dormir
__GFP_WAIT
L'allocateur peut accéder au tampon d'événements d'urgence pool
__GFP_HIGH
L'allocateur peut démarrer le disque IO
__GFP_IO
L'allocateur peut démarrer le système de fichiers IO
__GFP_FS
L'allocateur doit utiliser des cadres de page qui sont sur le point d'être éliminés de la cache
__GFP_COLD
L'allocateur n'émet pas d'avertissements
__GFP_NOWARN
Réallouer en cas d'échec d'allocation
__GFP_REPEAT
Réallouer en cas d'échec d'allocation jusqu'au succès
__GFP_NOFAILT
Plus en cas d'échec de l'allocation Réallocation
__GFP_NORETRY
Modificateur de région
Le modificateur de région indique à partir de quelle région de l'espace mémoire du noyau doit être allouée. Par défaut, l'allocateur de mémoire commence à partir de ZONE_NORMAL dans l'espace du noyau et alloue progressivement des zones de mémoire au demandeur de mémoire. Si l'utilisateur a spécifiquement besoin d'obtenir de la mémoire à partir de ZONE_DMA ou de ZONE_HOGNMEM, alors le demandeur de mémoire doit utiliser les deux zones suivantes dans la mémoire. fonction de requête. Description du modificateur :
Modificateur
__GFP_DMA Allouer de la mémoire à partir de la zone ZONE_DMA
__GFP _HIGHMEM Allouer de la mémoire de la zone ZONE_HIGHMEM
类型修饰符
函数vmalloc()
void *vmalloc(unsigned long size)
{
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
}
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
{
return kmalloc(size, (gfp_mask | __GFP_COMP) & ~__GFP_HIGHMEM);
}
void vfree(const void *addr)
{
kfree(addr);
}
函数kmalloc()
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
struct kmem_cache *cachep;
void *ret;
if (__builtin_constant_p(size)) {
int i = 0;
if (!size)
return ZERO_SIZE_PTR;
#define CACHE(x) \
if (size <= x) \
goto found; \
else \
i++;
#include <linux/kmalloc_sizes.h>
#undef CACHE
return NULL;
found:
#ifdef CONFIG_ZONE_DMA
if (flags & GFP_DMA)
cachep = malloc_sizes[i].cs_dmacachep;
else
#endif
cachep = malloc_sizes[i].cs_cachep;
ret = kmem_cache_alloc_notrace(cachep, flags);
trace_kmalloc(_THIS_IP_, ret,
size, slab_buffer_size(cachep), flags);
return ret;
}
return __kmalloc(size, flags);
}
void kfree(const void *objp)
{
struct kmem_cache *c;
unsigned long flags;
trace_kfree(_RET_IP_, objp);
if (unlikely(ZERO_OR_NULL_PTR(objp)))
return;
local_irq_save(flags);
kfree_debugcheck(objp);
c = virt_to_cache(objp);
debug_check_no_locks_freed(objp, obj_size(c));
debug_check_no_obj_freed(objp, obj_size(c));
__cache_free(c, (void *)objp);
local_irq_restore(flags);
}
函数alloc_pages()
static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)
{
if (unlikely(order >= MAX_ORDER))
return NULL;
return alloc_pages_current(gfp_mask, order);
}
void *page_address(struct page *page)
{
unsigned long flags;
void *ret;
struct page_address_slot *pas;
if (!PageHighMem(page))
return lowmem_page_address(page);
pas = page_slot(page);
ret = NULL;
spin_lock_irqsave(&pas->lock, flags);
if (!list_empty(&pas->lh)) {
struct page_address_map *pam;
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) {
ret = pam->virtual;
goto done;
}
}
}
done:
spin_unlock_irqrestore(&pas->lock, flags);
return ret;
}
void __free_pages(struct page *page, unsigned int order)
{
if (put_page_testzero(page)) {
if (order == 0)
free_hot_page(page);
else
__free_pages_ok(page, order);
}
}
函数kmap()
void *kmap(struct page *page)
{
might_sleep();
if (!PageHighMem(page))
return page_address(page);
return kmap_high(page);
}
Mémoire bas de gamme (3G~3G+high_memory-1)Zone de cartographie linéaire (zone de cartographie fixe)
ZONE_NORMAL
ZONE_D MA
Espace utilisateur (0 ~ 3G-1)
Répertoire de pages --> Répertoire de pages intermédiaire --> Tableau des pages
Recommandations associées : "
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!