[LDD3速记]_内存分配

来源:互联网 发布:网络禁歌120首试听 编辑:程序博客网 时间:2024/05/13 06:15

一、kmalloc函数

原型:

#include <linux/slab.h>void *kmalloc(size_t size, int flags);

最终总是调用get_free_pages(GFP)实现


1. flags参数

        常用的标志:

        GFP_KERNEL:运行于进程上下文,可休眠以等待一个页面(休眠时内核把缓冲区的内容刷写到硬盘,或者从一用户进程换出内存,以获取一个内存页面)

        GFP_ATOMIC:原子性分配,可在进程上下文之外被调用(如中断处理进程、tasklet以及内核定时器)

        还有一些标志控制如何进行分配,可以上面的“或”起来使用:

        __GFP_DMA:分配DMA区段的内存

        __GFP_HIGHMEM:可位于高端内存(但是kmalloc不能分配高端内存)

        Linux内核把内存分为三个区段:DMA内存、常规内存、高端内存


2. size参数

        内核只能分配一些预定义的、固定大小的字节数组

        kmalloc能处理的最小的内存块是32或64,最大不该超过128KB(为了具有完整的移植性)


二、后备高速缓存

        驱动程序经常反复分配同一大小的内存块,为这些块增加某些特殊的内存池,这种形式的内存池称为后备高速缓存(lookasidecache)

        内核的高速缓存管理器有时称为“slab分配器”,它实现的高速缓存具有kmem_cache_t类型,通过kmem_cache_create创建

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决定,name保管一些信息以便追踪(可直接用字符串),offset为第一个对象的偏移量

        flags标志:

        SLAB_NO_REAP:保护高速缓存不被减少(系统寻找内存时)

        SLAB_HWCACHE_ALIGN:要求所有数据对象跟高速缓存行(cache line)对齐

        SLAB_CACHE_DMA:从DMA内存区段分配

        constructor、destructor:可选参数(不能只有destructor而没有constructor),前者用于初始化新分配的对象,后者用于释放对象

void *kmem_cache_alloc(kmem_cache_t *cache, int flags);/* 创建高速缓存对象后,可调用此函数从中分配内存对象,其中flags与kmalloc的相同 */void kmem_cache_free(kmem_cache_t *cache, const void *obj);/* 释放内存对象 */int kmem_cache_destroy(kmem_cache_t *cache);/* 释放高速缓存对象,需内存对象均归还才能成功,若失败->内存泄漏 */

        可从/proc/slabinfo获得高速缓存的使用情况


三、get_free_page

        分配大块内存,面向页,不产生内存碎片

get_zeroed_page(unsigned int flags);/* 返回新页面的指针,并清零 */__get_free_page(unsigned int flags);/* 返回新页面的指针,不清零 */__get_free_pages(unsigned int flags, unsigned int order);/* 分配若干页(物理连续),返回第一个字节的指针,不清零 */

        其中flags与kmalloc的一样,order是页面数的以2为底的对数(log2 N),可以使用函数get_order()转换

void free_page(unsigned long addr);void free_pages(unsigned long addr, unsigned long order);/* 释放页面,第一个函数是一个宏,调用第二个函数 */

        alloc_pages接口:

struct page *alloc_pages_node(int nid, unsigned int flags,unsigned int order);/* Linux页分配器核心代码 */struct page *alloc_pages(unsigned int flags, unsigned int order);struct page *alloc_page(unsigned int flags);/* 两个宏 */void __free_page(struct page *page);void __free_pages(struct page *page, unsigned int order);void free_hot_page(struct page *page);//高速缓存中的void free_cold_page(struct page *page);//高速缓存中的/* 释放 */

四、vmalloc函数

1 .特性

        虚拟地址连续,但物理地址可能不连续

        分配的内存使用起来效率不高,不鼓励使用

        与kmalloc的差异:kmalloc返回的虚拟地址与物理内存是一一对应的(可能有PAGE_OFFSET偏移),而vmalloc的虚拟地址需建立页表进行映射,故当需要真正的物理地址时不应使用vmalloc

        不能用于原子上下文,内部调用kmalloc(GFP_KERNEL),可能休眠


2. 接口

#include <linux/vmalloc.h>void *vmalloc(unsigned long size);//分配void vfree(void * addr);//释放/* 使用示例:create_module */void *ioremap(unsigned long offset, unsigned long size);void iounmap(void * addr);//释放/* 与vmalloc一样会建立页表,但不实际分配内存更多用于映射(物理的)PCI缓冲区地址到(虚拟的)内核空间返回的地址需用readb或其他I/O函数来访问,不应直接访问*/

五、获取大的、连续的缓冲区

        方法:在引导时获得专用缓冲区

#include <linux/bootmem.h>void *alloc_bootmem(unsigned long size);void *alloc_bootmem_low(unsigned long size);void *alloc_bootmem_pages(unsigned long size);void *alloc_bootmem_low_pages(unsigned long size);/* _low版本防止分配高端内存(高端内存不能用于DMA操作) */void free_bootmem(unsigned long addr, unsigned long size);/* 注意:释放的部分页面不会返回给系统 */