分配内存相关函数 >>Linux设备驱动程序

据离职开始自学转行到现在已经有3个月的时间,理解了为啥只有少部分愿意去理解内核,不是没有原因的;
承受着多种压力和快节奏的生活,让人们越来越趋向于即时反馈,这样其实并不是很好;
有段可以安安静静自我提升的时间简直就是奢望,比起他们来说我已经很幸运了;
看了加布里尔·穆奇诺的《当幸福来敲门》,才知道我这点困难算什么?唯有继续才能对得起这珍贵的时间;

[0x100]内容概述

  1. 连续的直接映射内存块分配
  2. 连续的直接映射内存页分配
  3. 连续的完全虚拟内存页分配
  4. 引导时分配物理内存页

[0x110] 常见的内存区域

  • 内存区域类型 : DMA区、常规内存区、高端内存区,其中高端内存的搜索的范围最大;
  • 默认标识位设置 :默认搜索 常规内存区、DMA内存区;
  • DMA标识位置位 :只搜索DMA内存,通常为RAM的前16M,可用于设备物理内存访问;
  • HIGHMEM置位 :搜索所有的三种区域来获取地址;

[0x200] 内核分配内存函数

[0x210] 连续分配直接映射物理内存

#include <linux/slab.h>
/*implement kernel-dir/mm/slab.c*/
static __always_inline void *kmalloc(size_t size, gfp_t flags);

功能 : 从低端内存高速分配,连续、不清空数据、直接映射物理内存;
args 1 : 需要分配的连续物理内存大小,字节为单位;
args 2 : 用于控制内存分配的操作方式,内存属性标识
retval : 分配成功的物理内存起始地址,如分配失败 NULL

[0x211] 常见的内存标志

#include <linux/gfp.h> 
//分配标识组合属性
#define __GFP_IO        ((__force gfp_t)___GFP_IO)      /* 允许启动 物理 IO */
#define __GFP_FS        ((__force gfp_t)___GFP_FS)      /* 允许调用低等级的文件系统*/
#define __GFP_WAIT      ((__force gfp_t)___GFP_WAIT)    /* 允许等待或者重新调度 */
#define __GFP_HIGH      ((__force gfp_t)___GFP_HIGH)    /* 允许启用紧急池 消耗最后一个空页面*/
//分配标识
#define GFP_NOIO        (__GFP_WAIT)                        /*禁止启动物理IO初始化*/
#define GFP_NOFS        (__GFP_WAIT | __GFP_IO)             /*禁止调用低级文件系统,可休眠,可初始化物理IO*/  
#define GFP_KERNEL      (__GFP_WAIT | __GFP_IO | __GFP_FS)  /*GFP_NOFS | __GFP_FS 可休眠 的调用低级文件系统*/
#define GFP_ATOMIC      (__GFP_HIGH)                        /*原子分配内存,允许访问最后一个可用内存页*/

[0x212] 分配内存规则

  1. 在内核中分配内存是基于对象块为单位,所以不像虚拟内存可以任意分割大小,最大有可能所需大小的两倍;
  2. 最小的内存大小是32k或者64k,kmalloc单次最大分配不能超过128k;

[0x220] 后备高速缓存管理–struct kmem_cache

[0x221] 常见 SLAB flags

  • 功能 :用于指导如何分配高速缓存,或者内存分配debug;
#include <linux/slab.h>
#define SLAB_HWCACHE_ALIGN      0x00002000UL    /* 基于高速缓存行对齐分配的内存对象,基于空间换取效率 */
#define SLAB_CACHE_DMA          0x00004000UL    /* 使用DMA区域内存分配 */
#define SLAB_PANIC              0x00040000UL    /* 分配失败发出内核恐慌提示 */

[0x222] 分配高速缓存对象

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
struct kmem_cache *
kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))

功能 : 创建高速缓存对象;
args 1 : 在/proc/slabinfo 文件中标注当前创建的高速缓存的名称;
args 2 : 在当前缓存中分配不同内存对象区域的大小,均来自于这个值;
args 3 : 内存对象对齐方式;
args 4 : SLAB flags;
args 5 :指定内存对象的构建方式;
retval : 分配成功返回指向缓存的结构体指针,如分配失败 NULL;

[0x223] 内存对象的分配与释放

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
/*从分配的高速缓存对象中获取内存对象,通常直接使用 kmalloc 来调用这个函数,不直接使用*/
void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
/*返还内存对象到高速缓存中,通常直接使用 kfree 来调用这个函数,不直接使用*/
void kmem_cache_free(struct kmem_cache *, void *);

[0x224] 释放高速缓存对象

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
/*移除一个缓存对象,调用前必须确保该缓存对象中的内存对象都已经返回到缓存对象中*/
void kmem_cache_destroy(struct kmem_cache *cachep)

[0x230] 分配直接映射物理内存页–struct page

  1. 高速、连续分配直接映射物理内存页;
  2. 以页为基本分配内存的颗粒细度,节省内存空间;
  3. 可以自主调整页表来合并多个内存页,获得更大的线性空间;

[0x231] 获取内存页

#include <linux/gfp.h> 
/*implement kernel-dir/mm/page_alloc.c*/
/*分配单页内存并填0*/
unsigned long get_zeroed_page(gfp_t gfp_mask)
{
  return __get_free_pages(gfp_mask | __GFP_ZERO, 0);
}
/*分配单页内存不填0*/
#define __get_free_page(gfp_mask) __get_free_pages((gfp_mask), 0)

[0x232] 获取多个内存页

#include <linux/gfp.h> 
/*implement kernel-dir/mm/page_alloc.c*/
/*分配多页内存*/
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
        struct page *page;
        VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);

        page = alloc_pages(gfp_mask, order);
        if (!page)
                return 0;
        return (unsigned long) page_address(page);
}

功能 : 连续分配2次幂数量多个内存页对象,分配的页数可以在/proc/buddyinfo;
args 1 : slab flags
args 2 : order 标识 以2为底的对数,所以 0 = 1页,2=4页;
retval : 页起始位置,失败返回0;

[0x233] 释放内存页

/*销毁一个内存页,是个调用多个内存页的宏*/
#define free_page(addr) free_pages((addr), 0)
/*销毁多个内存页*/
void free_pages(unsigned long addr, unsigned int order)
{
        if (addr != 0) {
                VM_BUG_ON(!virt_addr_valid((void *)addr));
                __free_pages(virt_to_page((void *)addr), order);
        }
}

[0x234]页分配器函数接口

#include <linux/mm_types.h>
#include <linux/gfp.h>
  
/*对外分配内存页操作的宏接口*/
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
#define alloc_pages(gfp_mask, order) alloc_pages_node(numa_node_id(), gfp_mask, order)
/*分配内存页核心函数 返回页结构*/
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,unsigned int order)
{               
        /* Unknown node is current node */
        if (nid < 0)
                nid = numa_node_id();

        return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
}
/*销毁内存页*/
#define __free_page(page) __free_pages((page), 0)
void __free_pages(struct page *page, unsigned int order);
void free_hot_cold_page(struct page *page, int cold);
void free_hot_cold_page_list(struct list_head *list, int cold);

[0x240] 分配完全虚拟内存页–vmalloc

  1. 分配连续的虚拟内存页,相应物理内存页不连续;
  2. vmalloc 的开销大于任何直接映射物理空间的分配函数,因为需要建立页表来映射虚拟内存区域;
  3. 分配的内存只在处理器MMU控制范围内有效;

[0x241]vmalloc 虚拟内存页地址范围

#include <asm/pgtable.h>
#define VMALLOC_OFFSET          (8*1024*1024)
#define VMALLOC_START           (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_END     0xff000000UL

[0x242]vmalloc 函数接口

/*基于虚拟内存页为单位 分配连续的虚拟内存*/
void *vmalloc(unsigned long size);
/*基于虚拟内存页为单位 分配连续的虚拟内存,清空分配页上数据填0*/
void *vzalloc(unsigned long size);
/*注销虚拟存储页*/
void vfree(const void *addr);

[0x250] 引导时分配更大缓存

1.只链接入内核的驱动程序;
2. 系统启动前,获取更大的连续直接映射物理内存的方式;

#define alloc_bootmem(x) __alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
#define alloc_bootmem_pages(x) __alloc_bootmem(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
#define alloc_bootmem_low(x) __alloc_bootmem_low(x, SMP_CACHE_BYTES, 0)
#define alloc_bootmem_low_pages(x) __alloc_bootmem_low(x, PAGE_SIZE, 0)

void * __init __alloc_bootmem(unsigned long size, unsigned long align,unsigned long goal)
{
        unsigned long limit = 0;
        return ___alloc_bootmem(size, align, goal, limit);
}
void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,unsigned long goal)
{
        return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
}

猜你喜欢

转载自blog.csdn.net/god1992/article/details/85272150
今日推荐