linux 内核内存分配函数 1 伙伴系统 1.1 alloc_page/alloc_pages/free_pages 1.1.1 alloc_pages_node 1 struct page *alloc_pages_node (int nid, unsigned int flags, unsigned int order) ;
参数
含义
nid
NUMA node ID
flags
Usual GFP_ allocation flags
order
he size of the allocation
现在的机器上都是有多个CPU和多个内存块的,以前我们都是将内存块看成是一大块内存,所有CPU到这个共享内存访问消息是一样的,这就是之前普遍使用的SMP模型
但是随着处理器的增加,共享内存可能会导致内存访问冲突越来越厉害,NUMA(Non-Uniform Memory Access)就是在这样的环境下引入的一个模型,比如一台机器有2个处理器、4个内存块,我们将1个处理器和两个内存块关联起来,称为一个 NUMA node,这样这个机器就会有两个 NUMA node,在物理分布上,NUMA node 的处理器和内存块的物理距离更小,因此访问也更快。比如这台机器会分左右两个处理器(cpu1、cpu2),在每个处理器两边放两个内存块(memory1.1, memory1.2, memory2.1,memory2.2),这样 NUMA node1 的 cpu1 访问memory1.1 和 memory1.2 就比访问 memory2.1 和 memory2.2 更快。所以使用 NUMA 的模式如果能尽量保证本 node 内的 CPU 只访问本 node 内的内存块,那这样的效率就是最高的
Get Free Page (GFP) Flags 的取值参考:Physical Page Allocation
需要分配的物理页个数为:2 的 order 次方
alloc_pages_node 的返回值:如果分配成功,是第一个页的指针;如果分配失败,是空指针 NULL
1.1.2 alloc_pages 1 2 #define alloc_pages(gfp_mask, order) \ alloc_pages_node(numa_node_id(), gfp_mask, order)
alloc_pages 是宏定义,逻辑是调用 alloc_pages_node 函数,传递给 nid 参数的值是调用 numa_node_id() 函数获取到的当前 CPU 所处的 NUMA node 的 ID,需要用户传递参数 GFP flags 和需要分配的页个数
1.1.3 alloc_page 1 #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
alloc_page 是宏定义,逻辑是调用 alloc_pages,传递给 order 参数的值为 0,表示需要分配的物理页个数为 2 的 0 次方,即 1 个物理页,需要用户传递参数 GFP flags
1.1.4 free_pages 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void free_pages (unsigned long addr, unsigned int order) { if (addr != 0 ) { VM_BUG_ON(!virt_addr_valid((void *)addr)); __free_pages(virt_to_page((void *)addr), order); } } void __free_pages(struct page *page, unsigned int order){ if (put_page_testzero(page)) { if (order == 0 ) free_hot_cold_page(page, false ); else __free_pages_ok(page, order); } }
参数
含义
addr
要释放的连续物理页中第一个的地址
order
被释放的物理页个数为 2 的 order 次方
2 Slab 2.1 kmem_cache 2.1.1 kmem_cache_create 1 2 3 4 5 6 7 kmem_cache_t *kmem_cache_create (const char *name, size_t size, size_t offset, unsigned long flags, void (*constructor)(void *, kmem_cache_t *, unsigned long flags), void (*destructor)(void *, kmem_cache_t *, unsigned long flags)) ;
参数
含义
name
被缓存对象的类名
size
每个对象的大小
offset
第一个对象在物理页中的偏移量,通常置为 0
flags
决定内存如何被分配、管理
constructor
构造函数
destructor
析构函数
flags 参数是 bitmap,决定了内存如何被分配、管理,
flag
作用
SLAB_NO_REAP
保护缓存在系统查找内存时不被削减,不推荐
SLAB_HWCACHE_ALIGN
所有数据对象跟高速缓存行对齐,平台依赖,可能浪费内存
SLAB_CACHE_DMA
每个数据对象在 DMA 内存区段分配
参数 constructor 和 destructor 是可选函数(不能只有destructor,而没有constructor ),用来初始化新分配的对象和在内存被作为整体释放给系统之前“清理”对象
constructor 函数不保证在为对象被分配内存后立即被调用,同理,destructor 函数不是立刻在一个对象被释放后调用
将 flags 按位与 SLAB_CTOR_ATOMIC,可以确保 constructor 函数 和 destructor 函数是原子的,不允许在执行中线程被置于睡眠状态
2.1.2 kmem_cache_alloc
通过调用 kmem_cache_alloc 从已创建的后备高速缓存中分配对象,
1 void *kmem_cache_alloc (kmem_cache_t *cache, int flags) ;
参数
含义
cache
调用 kmem_cache_create 函数创建的缓存
flags
GFP(Get Free Page)flags
2.1.3 kmem_cache_free
使用 kmem_cache_free 释放一个对象,
1 void kmem_cache_free (kmem_cache_t *cache, const void *obj) ;
2.1.4 kmem_cache_destroy
当用完这个后备高速缓存(通常在当模块被卸载时),释放缓存,
1 int kmem_cache_destroy (kmem_cache_t *cache) ;
2.2 kmalloc
1 void *kmalloc (size_t size, int flags) ;
参数
含义
size
要分配内存空间的字节数
flags
GFP(Get Free Page)flags
分配到的内存空间可能会略大于 size 参数的值
flags 参数可取以下值,
flags 取值
含义
GFP_KERNEL
在分配内存空间时,当前进程可被置于睡眠状态
GFP_ATOMIC
在分配内存空间时,当前进程不可被打断
GFP_USER
用于在用户空间分配内存,当前进程可被置于睡眠状态
GFP_HIGHUSER
和 GFP_USER 相似,但是在 high memory 分配内存
GFP_NOIO
和 GFP_KERNEL 相似,但是在内存分配过程中限制 I/O 操作
GFP_NOFS
和 GFP_KERNEL 相似,但是在内存分配过程中限制文件系统调用
更多 flags 的取值可参考 <linux/gfp.h> 头文件
2.3 kzalloc
kzalloc 等价于先调用 kmalloc 分配一块内存空间,然后初始化为0,
1 2 3 4 5 6 7 8 9 static inline void *kzalloc (size_t size, gfp_t flags) { return kmalloc(size, flags | __GFP_ZERO); }
2.4 kfree
分配内存后如果不释放会造成内存泄漏,在内核中可能导致系统崩溃,
1 void kfree (const void *objp) ;
可以调用 kfree 函数释放动态分配的内存
3 vmalloc/vfree 3.1 vmalloc 1 void *vmalloc (unsigned long size) ;
使用 vmalloc 函数在虚拟地址空间分配连续的内存空间,这些内存的物理地址并不是连续的
3.2 vfree 1 void vfree (void * addr) ;
使用 vfree 函数释放由 vmalloc 函数分配的内存空间