i. 分配函数的选择
kmalloc():如果需要连续的物理页,可以使用此函数,这是内核中内存分配的常用方式,也是大多数情况下应该使用的内存分配方式。传递给函数的最常用的标志是GTP_ATOMIC和GTP_KERNEL。前面的标志表示进行不睡眠的高优先级分配。在中断处理程序和其他不能睡眠的代码段中需要。后面的标志可以睡眠,在没有持自旋锁的进程上下文中使用。此函数返回内核逻辑地址。
get_free_pages():如果模块需要分配大块的内存,使用此面向页的分配技术。返回内核逻辑地址。
alloc_pages():想从高端内存中分配,就使用此函数。但返回一个指向page结构的指针。为了获得真正的指针,应该调用kmap(),把高端内存映射到内核的虚拟地址空间。
vmalloc():不需要物理上连续的页,仅仅需要虚拟地址上连续的页。
Slab高速缓存:多次创建和销毁较大的数据结构。
内存池:内核有些地方的内存分配不允许失败,为了确保这种情况的成功分配,内核开发者建立了内存池的抽象。内存池其实就是某种形式的后备高速缓存。mempool_create建立内存池对象,mempool_alloc创建内存池。
perCUP:减少数据锁定,大大减少缓存失效。唯一的要求是要禁止内核抢占。
ii. alloc_pages()
该函数分配连续的物理页,并返回一个指针,指针指向第一个页的page结构体。可以用kmap()函数把此页转换成逻辑地址。此函数可以在高端内存或普通内存分配物理页。
iii. get_free_pages()
此函数与alloc_pages()作用相同,但它直接返回所请求的第一个页的逻辑地址。此函数不能从高端内存分配物理页。
iv. void *kmalloc(size_t size, int flags)
可以获得以字节为单位的内核内存。参数flags有三类标志:行为描述符,区描述符以及类型。
行为修饰符:不常用,忽略。
区描述符:__GFP_DMA从ZONE_DMA分配。
__GFP_HIGHMEM从ZONE_HIGHMENZONG_NORMAL分配。
类型标志:GFP_KERNEL,常规分配方式,可能阻塞。用于进程上下文中。
GFP_ATOMIC,高优先级的,不会睡眠。用着中断处理程序,下半部,
不能睡眠的地方
v. vmalloc
工作方式类似于kmalloc(), 不过虚拟地址连续,物理地址不连续。获得大块内存时使用。
vi. slab高速缓存
slab高速缓存一般用于数据频繁分配和回收,如inode和task_struct数据结构。
1. slab层设计
2. slab分配器接口使用
下面以进程描述符task_struct为例说明slab分配器接口使用:
3. slab着色
让slab缓存的对象在CPU的cache彼此错开,不会造成CPU cache频频失效
vii. percpu
1. 定义CPU变量
a) 编译时定义:DEFINE_PER_CPU(type, name);
b) 动态定义:void *alloc_percpu(type)
void *__alloc_percpu(size_t size, size_t,aligh);
释放CPU变量:void free_percpu(const void *);
2. 获取CPU数据
a) get_cpu_ptr(ptr); //返回一个void类型指针,指向处理器的ptr拷贝
b) put_cpu_ptr(ptr);//完成:重新激活内核抢占
3. 实例说明
4. 返回数据:per_cpu_ptr(ptr,cpu);
此函数返回指定处理器的唯一数据。这个函数不会禁止内核抢占,如果需要访问另外处理器的数据,需要给数据上锁。