系统相关
首页 > 系统相关> > 内存管理基础(Linux内核涉及与实现)

内存管理基础(Linux内核涉及与实现)

作者:互联网

文章目录

1. 页

2. 区

区名描述
ZONE_DMA这个区的页能用来执行DMA
ZONE_DNA32和ZONE_DMA类似,但只能被32位设备访问
ZONE_NORMAL这个区包含的都是能正常映射的页
ZONE_HIGHEM这个区包含“高端内存”,其中的页不能永久地映射到内核地址空间

在这里插入图片描述
在这里插入图片描述

3. 获得页

所有接口都是以页为单位分配内存。
struct page * alloc_pages(gfp_t gfp_mask, unsigned int order) //分配2^order(1<<order)个连续的物理页,返回第一个页的page结构体,出错返回NULL。
把给定的页转换成它的逻辑地址:
void * page_address(struct page *page) //返回给定物理页当前所在的逻辑地址。
与alloc_pages()类似的函数:
unsigned long __get_free_pages(gfp_t gfp_mask, ungigned int order) //直接放回第一个页的逻辑地址。
只需要一页:
struct page * alloc_page(gfp_t gfp_mask)
unsigned long __get_free_page(gfp_t gfp_mask)

3.1 获得填充为0的页

unsigned long get_zeroed_page(unsigned int gfp_mask)
在这里插入图片描述

3.2 释放页

void __free_pages(struct page *page, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
void __free_pages(unsigned long addr)

#define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)
__GFP_WAIT : 缺内存页的时候可以睡眠;
__GFP_IO : 允许启动磁盘IO;
__GFP_FS : 允许启动文件系统IO。

4. kmalloc():物理地址和虚拟地址都连续

4.1 gfp_mask标志

三类:行为修饰符、区修饰符及类型。
行为修饰符:表示内核应当如何分配所需的内存。
在这里插入图片描述
在这里插入图片描述
区修饰符:表示内存区应当从何处分配。
在这里插入图片描述
不能给__get_free_pages()或kalloc()指定ZONE_HIGHMEM,因为返回逻辑地址,而不是page。只有alloc_pages()才可以分配高端内存。
类型标志:指定所需的行为和区描述符。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 kfree()

void kfree(const void *ptr)
kfree(NULL)是安全的。

5. vmalloc():虚拟地址连续,物理地址地址无须连续

void * vmalloc(unsigned long size) //返回大小至少为size的逻辑连续的内存区。
````void vfree(const void *addr) //释放通过vmalloc()所获得到的内存。```

6. slab层

slab分配器是基于对象进行管理的,所谓的对象就是内核中的数据结构(例如:task_struct,file_struct 等)。相同类型的对象归为一类,每当要申请这样一个对象时,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免内部碎片。slab分配器并不丢弃已经分配的对象,而是释放并把它们保存在内存中。slab分配对象时,会使用最近释放的对象的内存块,因此其驻留在cpu高速缓存中的概率会大大提高。
在这里插入图片描述
简要分析下这个图:kmem_cache是一个cache_chain的链表,描述了一个高速缓存,每个高速缓存包含了一个slabs的列表,这通常是一段连续的内存块。存在3种slab:slabs_full(完全分配的slab),slabs_partial(部分分配的slab),slabs_empty(空slab,或者没有对象被分配)。slab是slab分配器的最小单位,在实现上一个slab有一个货多个连续的物理页组成(通常只有一页)。单个slab可以在slab链表之间移动,例如如果一个半满slab被分配了对象后变满了,就要从slabs_partial中被删除,同时插入到slabs_full中去。
举例说明:如果有一个名叫inode_cachep的struct kmem_cache节点,它存放了一些inode对象。当内核请求分配一个新的inode对象时,slab分配器就开始工作了:


slab分配器扮演了通用数据结构缓存层的角色。
在这里插入图片描述

6.1 slab层的设计

slab层把不同的对象划分为所谓告诉缓存组,其中每个缓存组都存放不同类型的对象,每种对象类型对应一个告诉缓存。
slab由一个或多个物理上连续的页组成。每个高速缓存由多个slab组成。
每个slab处于状态:满、部分满或空。
在这里插入图片描述
在这里插入图片描述

6.2 slab分配器的接口

一个新的高速缓存创建函数:
在这里插入图片描述s
name:高速缓存的名字;
size:高速缓存中每个元素的大小;
align:slab内第一个对象的偏移,确保在页内进行特定的对齐,0表示标准对齐;
flags:可选设置项,用来控制高速缓存的行为,0表示没有特殊行为。
撤销高速缓存:
在这里插入图片描述

着色:https://www.cnblogs.com/linhaostudy/p/13184704.html

7. 在栈上静态分配

32位和64位栈一般为2页,为8KB和16KB。
内核栈,中断栈

8. 高端内存的映射

高端内存中的页不能永久映射到内核地址空间上。用alloc_pages()函数以__GFP_HIGHMEM标志获得到的页不可能有逻辑地址。

8.1 永久映射

映射一个给定的page结构到内核地址空间:
void *kmap(struct page *page) //page如果是低端内存则单纯的返回页的虚拟地址,如果是高端内存则建立永久映射地址,该函数可以睡眠,只能用于进程上下文。
解除映射:
void kunmap(struct page *page)

8.2 临时映射

当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(原子映射)。内核可以原子地把高端内存中的一个页映射到某个保留的映射中,因此临时映射可以用在不能睡眠的地方,比如中断处理程序中。
建立临时映射函数:
void * kmap_atomic(struct page *page, enum km_type type)
这个函数不会阻塞,可以用在中断上下文和其他不能重新调度的地方,也禁止内核抢占。
在这里插入图片描述
取消映射:
void kunmap_atomic(void *kvaddr, enum km_type type)

9. 每个CPU的分配

每个CPU的数据存放在一个数组中。

10. 新的每个CPU接口

10.1 编译是的每个CPU数据

10.2 运行时的每个CPU数据

11. 使用每个CPU的原因

12. 分配函数的选择

13. 小结

标签:映射,void,内存,Linux,slab,page,slabs,内核
来源: https://blog.csdn.net/u012491028/article/details/118681803