- #inlcude<linux/slab.h>
- kmem_cache_t *kmem_cache_create(const char *name,size_t size,
- size_t offset,
- unsigned long flags,
- void (*constructor)(void *,kmem_cache_t *,
- unsigned long flags),
- void (*destructor)(void *,kmem_cache_t *,
- unsigned long falgs));
- void *kmem_cache_alloc(kmem_cache_t *cache,int flags);
参数cache是前面创建好的高速缓存,参数flags和kmalloc相同。
释放一个内存对象:
- void kmem_cache_free(kmem_cache_t *cache,const void *obj);
释放高速缓存:
- int kmem_cache_destroy(kmem_cache_t *cache);
只有在所有对象都归还后,该操作才会成功,应该检查该函数返回状态,如果失败(返回负值)说明发生了内存泄露,有些对象未被释放。
3. 内存池
为了确保内存分配的成功,内核开发者建立了一种称为内存池的抽象。内存池其实就是某种形式的后备高速缓存,它试图始终保存空闲的内存,以便在紧急状态下使用。
内核中内存池的对象类型为:mempool_t,用下面的函数来建立内存池对象:
- #include<linux/mempoll.h>
- mempool_t *mempoll_create(int min_nr,
- mempool_alloc_t *alloc_fn,
- mempool_free_t *free_fn,
- void *pool_data);
min_nr 表示内存池应该始终保持的已分配对象的最少数目。对象的实际分配和释放由后面两个函数alloc_fn和free_fn处理,定义如下:(但是通常我们用内核提供的两个函数mempool_alloc_slab,mempool_free_slab来完成)
- typedef void *(mempool_alloc_t)(int gfp_mask,void *pool_data);
- typedef void (mempool_free_t)(void *element,void *pool_data);
而mempool_create的第四个参数pool_data即为传入分配和释放函数的第二个参数。
构造内存池的通用写法:
首先,创建内存池:
- cache = kmem_cache_create(...);
- pool = mempool_create(MY_POOL_MININUM,mempool_alloc_slab,mempool_free_slab,cache);
然后用如下函数分配和释放对象:
- void *mempool_alloc(mempool_t *pool, int gfp_mask);
- void mempool_free(void *element,mempool_t *pool);
创建mempool时,会多次调用分配函数为预先分配的对象创建内存池,之后,对mempool_alloc的调用将首先通过分配函数获得该对象。如果该分配失败,就会返回预先分配的对象(如果存在的话)。
mempool_free释放一个对象,如果预先分配的对象数目小于要求的最低数目,该对象会保留在内存池中,否则,该对象返回给系统。
还可以利用下面函数调整mempool的大小:
- int mempool_resize(mempool_t *pool, int new_min_nr,int gfp_mask);
将内存池大小调整为至少有new_min_nr个预分配对象。
如果不在需要内存池,调用:
- void mempool_destroy(mempool_t *pool);
在销毁之前,必须要将多有分配的对象返回到内存池中,否则会引起oops。
驱动程序一般避免使用mempool。
4. get_free_pages和相关函数
如果模块需要分配大块的内存,使用面向页的分配技术会好些。分配页面涉及到的函数如下:
- get_zeroed_page(unsigned int flags);
- //返回指向新页面的指针并将页面清零。
- __get_free_page(unsigned int flags);
- // 类似于上述函数,但不清零页面。
- __get_free_pages(unsigned int flags,unsigned int order);
- //分配若干(物理连续的)页面,并返回指向该内存区域第一个字节的指针,但不清零页面。
释放页面涉及到的函数如下:
- void free_page(unsigned int addr);
- void free_pages(unsigned long addr,unsigned long order);
注意:分配和释放的页面数目必须相等,不然内存映射关系会破坏,系统会出错。
5.vmalloc及其辅助函数
vmalloc返回虚拟地址空间的连续区域,尽管在物理上不一定连续,vmalloc错误返回0(NULL地址),成功时一个指向线性的,大小最少为size的线性内存区域。
函数原型如下:
- #include<linux/vmalloc.h>
- void *vmalloc(unsigned long size);
- void vfree(void *addr);
- void *ioremap(unsigned long offset, unsigned long size);
- void iounmap(void *addr);
注意:kmalloc和__get_free_pages使用的地址范围和物理地址是一一对应的,可能会有一个PAGE_OFFSET的偏移,但是不需要为该地址段修改页表。但是vmalloc和ioremap使用的地址范围完全虚拟,每次分配要通过对页表的适当设置来建立内存区域。
vmalloc不能在原子上下文中使用,因为该函数内部调用了kmallc(GFP_KERNEL),可能导致休眠。