区名 | 描述 |
---|---|
ZONE_DMA | 这个区的页能用来执行DMA |
ZONE_DNA32 | 和ZONE_DMA类似,但只能被32位设备访问 |
ZONE_NORMAL | 这个区包含的都是能正常映射的页 |
ZONE_HIGHEM | 这个区包含“高端内存”,其中的页不能永久地映射到内核地址空间 |
所有接口都是以页为单位分配内存。
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)
unsigned long get_zeroed_page(unsigned int gfp_mask)
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。
void * kmalloc(size_t size, gfp_t flags) //返回至少有size大小的内存块指针。flags可以为GFP_KERNEL
三类:行为修饰符、区修饰符及类型。
行为修饰符:表示内核应当如何分配所需的内存。
区修饰符:表示内存区应当从何处分配。
不能给__get_free_pages()或kalloc()指定ZONE_HIGHMEM,因为返回逻辑地址,而不是page。只有alloc_pages()才可以分配高端内存。
类型标志:指定所需的行为和区描述符。
void kfree(const void *ptr)
kfree(NULL)是安全的。
void * vmalloc(unsigned long size) //返回大小至少为size的逻辑连续的内存区。
````void vfree(const void *addr) //释放通过vmalloc()所获得到的内存。```
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分配器扮演了通用数据结构缓存层的角色。
slab层把不同的对象划分为所谓告诉缓存组,其中每个缓存组都存放不同类型的对象,每种对象类型对应一个告诉缓存。
slab由一个或多个物理上连续的页组成。每个高速缓存由多个slab组成。
每个slab处于状态:满、部分满或空。
一个新的高速缓存创建函数:
name:高速缓存的名字;
size:高速缓存中每个元素的大小;
align:slab内第一个对象的偏移,确保在页内进行特定的对齐,0表示标准对齐;
flags:可选设置项,用来控制高速缓存的行为,0表示没有特殊行为。
撤销高速缓存:
void * kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) //返回一个指向对象的指针,flags的值传递给_get_free_pages()。
void kmem_cache_free(struct kmem_cache *cachep, void *objp) //将cachep中的objp对象标志空。
着色:https://www.cnblogs.com/linhaostudy/p/13184704.html
32位和64位栈一般为2页,为8KB和16KB。
内核栈,中断栈
高端内存中的页不能永久映射到内核地址空间上。用alloc_pages()函数以__GFP_HIGHMEM标志获得到的页不可能有逻辑地址。
映射一个给定的page结构到内核地址空间:
void *kmap(struct page *page) //page如果是低端内存则单纯的返回页的虚拟地址,如果是高端内存则建立永久映射地址,该函数可以睡眠,只能用于进程上下文。
解除映射:
void kunmap(struct page *page)
当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(原子映射)。内核可以原子地把高端内存中的一个页映射到某个保留的映射中,因此临时映射可以用在不能睡眠的地方,比如中断处理程序中。
建立临时映射函数:
void * kmap_atomic(struct page *page, enum km_type type)
这个函数不会阻塞,可以用在中断上下文和其他不能重新调度的地方,也禁止内核抢占。
取消映射:
void kunmap_atomic(void *kvaddr, enum km_type type)
每个CPU的数据存放在一个数组中。