大块物理地址连续的内存分配
@
Jun1,2009
目录目录
目录
1前言2
2kernel内存的分配2
2.1kmalloc......................................2
2.2vmalloc......................................2
2.3alloc_page类函数.................................2
3实际解决方案3
3.1预留内存(mem=).................................3
3.2bigphysarea....................................5
3.2.1bigphysarea简介.............................5
3.2.2实现原理.................................5
1
2KERNEL内存的分配
1前言
工作中,我们经常能够遇到一些对内存有特殊要求的硬件,有些是其本身的设计
要求所至,而另一些则是从系统性能考虑(如不能支持Scatter/gatherDMA),两种情况
下都必须为其driver分配大块的物理地址连续内存块。更槽糕的是,系统连续运行时间
越长,大块物理地址连续的内存块分配到的可能性就越低。大多数情况下(内存泄露除
外),你会发现系统的剩余内存(free或cat/proc/meminfo)足够大却依然无法分配到,
很显然,内存碎片(memoryfragmentation)出现了。在uClinux系统中,由于没有VM的
存在,问题更加突出。当你播放一个H.264或者WMVHD(1920x1080p)的视频时,甚
至播放一个WMAPro,系统都有可能“Outofmemory",根本原因还是系统此时无法为
videodecode或audiodecoder分配一个大块的物理地址连续内存。
2kernel内存的分配
那么如何才能保证一定能够分配到大块物理地址连续的内存呢?在看下面的解决方
案之前,我们有必要了解一下Linuxkernel中常用的几种内存分配方式,从能分配的上
限和分到的可能性来看是否能够满足需求。
2.1kmalloc
kmalloc是建立在slab分配器之上,它并不直接从buddysystem中获得空闲页面,
而是从slab页面分配器获得需要的内存。kmalloc主要用于分配以字节大小为单位
的小内存区域(32131072),所以kmalloc能够分配的内存块的大小有一个上限。slab
的实现限制了最大分配的大小为128k,即131072bytes,理论上可以更改slab.c中的
cache_sizes数组中的最大值使得kmalloc可以获得更大的内存数,但是有必要这么做
么?因为128k对于slab而言是一个很合理的经验值,而且获取较大内存的方法有很
多,我们不需要为了解决一个问题而引入一系列问题。
kmalloc分配的内存在线形地址和物理地址上都是连续的,但是它不能分配到高端
内存区域(Highmemory)内的内存,高端内存区域内的内存必须由专门的方式(kmap)来
获得。而且系统长时间运行后,也并不能保证能够分配到大块内存。
2.2vmalloc
vmalloc与kmalloc不同的是,vmalloc分配的内存只是在线形地址上是连续的,它
不保证分配的内存在物理上也连续。vmalloc的主要目的是用于非连续物理内存分配。
采用vmalloc的主要原因在于:由于buddysystem会产生外碎片(externalfragmentation)
,系统长时间运行后我们很难找到大块的物理连续的内存块,因此Linux采用vmalloc
来解决这个问题。由于vmalloc会涉及到页表的更改,并且以后访问用vmalloc申请的
内存,很有可能导致TLBmiss,效率肯定会损失一些。对于一定要使用大块物理连续
地址内存块的硬件来说,vmalloc显然不行。uClinux中,由于没有物理地址和虚拟地
址的区别,vmalloc就等同于kmalloc.
2.3alloc_page类函数
此类函数主要通过buddysystem进行分配,是以页为单位分配大块连续内存的.但
是一次请求能分配的最大物理页数由变量MAX_ORDER决定.
1staticinlinestructpage
*
2alloc_pages(unsignedintgfp_mask,unsignedintorder)
3{
2
3实际解决方案
4if(unlikely(order>=MAX_ORDER))
5returnNULL;
6
7returnalloc_pages_current(gfp_mask,order);
8}
Linux中(linux-2.6.11/include/linux/mmzone.h)
1/
*
Freememorymanagement-zonedbuddyallocator.
*
/
2#ifndefCONFIG_FORCE_MAX_ZONEORDER
3#defineMAX_ORDER11
4#el
5#defineMAX_ORDERCONFIG_FORCE_MAX_ZONEORDER
6#endif
最大为2048个page,8M的内存。
而在uClinux中(linux-2.4.22-uc0/include/linux/mmzone.h)
1#ifdefCONFIG_FORCE_MAX_ZONEORDER
2#defineMAX_ORDERCONFIG_FORCE_MAX_ZONEORDER
3#elifdefined(CONFIG_NO_MMU_LARGE_ALLOCS)
4#defineMAX_ORDER13
5#el
6#defineMAX_ORDER10
7#endif
即便在定义了CONFIG_NO_MMU_LARGE_ALLOCS的情况下,最多也只能分配32M.
由上可见,alloc_page或kmalloc都有分配上限,而且也无法确保系统长时间运行
后依然能够分配到大块物理连续的内存块。系统由于长时间运行后,大块连续物理内存
被系统中持续不断的小块内存分配请求打散,找成内存碎片。一个可行的解决方法就
是,把小内存请求和大块内存请求隔离开,即:分出一大块内存独立管理,用来满足所
有的大块内存请求;剩余内存由系统直接管理,满足小内存请求和系统任务分配需要。
问题是:如何从内存中分配大块的内存呢(从128M中分出80M)?
3实际解决方案
3.1预留内存(mem=)
系统启动时,可以通过mem=(kernelparameter,可在commandline指定mem=XXX
)或类似机制直接指定由系统直接管理的内存数,剩余内存由driver独立管理。用户
进程要想访问预留的存储器,首先必须用打开驱动程序提供的设备文件,然后利用
mmap()映射内存。而驱动程序一侧则必须实现ioremap.以我们的系统为例,系统配置
为:
•OS:uClinux2.4.22-uc0,
•CPU:PT110(PicoTurb生产,ARM7TDMI兼容),
•DRAM:128M
•Flash:16M
3
3.1预留内存(mem=)3实际解决方案
当config中的DRAM_SIZE设为0x03000000时(等同于mem=48M),48MB将由内
核管理,OS下运行的各种任务和小内存分配请求共享此块内存。剩下的80M,OS无
法管理,由驱动程序独立管理。
kernelconfig设置:
DRAM_BASE=0x90080000
DRAM_SIZE=0x03000000
FLASH_MEM_BASE=0x46000000
FLASH_SIZE=0x00800000
CONFIG_16MB_FLASH=y
0x00020000
Hostinterface
0x00030000
0x00040000
0x00050000
DRMCRTL0
DRMCRTL1
DRMCRTL2
CPUBlock
0x00060000
0x40000000
configuration
0x44000000
0x45000000
0x46000000
MEMORY_BASE_HOST_SFLASH
MEMORY_BASE_HOST_PB0
MEMORY_BASE_HOST_PB1
MEMORY_BASE_HOST_PB2
CONFIG
0x47000000
Hostmemory
ROOTFS
ROOTFS2
Cert
Flag
Bootloader
0x46020000
0x46040000
0x46bc0000
0x46fc0000
0x46fe0000
PCI_BASE_CONFIG
PCI_BASE_IO
PCI_BASE_MEMORY
0x50000000
0x58000000
0x60000000
FLASH
0x90000000
CACHE_START
BOOTLOADER
CACHE_START
DRAM_BASE
CACHE_START
KERNEL
CACHE_START
MEM_END
CACHE_START
RESERVE_END
CACHE_START
MEMORY_BASE_DRAMCRL1
0x90060000
0x90080000
0x90090000
0x93080000
0x98080000
0xa0000000
0xb0000000
MEMORY_BASE_DRAMCRL2
NON-CACHE
CACHE
0xFFFFFFFF
0x80000000
CACHE_START
0x00000000
图1:系统内存映射(memorymap)
系统内存:MEM_END=DRAM_BASE+DRAM_SIZE=0x90080000+0x03000000
=0x93080000=0x13080000|0x80000000.
4
3.2bigphysarea3实际解决方案
预留内存:0x90080000+0x08000000=0x98080000.
RESERVE_END也可以由driver验证:
cat/proc/driver/xxxx/0/resources
#*
[MM0]
*
MemoryArea[0x13080000,83361792(79.5MB)]
***
奇怪的是:0x93080000却变成了0x13080000。其实从0-0x7FFFFFFF(2G空间)和0x80000000
-0xFFFFFFFF(2G空间)对应的是同一地址空间,区别只是前2G为Non-Cache地址,
后2G为Cache地址。
1#defineXXXX_DRAM_C2NC(x)((x)&0x7fffffff)
2#defineXXXX_DRAM_NC2C(x)((x)|0x80000000)
这种做法(mem=)有其明显的缺陷,试想,在一个拥有4GB的x86机器上,mem=3G
会为driver预留最后的1GB,不幸的是,当driver读写这1GB时,有可能直接写到PCI
configspace中去了,产生奇怪的错误也就不难理解了。从理论上来讲,用"mem="为
driver预留内存是一个错误的做法,它只是碰巧在我们的平台工作而已,而且也为其他
系统的移植埋下了定时炸弹.它并不能在内存非连续或有hotplug内存的系统上很好地工
作.
对于本例中的嵌入式应用,该方法还是可以接受的。那么什么是正解呢?这就是下
面要提到的bigphysarea.
3.2bigphysarea
3.2.1bigphysarea简介
bigphysareapatch是基于bootmem的分配大块(比kmalloc,alloc_page上限大)物理
地址连续的内存块的方法。预留的内存由bigphysarea独立管理,同时还提供了2个接口
给其它需要大块物理地址连续内存块的driver使用:
•分配内存
1caddr_tbigphysarea_alloc_pages(intcount,intalign,int
priority)
count为所分页数,align为地址对其系数:PAGE_SIZE*align,priority同kmalloc
中的参数意义一样,可以为GFP_ATOMIC(用于中断环境),GFP_KERNEL(usual
calls).返回值为所分区域的基地址。
•释放内存
1voidbigphysarea_free_pages(caddr_tba)
ba为bigphysarea_alloc_pages返回值
3.2.2实现原理
1static
2int__initbigphysarea_tup(char
*
str)
3{
4intpar;
5if(get_option(&str,&par)){
5
3.2bigphysarea3实际解决方案
6bigphysarea_pages=par;
7//Allocthememory
8bigphysarea=alloc_bootmem_low_pages(
bigphysarea_pages<
9if(!bigphysarea){
10printk(KERN_CRIT"bigphysarea:not
enoughmemoryfor%dpagesn",
bigphysarea_pages);
11return-ENOMEM;
12}
13
14//registertheresourceforit
15mem_=bigphysarea;
16mem_=mem_+(
bigphysarea_pages<
17request_resource(&iomem_resource,&
mem_resource);
18}
19return1;
20}
1__tup("bigphysarea=",bigphysarea_tup);
这里提到了m要解决的问题就是如何初始化buddy,而初始化
buddy很显然需要分配内存给buddy数据结构,那么由谁分配而且如何分配内存呢?这
就是bootmem的职责所在。简而言之:
•bootmem
就
是
系
统
启
动
时
内
核
中
使
用
的
一
种
较
简
单
的
内
存
分
配
策
略
,
其
主
要
作
用就是系统启动时,帮助buddy等内存分配系统完成初始化,同时用bitmap标记
buddy等数据结构使用的页面(不能释放的页面),然后把所有未使用的内存页(也
包括bootmembitmap等数据结构所使用的内存页)释放给buddy。
•bootmem只在buddy尚未接管内存管理时使用,在buddy等内存分配系统初始化
完成后将废弃不用。
•bootmem基本思想是用bitmap标记内存页,分配方法是从低往高找直到找到
一块或连续多块满足大小要求的空闲页面为止。(详见[5,Chapter5,BootMemory
allocator])
bigphysarea根据kernelcommadline(bigphysarea=page数)传入的预留的page数,调
用alloc_bootmem_low_pages分配预留的内存.
需要大块物理连续内存块的driver可以调用bigphysarea_alloc_pages从bigphysarea
driver分配内存,bigphysarea_alloc_pages实现如下:
1typedefstructrange_struct{
2structrange_struct
*
next;
3caddr_tba;/
*
baofallocated
block
*
/
4size_tsize;/
*
sizeinbytes
*
/
5}range_t;
6
7/
*
8*
0:nothinginitialized
9*
1:bigphysarea_pagesinitialized
10*
2:freelistinitialized
6
3.2bigphysarea3实际解决方案
11*
/
12staticintinit_level=0;
13staticintbigphysarea_pages=0;
14staticcaddr_tbigphysarea=0;
15staticrange_t
*
free_list=NULL;
16staticrange_t
*
ud_list=NULL;
17staticstructresourcemem_resource={"Bigphysarea",0,0,
IORESOURCE_MEM|IORESOURCE_BUSY};
18
19/
*
20*
Allocate`count'
arealignedto
21*
amultipleof`align'.`priority'hasthesamemeaningin
kmalloc,it
22*
isneededformanagementinformation.
23*
Thisfunctionmaynotbecalledfromaninterrupt!
24*
/
25caddr_tbigphysarea_alloc_pages(intcount,intalign,int
priority)
26{
27range_t
*
range,
**
range_ptr,
*
new_range,
*
align_range;
28caddr_taligned_ba;
29
30if(init_level<2)
31if(init2(priority))
32return0;
33new_range=NULL;
34align_range=NULL;
35
36if(align==0)
37align=PAGE_SIZE;
38el
39align=align
*
PAGE_SIZE;
40/
*
41*
Searchafreeblockwhichislargeenough,even
withalignment.
42*
/
43range_ptr=&free_list;
44while(
*
range_ptr!=NULL){
45range=
*
range_ptr;
46aligned_ba=
47(caddr_t)((((unsignedlong)range->ba+
align-1)/align)
*
align);
48if(aligned_ba+count
*
PAGE_SIZE<=
49range->ba+range->size)
50break;
51range_ptr=&range->next;
52}
53if(
*
range_ptr==NULL)
54return0;
55range=
*
range_ptr;
56/
*
57*
Whenwehavetoalign,thepagesneededfor
alignmentcan
58*
beputbacktothefreepool.
59*
Wecheckhereifweneedacondrangedata
structurelater
7
3.2bigphysarea3实际解决方案
60*
andallocateitnow,sothatwedon'thavetocheck
fora
61*
failedkmalloclater.
62*
/
63if(aligned_ba-range->ba+count
*
PAGE_SIZE<
range->size){
64new_range=kmalloc(sizeof(range_t),priority)
;
65if(new_range==NULL)
66returnNULL;
67}
68if(aligned_ba!=range->ba){
69align_range=kmalloc(sizeof(range_t),
priority);
70if(align_range==NULL){
71if(new_range!=NULL)
72kfree(new_range);
73returnNULL;
74}
75align_range->ba=range->ba;
76align_range->size=aligned_ba-range->ba
;
77range->ba=aligned_ba;
78range->size-=align_range->size;
79align_range->next=range;
80*
range_ptr=align_range;
81range_ptr=&align_range->next;
82}
83if(new_range!=NULL){
84/
*
85*
Rangeislargerthanneeded,createanew
listelementfor
86*
theudlistandshrinktheelementinthe
freelist.
87*
/
88new_range->ba=range->ba;
89new_range->size=count
*
PAGE_SIZE;
90range->ba=new_range->ba+new_range->
size;
91range->size=range->size-new_range->size;
92}el{
93/
*
94*
Rangefitsperfectly,removeitfromfree
list.
95*
/
96*
range_ptr=range->next;
97new_range=range;
98}
99/
*
100*
Inrtblockintoudlist
101*
/
102new_range->next=ud_list;
103ud_list=new_range;
104
105returnnew_range->ba;
106}
8
3.2bigphysarea3实际解决方案
Bigphysarea为已使用内存(ud)和空闲(free)内存各维护一个链表,分配时从free
链表中寻找一个符合对齐和大小要求的block,当然最初的free表只有一个block.
1static
2intinit2(intpriority)
3{
4if(init_level==1){
5free_list=kmalloc(sizeof(range_t),priority)
;
6if(free_list!=NULL){
7free_list->next=NULL;
8free_list->ba=bigphysarea;
9free_list->size=bigphysarea_pages
*
PAGE_SIZE;
10init_level=2;
11return0;
12}
13}
14return
15}
当调用bigphysarea_free_pages释放内存时,首先从ud链表中删除对应的项,然
后在free链表中查找合适的插入点并尽可能合并相邻项。
1/
*
2*
Freepagesallocatedwith`bigphysarea_alloc_pages'.`ba'
mustbean
3*
addressreturnedby`bigphysarea_alloc_pages'.
4*
Thisfunctionmynotbecalledfromaninterrupt!
5*
/
6voidbigphysarea_free_pages(caddr_tba)
7{
8range_t
*
prev,
*
next,
*
range,
**
range_ptr;
9
10/
*
11*
Searchtheblockintheudlist.
12*
/
13for(range_ptr=&ud_list;
14*
range_ptr!=NULL;
15range_ptr=&(
*
range_ptr)->next)
16if((
*
range_ptr)->ba==ba)
17break;
18if(
*
range_ptr==NULL){
19printk("bigphysarea_free_pages(0x%08x),not
allocated!n",
20(unsigned)ba);
21return;
22}
23range=
*
range_ptr;
24/
*
25*
Removerangefromtheudlist:
26*
/
27*
range_ptr=(
*
range_ptr)->next;
28/
*
29*
Thefree-listissortedbyaddress,arch
inrtionpoint
30*
andinrtblockinfreelist.
31*
/
9
参考文献参考文献
32for(range_ptr=&free_list,prev=NULL;
33*
range_ptr!=NULL;
34prev=
*
range_ptr,range_ptr=&(
*
range_ptr)->
next)
35if((
*
range_ptr)->ba>=ba)
36break;
37range->next=
*
range_ptr;
38*
range_ptr=range;
39/
*
40*
Concatenatefreerangewithneighbors,ifpossible.
41*
Tryforupperneighbor(nextinlist)first,then
42*
forlowerneighbor(predecessorinlist).
43*
/
44if(range->next!=NULL&&
45range->ba+range->size==range->next->ba){
46next=range->next;
47range->size+=range->next->size;
48range->next=next->next;
49kfree(next);
50}
51if(prev!=NULL&&
52prev->ba+prev->size==range->ba){
53prev->size+=prev->next->size;
54prev->next=range->next;
55kfree(range);
56}
57}
作为一个很有用的内存分配patch,bigphysarea却并没有merge到kernelmainline
中去,主要原因就是:几乎没有什么driver能够用到bigphysarea.
注:本例中用到的bigphysareapatch版本为.
参考文献
[1]Whatisbigphysarea,/Q/What_is_bigphysarea
[2]Whynobigphysareainmainline?/pipermail/linuxppc64-dev/2005-
March/
[3]/pipermail/celinux-dev/2007-September/
[4]/middelink/En/#bigphysarea
[5]UnderstandingtheLinuxVirtualMemoryManagement,MelGorman,PrenticeHall,2004
10
本文发布于:2023-03-05 23:47:17,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/1678031238151019.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:物理内存是什么.doc
本文 PDF 下载地址:物理内存是什么.pdf
留言与评论(共有 0 条评论) |