__get_free_pages()系列函数/宏是 Linux 内核本质上最底层的用于获取空闲内存的方法,因为底层的伙伴算法以 page 的 2 的 n 次幂为单位管理空闲内存,所以最底层的内存申请总是以页为单位的。
__get_free_pages()系列函数/宏包括 get_zeroed_page()、 __get_free_page()和__get_free_pages()。
/* 该函数返回一个指向新页的指针并且将该页清零 */
get_zeroed_page(
unsigned int flags);
/* 该宏返回一个指向新页的指针但是该页不清零 */
__get_free_page(
unsigned int flags);
/* 该函数可分配多个页并返回分配内存的首地址,分配的页数为 2^order,分配的页也不清零 */
__get_free_pages(
unsigned int flags,
unsigned int order);
/* 释放 */
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
__get_free_pages 等函数在使用时,其申请标志的值与 kmalloc()完全一样,各标志的含义也与kmalloc()完全一致,最常用的是 GFP_KERNEL 和 GFP_ATOMIC。
vmalloc()
vmalloc()一般用在为只存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存,vmalloc()远大于__get_free_pages()的开销,为了完成 vmalloc(),新的页表需要被建立。因此,只是调用 vmalloc()来分配少量的内存(如 1 页)是不妥的。
vmalloc()申请的内存应使用 vfree()释放, vmalloc()和 vfree()的函数原型如下:
void *vmalloc(unsigned long size);
void vfree(void * addr);
vmalloc()不能用在原子上下文中,因为它的内部实现使用了标志为 GFP_KERNEL 的 kmalloc()。
slab
一方面,完全使用页为单元申请和释放内存容易导致浪费(如果要申请少量字节也需要 1 页);另一方面,在操作系统的运作过程中,经常会涉及大量对象的重复生成、使用和释放内存问题。在Linux 系统中所用到的对象,比较典型的例子是 inode、 task_struct 等。如果我们能够用合适的方法使得在对象前后两次被使用时分配在同一块内存或同一类内存空间且保留了基本的数据结构,就可以大大提高效率。 内核的确实现了这种类型的内存池,通常称为后备高速缓存(lookaside cache)。内核对高速缓存的管理称为slab分配器。实际上 kmalloc()即是使用 slab 机制实现的。
注意, slab 不是要代替__get_free_pages(),其在最底层仍然依赖于__get_free_pages(), slab在底层每次申请 1 页或多页,之后再分隔这些页为更小的单元进行管理,从而节省了内存,也提高了 slab 缓冲对象的访问效率。
#include <linux/slab.h>
/* 创建一个新的高速缓存对象,其中可容纳任意数目大小相同的内存区域 */
struct kmem_cache *kmem_cache_create(
const char *name,
/* 一般为将要高速缓存的结构类型的名字 */
size_t size,
/* 每个内存区域的大小 */
size_t offset,
/* 第一个对象的偏移量,一般为0 */
unsigned long flags,
/* 一个位掩码:
SLAB_NO_REAP 即使内存紧缩也不自动收缩这块缓存,不建议使用
SLAB_HWCACHE_ALIGN 每个数据对象被对齐到一个缓存行
SLAB_CACHE_DMA 要求数据对象在DMA内存区分配
*/
/* 可选参数,用于初始化新分配的对象,多用于一组对象的内存分配时使用 */
void (*constructor)(
void*,
struct kmem_cache *,
unsigned long),
void (*destructor)(
void*,
struct kmem_cache *,
unsigned long)
);
/* 在 kmem_cache_create()创建的 slab 后备缓冲中分配一块并返回首地址指针 */
void *kmem_cache_alloc(
struct kmem_cache *cachep, gfp_t flags);
/* 释放 slab 缓存 */
void kmem_cache_free(
struct kmem_cache *cachep,
void *objp);
/* 收回 slab 缓存,如果失败则说明内存泄漏 */
int kmem_cache_destroy(
struct kmem_cache *cachep);
Tip: 高速缓存的使用统计情况可以从/proc/slabinfo获得。
内存池(mempool)