1.概述:
当我们在内核驱动代码中需要用到大量内存时,一般建议采用面向页的内存管理.面向页的内存管理有如下优点:
高效利用内存,避免内存碎片的产生.因为内核都是基于页为基本单位去管理内存的.
面向页的内存管理,顾名思义,就是以页为基本单位来操作内存的.它比kmalloc()对内存使用上效率会高出很多.主要是两种策略的内存粒度不一样.面向页的内存管理其粒度是page,而kmalloc的粒度是char.使用面向页的内存管理不会导致内存的浪费,而kmalloc分配的内存会因为其粒度及系统对齐的要求而导致内存使用上的一定浪费.
2.内核中关于面向页内存分配的API:
2-1:alloc_pages():
内核中关于面向页内存分配的API最核心的为alloc_pages(),其他函数基本上都是封装了它的.
函数原型:
static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
{
if (unlikely(order >= MAX_ORDER))
return NULL;
return alloc_pages_current(gfp_mask, order);
}
函数功能:
分配若干个页.
参数说明:
gfp_mask:
此参数同 kmalloc 的用法相同.常常使用GFP_KERNEL或者GFP_ATOMIC,可能带有__GFP_DMA 标志( 给可能用在 ISA DMA 操作的内存 ) 或者__GFP_HIGHMEM 当可能使用高端内存时.
order:
order是你在请求的或释放的页数的以2为底的对数(即log2N).例如,如果你要一个页order 为0,如果你请求8页就是3(2^3=8).
返回值:
成功返回一连续系列页内存区域的首地址;失败返回NULL.
2-2:常用申请页API:
更为常用的申请页API如下:
get_zeroed_page(unsigned int flags);
分配一个页面并清零.
__get_free_page(gfp_mask);
类似于 get_zeroed_page, 但是没有清零该页.
__get_free_pages(unsigned int flags, unsigned int order);
分配并返回一个指向一个内存区第一个字节的指针, 内存区可能是几个(物理上连续)页长但是没有清零.
其中,flags、gfp_mask和上述alloc_pages的参数gfp_mask意义一样;ordef和上述alloc_pages()的参数order意义一样.
2-3.释放页:
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
第一个函数释放一个页,第二个参数释放若干个页,需要和相应的页申请函数配套使用.参数addr为申请页的时候的内存首地址.order和上述alloc_pages()参数order一样.
3.实例:
下面零星代码来自LDD3.
基于面向页分配内存:
/* Here's the allocation of a single quantum */
if (!dptr->data[s_pos])
{
dptr->data[s_pos] =
(void *)__get_free_pages(GFP_KERNEL, dptr->order);
if (!dptr->data[s_pos])
goto nomem;
memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
}
基于面向页释放内存:
/* This code frees a whole quantum-set */
for (i = 0; i < qset; i++)
if (dptr->data[i])
free_pages((unsigned long)(dptr->data[i]), dptr->order);