Linux 驱动开发中常用的内存分配方法浅析

1                         Device Driver中常用的Physcial Memory Allocating的方法

device driver的设计中我们常常需要dynamically allocate memory。这里我们介绍一些最为常用的allocating的方法,并讨论其不同之处,重点明确它们各自不同的应用场合。

Linux Kernel设计之初是基于X86来做的,当时在X86下:

1.       hardware physical memory通常有多块,每块甚至access time都不一样,physical address也不连续(non-contiguous

2.       DMAaccess到的memory是有一定限制的,大多只能access16Mbytes

3.       我们的Linux Kernel通常将physical memory采用一对一mapping到第4G Bytesvirtual memory上,此段virtual memory只有1 GB,这样如果我们的phyiscal memory超过了1GB怎么办?现在我们的机器都是2GB以上的了。在X86中超过896Mbytes都叫High MemoryHigh Memory没有做一对一的mapping

所以Linux Kernel的设计者采用了如下几乎很一致的Physical memory hierarchy

1.       Physical Memory Node:不同的access time non-contiguous physical address range,这就是所谓的Non-Uniform Memory Access (NUMA)。是否支持NUMA要看我们是否configCONFIG_NUMA

2.       Physical Memory Zones in X86 every node will be divided into three zones: ZONE_DMA/ZONE_NORMAL/ZONE_HIGHMEM

上述两个概念对应的是Key structurestruct node and struct zone,这里不讨论,只是给大家起个抛砖引玉的作用。LDDULK两本书都是基于X86来描述的,这里简述一下这些概念以便大家更好的理解这两本书。

我们需要重点说明并即记住的是:

1.       ARM Linux中(无论是U3还是No1)我们没有定义CONFIG_NUMA,即我们只支持一个node

2.       ARM Linux 只有一个zoneZONE_NORMAL

这样就简单很多了。

但是对于virtual memory address 而言,还是有high memory的概念的。我们可以看作为virtual high memory。关于这个详见后面的10.3.1

 

1.1                   buddy system中直接分配2^n0 <= n <= 10)个page frame

通常我们说page frame是指physical memory中的一个page,在ARM Linux中为4K bytes,整块的SDRAM(或DDR SDRAMSRAM等)被连续分成了多个page frame,所有的的free page frame都被buddy system 记录在11个从010list中,每个list中的element都是对应为连续2^n n为该listNo)个page frames,且其起始地址也是按照2^n * 4 Kbytes 对齐的。

当我们要allocate一块physical memory时,都是从这些list中找到一个对应的分配出而已,这是Linux kernel中所有其它分配physical memory的基础包括后面我们要介绍的kmalloc就是先通过此方法从buddy system中分配一块,然后再进行自己的memory managerment

 

1.1.1              alloc_pages__free_pages

static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)

参数很简单,第一个是设定如何allocate这些pagesflag,第二个是指定我们要分配2^orderpages

这里我们重点说明一些常用的、对device driver设计至关重要的几个flags

1.       GFP_ATOMIC,其有两层含义:一是不会进入sleep,如果没有memory,那么直接返回;二是指定为高优先级的allocating,即会从Linux Kernel reserved memory pages pool中分配,这个pool是为了一些紧急情况而预留的。比如系统restartkill一些proces等。

2.       GFP_KERNELallocate pages for kernel space。当memory不够时可能会进入sleep

3.       GFP_USERallocate pages for user space。目前这两个其实没有区别。

 

通过alloc_pages分配的pages,之后我们可以再通过kmap mappingKernel space这里我们可以回顾一下之前介绍的mappinguser space的方法,并做一个简单比较。

我们可以简单看看kmap是如何实现的:对应我们的platform,其实现在include/linux/highmem.h

Kmapàpage_addressàlowmem_page_address

static __always_inline void *lowmem_page_address(struct page *page)

{

   return __va(page_to_pfn(page) << PAGE_SHIFT);

}

其中page_to_pfn定义在include/asm-generic/memory_module.h

#define page_to_pfn __page_to_pfn

 

讨论:

1.       我们前面讨论过,在interrupt handlersoftirq中我们不能使用任何导致sleep的函数,如果需要dynamically allocating memory需要加上GFP_ATOMIC。但是在实际中我们一般都不会这样做,通常都会在init中先分配好。一是因为相对耗时,二是因为可能会去reserved pool中分配,这样会降低系统的整体可靠性。

2.       这里介绍的flag同样适用于后面介绍的所有allocating methods

3.       我们能够分配到8M连续的physical memory么?如果我们要分配1Mphysical连续的memory,确定一定可以分配的到么?

1.1.2              __get_free_pagesfree_pages

fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

我们在device driver设计中通常不用alloc_pages,而用__get_free_pages,其实__get_free_pages等价于alloc_pages kmap所以这个一般使我们建议使用的方法

其它类似的如:get_zeroed_page分配一个page frame,并且清零; __get_free_page分配一个page frame。释放(free)的方法都是一致的:free_pages

 

注:这里的命名有点乱,__free_pages释放的是alloc_pages分配的pages,其输入参数是struct page*;而free_pages释放的是__get_free_pages分配的pages,其输入参数是这些pagevirtual address

1.2                   Allocate32 128K bytes的连续physical memory的方法

假定我们只要分配几个或几十个bytesmemory,如果Linux Kernel仅仅提供上述所说的分配一个或多个page frame的方法,那么我们至少分配一个page frame 4 Kbytes出去,这样就太浪费了,极端的情况会消耗掉大部分的memory

1.2.1              Kmallockfree

Kmalloc是建立在buddy system基础上,首先它从buddy system中分配一些连续的pages,然后再将这些pages分成多个kmalloc所要分配的block,这些blocksize3264128一直到128K byste

static inline void *kmalloc(size_t size, gfp_t flags)其中flag10.1.1中所描述的。Size通常是3264,一直到128K bytes,如果不是,它找比其大的最小的一个。比如说33,则实际分配的是64

1.3                   Allocate多个page frame但是不要求这些page frame连续的的方法

如果我们要求分配一个块较大的连续physical memory,如1M,其实对整个系统的要求是比较高的,因为系统可能存在大量的free page,但是未必有一块连续的超过1Mmemory。(为什么呢? 大家不防思考一下),而且大多数的情况,我们并不需要分配一块physical上连续的memory,此时Linux Kernel同样提供了如下的分配方法。

讨论:

4.       什么时候必须要分配physical 连续的memory呢?能否举出一些例子来?可以以LCD frame buffer来描述之。这些主要都是硬件上的一些要求。

1.3.1              Vmallocvfree

void *vmalloc(unsigned long size)

最后实际分配的大小当size4K的整数倍时,则实际就是分配size,反之则分配(size / 4K + 1) * Kvmallockmalloc__get_free_pages相比还有一个大的区别就是其返回的virtual address并不是physical memory 一对一mapping到的virtual address上,所以我们不能用__pa(xxxx)来得到其physical address。而对于kmalloc__get_free_pages都是可以的。

讨论:

5.       __va__pa仅仅对一对一mappingphysical addressvirtual address有意义。对由vmalloc返回的结果或io address都没有意义。

 

Vmalloc返回的地址是在VMALLOC_STARTVMALLOC_END之间的一个地址,这个区间的virtual address位于所谓的virtual high memory中。

Virtual high memory的起始地址由下决定:

bootmem_initàhigh_memory = __va(memend_pfn << PAGE_SHIFT);

 

VMALLOC_START的定义如下:

include/asm-arm/pgtable.h

/*

 * Just any arbitrary offset to the start of the vmalloc VM area: the

 * current 8MB value just means that there will be a 8MB "hole" after the

 * physical memory until the kernel virtual memory starts.  That means that

 * any out-of-bounds memory accesses will hopefully be caught.

 * The vmalloc() routines leaves a hole of 4kB between each vmalloced

 * area for the same reason. ;)

 *

 * Note that platforms may override VMALLOC_START, but they must provide

 * VMALLOC_END.  VMALLOC_END defines the (exclusive) limit of this space,

 * which may not overlap IO space.

 */

#ifndef VMALLOC_START

#define VMALLOC_OFFSET              (8*1024*1024)

#define VMALLOC_START         (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))

#endif

 

而:include/asm-arm/arch-ad6900/vmalloc.h

#define VMALLOC_END       (0xe8000000)

这个是我们移植的时候需要设定的。

 

介绍vm_struct,并和vm_area_struct比较。

讨论:

6.       vmalloc分配physical memory不能保证是连续的,但是用它分配的physical memory也是有机会连续的,之前有一个很资深的工程就用vmalloc分配memory用做frame buffer,并用DMA access,并且测试也是通过。测试通过并不代表没有bug,只是它碰巧在那种情况下,vmalloc分配的physical memory恰巧连续而已。而且他也不断找资料来证明他是对,确实vmalloc返回的地址也是连续,但是只是virtual address连续,他混淆了virtual addressphysical address了。其实他不用去找什么资料,直接看Linux Kernelsource code比什么资料都清楚。但是很多工程师宁愿去找书、上internet搜索,而对直接分析code望而却步。

 

1.4                   分配超过4Mbytes连续physical memory的方法

如果我们真的需要超过4M的连续physical memory的话(比如说8M),我的建议是不如直接将这段memory预留出来,比如总共有64M SDRAM,我们可以告知Kernel只有56M,余下的8M我们就当作IO Memory,在需要使用的时候通过ioremap出来就可以了。

讨论:

7.       还记得我们如何告知Linux Kernel physical memorystart addresssize么?U3中其中就有4M要给DSP使用,我们就是这样做的。

1.4.1              alloc_bootmem

Linux Kernel也提供了分配大块memory的方法,如alloc_bootmem,但它只能在Linux Kernel初始化的时候分配,并且一旦分配就不能被buddy system管理了。我们一般也不建议使用,这里我们就不去讨论它了。

 

1.5                   使用memory pool

最后我们简单介绍一下memory pool的概念,memory pool其实很简单,user可以通过mempool_create创建一个memory pool,其function prototype为:

mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,

                           mempool_free_t *free_fn, void *pool_data)

我们可以指定该memory pool中最少的user element数目:min_nruser elementallcatefree的的functionalloc_fn, free_fn,最后还可以带入一个private data。创建之时同时由alloc_fn先行allocate min_nrelement

之后user可以通过mempool_alloc首先仍然由alloc_fn进行分配,当分配不成功时,它才会从该memory poolallocate一个elementmempool_free则相反,先判断该memory poolelement是否少于min_nr,是的话就添加到该memory pool中,否则就由free_fn进行真正的free

Memory pool我们并不常用,它是2.6的新特性,但是有的时候也可以提供方便。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值