Allocating memory
Kmalloc
Kmalloc 可以用来在内核中申请小块的物理地址连续内存,申请的内存里的内容不会被清零。
函数声明
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
参数说明:size是表示申请的内存大小,flags表示申请内存的方式,GFP_KERNEL的前缀GFP表示最终调用的函数是__get_free_pages.使用GFP_KERNEL意味着当前进程可能会被休眠,直到有空闲的内存页可以被申请,这就要求调用kmalloc的函数必须是可重入的,且不能有原子语句。
如果进程不能被sleep的话,就需要使用GFP_ATOMIC了。通常情况下kernel会保留一些空闲的pages以备不时之需,如果使用GFP_ATOMIC,那么即使kernel中只有一页空闲的内存,也能申请成功。当然一页都没有的话,就会失败了。
内存区一般分为三种,分别是NORMAL、DMA_zone、HIGH_zone.如果要求分配dma传输空间的话,只会考虑DMA_zone的内存。三种内存区的分类依赖于硬件,对于大部分arm架构来说,所有内存都是DMA_zone的。
Kmalloc申请的最小内存区为32或64字节,最大的值也有限制,一般是128k字节。
后备高速缓存(lookaside cache)
一些设备驱动中可能会反复分配和释放同样大小的内存块,这时就可以将这些内存块放在缓存上。在linux 2.6中USB和SCSI驱动程序就使用了这种高速的缓存。
使用方法,步骤一,创建一个高速缓存对象,函数:
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 flags));
参数size表示将来申请的内存块的大小,两个函数是可选参数,在对象创建和释放的时候调用。
步骤二,继续申请内存块
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
参数cache就是刚申请的高速缓存。
步骤三、用完了就释放
void kmem_cache_free(kmem_cache_t *cache, const void *obj);
步骤四、所有内存块都用完了,释放高速缓存
int kmem_cache_destroy(kmem_cache_t *cache);
内存池(Memory Pools)
内核中有些地方的内存分配是不允许失败的。为了在这种情况下,内存分配成功,内核开发者提供了一种名为内存池(mempool)的对象。它试图始终保持一定数量的空闲内存,以便在紧急状态下使用。
内存池对象的类型是mempool_t,创建方法是
mempool_t *mempool_create(int min_nr,
mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn,
void *pool_data);
参数min_nr表示保持的对象的最小分配数目。对象的实际分配和释放函数由alloc_fn、free_fn处理。原型如下:
typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data);
typedef void (mempool_free_t)(void *element, void *pool_data);
最后一个参数pool_data,将被传入alloc_fn和free_fn。
内核中提供了这两个函数,分别是mempool_alloc_slab 和 mempool_free_slab。它们会调用kmem_cache_alloc 和kmem_cache_free。所以构造内存池的代码一般如下,步骤一:申请后备缓存和内存池
cache = kmem_cache_create(. . .);
pool = mempool_create(MY_POOL_MINIMUM,
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_alloc先通过普通分配函数进行分配,如果分配失败,就使用mem_pool里事先申请的内存。mempool_free释放一个对象时,如果内存池中的对象数目低于最低值,就将对象保存到内存池中,否则这个对象才还给系统。
也可以调整mempool的大小:
int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
步骤三:删除内存池
void mempool_destroy(mempool_t *pool);
切记删除之前,必须将所有分配的对象返回内存池,否则会出现oops错误。
由于mem_pool会造成大量的内存浪费,因此一般不要使用。
Get_free_page及其相关函数
如果驱动中需要分配大块的内存,这时使用面向页的分配技术会更好一些。分配页面的函数如下:
(1)get_zeroed_page(unsigned int flags);
返回指向新页面的指针,并将页面清零
(2)__get_free_page(unsigned int flags);
返回指向新页面的指针,不清零
(3)__get_free_pages(unsigned int flags, unsigned int order);
分配物理地址连续的若干页面,并返回指向第一个字节的指针。不清零
Order表示页面数量为2的order次幂,比如order = 0,即一个页面,order = 3,即8个页面。允许的order的最大值一般是10或者11(对应1024或者2048个页),这依赖于体系结构。
删除函数:
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);