1 概念
(1)页
内核把物理页作为内存管理的基本单位,用struct page表示系统中的每个物理页。
struct page{
unsigned long flags; //页状态,是不是脏,是不是锁定在内存中
atomic_t _count; // 引用计数
atomic_t _mapcount;
unsigned long private; // 私有数据
struct address_space *mapping; //指向的页高速缓存
pgoff_t index;
struct list_head lru;
void *virtual; // 页的虚拟地址,但高端内存并不永久地映射到内核地址空间,这个值为NULL
}
(2)区
由于硬件的限制,内核并不能对所有页一视同仁。linux必须处理由如下2种由硬件存在缺陷而引起的内存寻址问题:
- 一些硬件只能用某些特定的内存地址来执行DMA
- 一些体系结构的内存的物理寻址方位比虚拟寻址范围大得多。使得一些内存不能永久地映射到内核空间上。
linux主要使用四种区: - ZONE_DMA :包含的页能用来执行DMA操作
- ZONE_DMA32:可用来执行DMA操作,只能被32位设备访问
- ZONE_NORMAL:能正常映射的页
- ZONE_HIGHEM: 包含高端内存,其中的页不能永久映射到内核地址空间
2 获取内存
2.1 获得页
//分配2^order(1<<order)个连续的物理页
struct page* alloc_pages(gfp_t gfp_mask, unsigned int order)
//释放页
void __free_pages(struct page *page, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
void free_page(unsigned long addr)
gfp_mask有3类:
- 行为修饰符 :如何分配所需的内存
- 区修饰符 :从哪个区分配内存
- 类型修饰符:组合了行为修饰符和区修饰符,将各种可能的组合归纳为不同类型,简化使用
定义在 <linux/gfp.h>中
2.2 kmalloc
按字节获取内存
void * kmalloc(size_t size, gfp_t flags)
void kfree(const void *ptr)
2.3 vmalloc
void* vmalloc(unsigned long size)
void vfree(const void* addr)
kmalloc和vmalloc
kmalloc:分配的内存物理地址是连续的,虚拟地址也是连续的
vmalloc:分配的内存物理地址无须不连续的,虚拟地址是连续的
一般使用kmalloc。
3 slab
slab分配器,用来存储内核中那些经常分配并释放的对象。
slab层把不同的对象划分为高速缓存组,每个高速缓存组都存放不同类型的对象。
这些高速缓存划分为slab,slab由一个或多个物理连续的页组成,一般是一个页。
每个slab都包含一些对象成员。
slab描述符
struct slab{
struct list_head list; //满、部门满或空链表
unsigned long colouroff; //slab着色的偏移量
void* s_mem; //slab中的第一个对象
unsigned int inuse; //slab中已分配的对象数
kmem_bufctl_t free; //第一个空闲对象
}
接口
//创建高速缓存
struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *))
//从高速缓存中分配对象
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
//向高速缓存释放对象
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
//销毁高速缓存
void kmem_cache_destroy(struct kmem_cache *cachep)
4 分配函数选择
需要连续的物理页——选择低级页分配器或者 kmalloc 函数
需要虚拟地址上连续的页——vmalloc
需要高端内存——alloc_pages
创建和撤销很多大的数据结构——slab