内存管理
linux内核设计与实现第12章笔记
1.页
2.区
对相似特性的页分组
目的:某些硬件存在缺陷,无法寻址
- 只能用特定内存DMA
- 物理寻址范围大于虚拟寻址范围
四种区
- ZONE_DMA
- ZONE_DMA32
- ZONE_NORMAL
- ZONE_HIGHEM:高端内存,页不能永久映射到内核地址
不一定要定义所有区
3.获得页
底层接口<linux/gfp.h>,函数:
struct pages *alloc_pages(gfp_mask, order)
分配1<<order个连续的物理页,返回第一个页的struct结构体
void * page_address(struct page *page)
返回指针,指向给定物理页的逻辑地址
unsigned long __get_free_pages(fgp_mask, order)
与alloc_pages一样,返回第一个页的逻辑地址
获得填充为0的页
unsigned long get_zeroed_page(gfp_mask)
释放页
8个页的使用与释放
page = __get_free_pages(GFP_KERNEL, 3);
if (!page){
return -ENOMEM;
}
/* page指向逻辑地址 */
free_pages(page, 3);
4.kmalloc
void *kmalloc(size_t size, gfp_t flags)
分配的内存区,物理上连续
如果是NULL,需要处理错误
调用成功,返回逻辑地址,内存块的大小至少为请求的大小
gfp_mask标志
分为行为修饰符,区修饰符及类型
行为修饰符
描述分配器的具体特征
例子
ptr = kmalloc(size, __GFP_WAIT|__GFP_IO|__GFP_FS);
可以阻塞,执行IO,必要时可以执行FS操作
区修饰符
默认从ZONE_NORMAL分配
不能给get_free_pages(),kmalloc()指定ZONE_HIGHMEM,因为不能返回固定的逻辑地址
类型标志
指定所需的行为和区描述符(多个的复合)
标志 修饰符标志 GFP_KERNEL (__GFP_WAIT|__GFP_IO|__GFP_FS) GFP_ATOMIC __GFP_HIGH GFP_NOIO __GFP_WAIT GFP_NOFS __GFP_WAIT|__GFP_IO GFP_KERNEL:可能睡眠,普通优先级,最常用
可以用在可以重新安全调度的进程context
GFP_ATOMIC: 不能干任何事,分配成功机会较小,适用于不能睡眠的情况(中断处理程序,软中断,tasklet)
GFP_NOFS:用在FS代码中,如果没有用GFP_NOFS,会引起更多的文件系统操作,形成套娃,最后死锁
绝大多数代码中,要么GFP_KERNEL,要么GFP_ATOMIC
kfree()
5.vmalloc()
类似kmalloc(),前者的虚拟地址连续,物理地址不需要连续
大多数情况,只有硬件设备需要物理地址连续的设备
kmalloc有更好的性能,因此vmalloc不得已才会使用-》获得大段内存
例如模块被动态插入到内核中
用法与用户空间一样:void *vmalloc(unsigned long size)
void vfree(const void*addr)
这个函数可以睡眠,没有返回值
6.slab层
空闲链表:相当于对象高速缓存
slab扮演通用数据结构缓存层的角色
设计思想
不同的对象划分为高速缓存组,每种对象对应一个高速缓存
kmalloc建立在slab层上,使用一组cache
slab:一个多个物理上连续的页组成,一般情况下仅仅一页
把用的,不用的对象组织起来
例子:inode用slab管理, struct inode用inode_cachep高速缓存
每个高速缓存用kmem_cache结构表示,包含三个链表:slabs_full,partial,empty
每个slab用slab描述符 struct slab表示
struct slab {
struct list_head list;
unsigned long colouroff; //着色的偏移量
void *s_mem; //第一个对象
unsigned inuse; //已分配的对象
kmem_bufctl_t free; //第一个空闲对象
};
slab描述符:要么在slab外另行分配,要么在slab自身开始的地方。
slab为高速缓存创建新的slab,通过__get_free_pages()
void *kmem_getpages(struct kmem_cahce *cachep, gfp_t flags, int nodeid)
nodeid:用于NUMA系统,提供较好性能
返回一个slab大小的空页
slab层的管理:在每个高速缓存的基础上,给整个内核一个简单的接口完成
通过接口,创建撤销新的高速缓存
slab分配器的接口
高速缓存的创建
struct kmem_cache * kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor) (void*));
flags:
- SLAB_HWCACHE_ALIGN:所有对象,行对齐,防止错误的共享
- SLAB_POISON:用a5a5a5a5填充slab
- SLAB_RED_ZONE:红色警戒区,检测缓冲越界
- SLAB_CACHE_DMA:用于DMA
ctor:构造函数,linux不需要
不能在中断context中使用->可能睡眠
高速缓存的撤销
int kmem_cache_destroy(struct kmem_cache *cachep)
调用条件:
所有slab为空,不能被分配
调用destroy过程中不再访问这个高速缓存
1.从缓存中分配
创建cachep后可从下面函数获取对象:
void * kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
从指定的cachep获得一个指向对象的指针
flags:针对cachep没有空闲对象时, getpages的情况
释放对象
void * kmem_cache_free(struct kmem_cache *cachep, void *objp)