bass one 路亚竿:内存分配kmalloc,内存池mempool,页分配get_free_page,虚拟分配vmalloc

来源:百度文库 编辑:偶看新闻 时间:2024/04/30 07:41:17

(1)内核内存分配函数kmalloc 是一个功能强大且高速(除非被阻塞)的工具,所分配到的内存在物理内存中连续且保持原有的数据(不清零)。原型:#include void *kmalloc(size_t size, int flags); 
      size参数说明:内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc和典型的用户空间malloc在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统RAM。Linux处理内存分配的方法:创建一系列内存对象集合,每个集合内的内存块大小是固定。处理分配请求时,就直接在包含有足够大内存块的集合中传递一个整块给请求者。必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc能够处理的最小内存块是32或64字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于128 KB的内存。若只需多于几个KB的内存块,最好使用其他方法。
      flags参数说明:内存分配最终总是调用__get_free_pages 来进行实际的分配,这就是 GFP_ 前缀的由来。主要的标志包括:
GFP_KERNEL:最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时,内核会采取适当的动作来获取空闲页。
GFP_ATOMIC:内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用 GFP_ATOMIC,这样kmalloc 甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。
GFP_USER:用来为用户空间分配内存页,可能睡眠。
__GFP_DMA:要求分配可用于DMA的内存。
__GFP_HIGHMEM:分配的内存可以位于高端内存。

 

      后备高速缓存:内核为驱动程序常常需要反复分配许多相同大小内存块的情况,增加了一些特殊的内存池,称为后备高速缓存(lookaside cache)。设备驱动程序通常不会涉及后备高速缓存,但是也有例外:在 Linux 2.6 中USB 和SCSI 驱动。Linux 内核的高速缓存管理器有时称为“slab 分配器”。slab 分配器实现的高速缓存具有 kmem_cache_t 类型。对应的函数有:
      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));/*创建一个可以容纳任意数目内存区域的、大小都相同的高速缓存对象*/
      通过调用kmem_cache_alloc 从已创建的后备高速缓存中分配对象:
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
/*cache 参数是刚创建的缓存,flags 是和kmalloc 的相同*/
      使用kmem_cache_free释放一个对象:

void kmem_cache_free(kmem_cache_t *cache, const void *obj);
      当驱动用完这个后备高速缓存(通常在当模块被卸载时),释放缓存:
int kmem_cache_destroy(kmem_cache_t *cache);
/*只在从这个缓存中分配的所有的对象都已返时才成功。因此,应检查 kmem_cache_destroy 的返回值:失败指示模块存在内存泄漏*/

 

(2)内存池:为了确保在内存分配不允许失败情况下成功分配内存,内核提供了称为内存池( "mempool" )的抽象,它其实是某种后备高速缓存。它为了紧急情况下的使用。所以使用时必须注意:mempool会分配一些内存块,使其空闲而不真正使用,所以容易消耗大量内存。而且不要使用mempool处理可失败的分配。应避免在驱动代码中使用mempool。对应的函数有:
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);
/*给 mempool_create 最后的参数 *pool_data 被传递给 alloc_fn 和 free_fn */
      创建内存池后,分配和释放对象:
void *mempool_alloc(mempool_t *pool, int gfp_mask);
void mempool_free(void *element, mempool_t *pool);
     若不再需要内存池,则返回给系统:
void mempool_destroy(mempool_t *pool);
/*在销毁 mempool 之前,必须返回所有分配的对象,否则会产生 oops*/
 

 (3)get_free_page:如果一个模块需要分配大块的内存,最好使用面向页的分配技术。

__get_free_page(unsigned int flags); /*返回一个指向新页的指针, 未清零该页*/
get_zeroed_page(unsigned int flags); /*类似于__get_free_page,但用零填充该页*/
__get_free_pages(unsigned int flags, unsigned int order);
/*分配若干(物理连续的)页面并返回指向该内存区域的第一个字节的指针,该内存区域未清零*/
      当程序不需要页面时,它可用下列函数之一来释放它们。
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
它与kmalloc的区别:从用户的角度,可感觉到的区别主要是速度提高和更好的内存利用率(因为没有内部的内存碎片)。但主要优势实际不是速度,而是更有效的内存利用。__get_free_page 函数的最大优势是获得的页完全属于调用者,且理论上可以适当的设置页表将其合并成一个线性的区域。

 

 (4)vmalloc是一个基本的Linux内存分配机制,它在虚拟内存空间分配一块连续的内存区,尽管这些页在物理内存中不连续 (使用一个单独的alloc_page 调用来获得每个页) ,但内核认为它们地址是连续的。应当注意的是:vmalloc在大部分情况下不推荐使用。因为在某些体系上留给vmalloc 的地址空间相对小,且效率不高。函数原型如下:
#include
void *vmalloc(unsigned long size);
void vfree(void * addr);
      vmalloc使用的地址范围完全是虚拟的,且每次分配都要通过适当地设置页表来建立(虚拟)内存区域。vmalloc可获得的地址在从VMALLOC_START到 VAMLLOC_END 的范围中。vmalloc分配的地址只在处理器有MMU之上才有意义。当驱动需要真正的物理地址时,就不能使用vmalloc。调用vmalloc的正确场合是分配一个大的、只存在于软件中的、用于缓存的内存区域时。注意:vamlloc比__get_free_pages要更多开销,因为它必须即获取内存又建立页表。因此,调用vmalloc 来分配仅仅一页是不值得的。vmalloc的一个小的缺点在于它无法在原子上下文中使用。因为它内部使用kmalloc(GFP_KERNEL) 来获取页表的存储空间,因此可能休眠。vmalloc是面向页的 (它们会修改页表) ,重定位的或分配的空间都会被上调到最近的页边界。