Linux Bootmem机制.docx

上传人:b****5 文档编号:29964849 上传时间:2023-08-03 格式:DOCX 页数:21 大小:66.24KB
下载 相关 举报
Linux Bootmem机制.docx_第1页
第1页 / 共21页
Linux Bootmem机制.docx_第2页
第2页 / 共21页
Linux Bootmem机制.docx_第3页
第3页 / 共21页
Linux Bootmem机制.docx_第4页
第4页 / 共21页
Linux Bootmem机制.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

Linux Bootmem机制.docx

《Linux Bootmem机制.docx》由会员分享,可在线阅读,更多相关《Linux Bootmem机制.docx(21页珍藏版)》请在冰豆网上搜索。

Linux Bootmem机制.docx

LinuxBootmem机制

10. Bootmem机制

10.1. 简介

Bootmem机制是内核在启动时对内存的一种简单的页面管理方式。

它为建立页表管理代码中的数据结构提供动态分配内存的支持,为了对页面管理机制作准备,Linux使用了一种叫bootmem分配器(bootmemallocator)的机制,这种机制仅仅用在系统引导时,它为整个物理内存建立起一个页面位图。

这个位图建立在内核代码映象终点_end上方的地方。

这个位图用来管理低区(可被直接一一映射的物理内存区,小于896Mb)。

因为在0到896Mb的范围内,有些页面可能保留给内核代码,页目录,以及当前的位图使用,有些页面可能有空洞,因此,建立这个位图的目的就是要用一个比特位的两种状态标记物理页面的状态:

已被保留;可被动态分配。

Bootmem机制的核心是对Bitmap的操作,相关代码位于mm/bootmem.c和include/linux/bootmem.h中。

图 53. ARM上的Linux地址空间分布

在介绍Bootmem机制之前需要对内核的地址空间分布做一个深入的了解:

∙32位操作系统只有4G的虚拟地址空间,通常Linux将最上的1G用于内核虚拟地址。

ARM上将用户空间的3G最高处的16M用来给内核的模块使用。

∙Linux将物理内存完全一一映射到内核空间,这样很方便管理内存,任何页面的虚拟地址减去一个PAGE_OFFSET(0xc0000000)的偏移r然后加上物理地址的偏移PHYS_OFFSET就可以得到物理地址。

∙内核还需要动态管理一些内存用于vmalloc或者设备临时映射等,因此不能将1G的虚拟空间完全一一映射物理内存,因此权衡了一个896M的大小,0xc0000000到0xc0000000+896M的虚拟地址空间一一映射物理内存,从0xc0000000+896M到0xffffffff的地址空间作为动态映射的需要。

对于大于896M的物理内存,是无法通过一一映射来访问的,通过vmalloc可以访问它们,但是对于大于4G的内存需要PAE的支持,否则无法访问。

10.2. bootmem_data

用来存放位图的数据结构为bootmem_data。

include/linux/bootmem.h

typedefstructbootmem_data{

unsignedlongnode_min_pfn;

unsignedlongnode_low_pfn;

void*node_bootmem_map;

unsignedlonglast_end_off;

unsignedlonghint_idx;

structlist_headlist;

}bootmem_data_t;

在RAM为256M的ARM板上的测试结果输出如下

.node_min_pfn:

0x50000,node_low_pfn:

0x60000

.node_bootmem_map:

c053c000

.last_end_off:

0x0

.hint_idx:

0x0

∙node_min_pfn/node_low_pfn表示最小和最大的物理内存页框,node_low_pfn-node_min_pfn代表物理内存的页框数,最大不能超过896Mb对应的页框数目0x38000。

∙node_bootmem_map表示存放bootmem位图的地址,即内核映象结束处的第一个页面的所在地址。

∙last_end_off记录的是上次申请的空间后的第一个相对于0偏移的物理地址。

∙hint_idx记录了最后一次申请的空间后的一个物理页框的地址。

它方便下一次申请内存是使用。

∙list被用来连接到bdata_list。

10.3. UMA和NUMA

Linux对物理内存的描述机制有两种:

UMA和NUMA。

在传统的计算机结构中,整个物理内存都是均匀一致的,CPU访问这个空间中的任何一个地址所需要的时间都相同,所以把这种内存称为“一致存储结构(UniformMemoryArchitecture),简称UMA。

可是,在一些新的系统结构中,特别是多CPU结构的系统中,物理存储空间在这方面的一致性却成了问题。

这是因为,在多CPU结构中,系统中只有一条总线(例如,PCI总线),有多个CPU模块连接在系统总线上,每个CPU模块都有本地的物理内存,但是也可以通过系统总线访问其它CPU模块上的内存。

另外,系统总线上还连接着一个公用的存储模块,所有的CPU模块都可以通过系统总线来访问它。

因此,所有这些物理内存的地址可以互相连续而形成一个连续的物理地址空间。

显然,就某个特定的CPU而言,访问其本地的存储器速度是最快的,而穿过系统总线访问公用存储模块或其它CPU模块上的存储器就比较慢,而且还面临因可能的竞争而引起的不确定性。

也就是说,在这样的系统中,其物理存储空间虽然地址连续,但因为所处“位置”不同而导致的存取速度不一致,所以称为“非一致存储结构(Non-UniformMemoryArchitecture),简称NUMA。

事实上,严格意义上的UMA结构几乎不存在。

就拿配置最简单的单CPU来说,其物理存储空间就包括了RAM、ROM(用于BIOS),还有图形卡上的静态RAM。

但是,在UMA中,除主存RAM之外的存储器空间都很小,因此可以把它们放在特殊的地址上,在编程时加以特别注意就行,那么,可以认为以RAM为主体的主存是UMA结构。

由于NUMA的引入,就需要存储管理机制的支持,因此,Linux内核从2.4版本开始就提供了对NUMA的支持(作为一个编译可选项)。

为了对NUMA进行描述,引入一个新的概念-“存储节点(或叫节点),把访问时间相同的存储空间就叫做一个“存储节点”。

一般来说,连续的物理页面应该分配在相同的存储节点上。

例如,如果CPU模块1要求分配5个页面,但是由于本模块上的存储空间已经不够,只能分配3个页面,那么此时,是把另外两个页面分配在其它CPU模块上呢,还是把5个页面干脆分配在一个模块上?

显然,合理的分配方式因该是将这5个页面都分配在公用模块上。

mm/bootmem.c

bootmem_data_tbootmem_node_data[MAX_NUMNODES]__initdata;

Linux定义了一个大小为MAX_NUMNODES类型为bootmem_data_t的bootmem_node_data数组,数组的大小根据CONFIG_NODES_SHIFT的配置决定。

对于UMA来说,NODES_SHIFT为0,所以MAX_NUMNODES的值为1。

Linux把物理内存划分为三个层次来管理:

存储节点(Node)、管理区(Zone)和页面(Page)。

为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node)。

在一个单独的节点内,任一给定CPU访问页面所需的时间都是相同的。

然而,对不同的CPU,这个时间可能就不同。

对每个CPU而言,内核都试图把耗时节点的访问次数减到最少这就要小心地选择CPU最常引用的内核数据结构的存放位置。

另外,linux内核在一些特殊的单处理器上使用NUMA,这些系统的物理地址空间中拥有巨大的"洞"。

内核通过将有效物理地址的连续附属区域分配给不同的内存几点来处理这些体系结构。

每个节点中的物理内存又可以分为几个管理区(Zone)。

每个节点都有一个类型为pg_data_t的描述符。

对于UMA模式来说,系统中只需要描述符来定义一个节点,它被定义在mm/page_allloc.c中,名为contig_page_data。

#ifndefCONFIG_NEED_MULTIPLE_NODES

structpglist_data__refdatacontig_page_data={.bdata=&bootmem_node_data[0]};

EXPORT_SYMBOL(contig_page_data);

#endif

对于NUMA来说,Linux在arch/arm/mm/discontig.c中定义了一个名为discontig_node_data的数组。

contig_page_data和discontig_node_data均被EXPORT_SYMBOL出来,作为全局变量使用。

另外注意到contig_page_data和discontig_node_data在被定义时都是指定了成员bdata的值。

pg_data_t结构体中的structbootmem_data类型成员bdata被用来在系统启动时通过bitmap管理该节点代表的内存。

pg_data_tdiscontig_node_data[MAX_NUMNODES]={

{.bdata=&bootmem_node_data[0]},

{.bdata=&bootmem_node_data[1]},

{.bdata=&bootmem_node_data[2]},

{.bdata=&bootmem_node_data[3]},

......

10.4. Debug机制

bootmem.c中定义了bootmem_debug,将其置1,则可以查看Linux在使用bootmem机制时输出的信息。

mm/bootmem.c

staticintbootmem_debug=1;

bootmem.c中提供了一个名为bootmem_debug_setup的函数,它被用来在系统引导期间解析Bootloader传递来的参数行,如果提供了bootmem_debug=1,那么这里的bootmem_debug开关将被置为1。

staticint__initbootmem_debug_setup(char*buf)

{

bootmem_debug=1;

return0;

}

early_param("bootmem_debug",bootmem_debug_setup);

如果开启bootmem_debug,Bootloader中的bootargs参数看起来应该如下所示:

bootargs=mem=64Mconsole=ttyS1,115200n8root=/dev/ram0rwinitrd=0xc1180000,4Mbootmem_debug=1

10.5. 初始化函数

init_bootmem_core是bootmem机制中的核心函数,如果需要使用bootmem机制来管理内存,那么首先需要使用该函数来建立Bootmemallocator,并初始化位图。

并且该函数只在初始化时使用。

staticunsignedlong__initinit_bootmem_core(bootmem_data_t*bdata,

unsignedlongmapstart,unsignedlongstart,unsignedlongend);

∙bdata存放初始化后的位图信息。

∙mapstart指明位图所要存放的物理页框。

∙start和end指明该分配器所要管理的物理内存的起止页框。

init_bootmem_core完成了以下工作:

∙根据mapstart指定的物理页框,计算bdata中的node_bootmem_map所应该对应的虚拟地址。

首先将mapstart物理页框通过PFN_PHYS转换为对应的物理地址,然后通过phys_to_virt转换为虚拟地址。

∙bdata中的node_min_pfn和node_low_pfn被分别赋值为start和end。

它们记录了当前bdata可以管理的物理内存范围的起止页框。

∙使用link_bootmem函数将bdata挂载到bdata_list全局变量中,Linux中所有的bdata都被链入bdata_list进行统一管理。

链入时将根据node_min_pfn的值来确定挂载点。

整个链表中的bdata节点内node_min_pfn总是从小到大排列的。

∙最后物理页框对应的bitmap,也即node_bootmem_map指向的地址,大小为bitmap大小的区域全部置为0xff,也即全部保留。

bitmap大小由bootmap_bytes计算出,其中会考虑到字节圆整,通常的公式为(pages+7)/8,另外要保证对齐到32。

对于256M的内存来说,正好是0x2000。

对于一个使用UMA机制,物理内存为256Mb,且物理内存所在起始地址为0x50000000的系统,init_bootmem_core将打印以下信息:

bootmem:

:

init_bootmem_corenid=0start=50000map=5053cend=60000mapsize=2000

nid=0,指明当前操作的bdata对应到bootmem_node_data的数组索引,start=50000,即为0x50000000对应的物理页框。

图 54. 初始化后的位图

为了针对特定的内存节点应用Bootmem机制,bootmem.c中在init_bootmem_core的基础上封装了针对特定节点操作的init_bootmem_node函数。

另外还有针对默认节点操作的init_bootmem函数,它们的调用关系如图所示:

图 55. 初始化函数调用

unsignedlong__initinit_bootmem_node(pg_data_t*pgdat,unsignedlongfreepfn,

unsignedlongstartpfn,unsignedlongendpfn)

{

returninit_bootmem_core(pgdat->bdata,freepfn,startpfn,endpfn);

}

注意到init_bootmem_node的第一个参数为pg_data_t类型。

init_bootmem只需要两个参数,bitmap的物理页框地址start和物理页面数pages。

NODE_DATA的作用就是取contig_page_data节点,而这里的所处理的物理页框起始地址永远为0。

include/linux/mmzone.h

#defineNODE_DATA(nid)(&contig_page_data)

unsignedlong__initinit_bootmem(unsignedlongstart,unsignedlongpages)

{

max_low_pfn=pages;

min_low_pfn=start;

returninit_bootmem_core(NODE_DATA(0)->bdata,start,0,pages);

}

通常在系统引导的时候调用init_bootmem_node来针对特定的节点初始化bootmem,而不是直接调用init_bootmem,这是因为很少有物理地址从0开始,它是由CPU的物理地址分配决定的,通常RAM占用的物理地址空间总是有一定的偏移,比如0x50000000。

10.6. __reserve和__free

不管是何种内存管理方式,最基本的功能就是内存的分发和回收,比如malloc和free。

在bootmem机制中被称为__reserve和__free,分别对应bitmap中的比特位的状态1和0。

所以__reserve的作用就是将对应的物理页框的比特位置为1,相当于malloc。

staticint__init__reserve(bootmem_data_t*bdata,unsignedlongsidx,

unsignedlongeidx,intflags);

∙bdata指明当前的保留操作作用在哪个bootmem_data区。

∙sidx和eidx代表的是需要预留的物理页框对应的bit位在bdata中的node_min_pfn的索引值。

∙flags标志,当前只支持BOOTMEM_DEFAULT和BOOTMEM_EXCLUSIVE。

BOOTMEM_EXCLUSIVE可以保证在将要保留的整个页框中都是可以使用的,也即这些页框对应的bitmap连续为0,否则遇到已保留的页框,将释放已经获取的页框,并返回EBUSY。

BOOTMEM_DEFAULT则不考虑这一情况。

__reserve调用test_and_set_bit来设置这一区域中的比特位,注意区域范围为[sidx+bdata->node_min_pfn,eidx+bdata->node_min_pfn)。

staticvoid__init__free(bootmem_data_t*bdata,

unsignedlongsidx,unsignedlongeidx);

__free函数在bootmeme机制中相当于通常使用的free函数。

与__reserve相似,但是它调用test_and_clear_bit对需要释放的页框区对应的位图进行清零,如果在清零过程中发现该函数返回0,说明该区域中的比特位有异常翻转,调用BUG()抛出并将系统挂起。

它的作用区域与__reserve一致。

注意:

test_and_set_bit(0,start_addr)中,"0"不是要设置的值,而是表示start_addr中第0位需要被设置为"1"。

此函数返回相应比特位上一次被设置的值。

test_and_clear_bit与此相同。

10.7. alloc_bootmem_core

alloc_bootmem_core是使用bitmap分配内存空间的核心接口。

staticvoid*__initalloc_bootmem_core(structbootmem_data*bdata,

unsignedlongsize,unsignedlongalign,

unsignedlonggoal,unsignedlonglimit);

∙bdata指明当前的内存申请操作作用在哪个bootmem_data区。

∙size指明所要申请的内存空间大小,它的单位是bytes。

∙align指明申请的内存空间的边界大小,通常为32,PAGE_SIZE(4K)等。

∙goal代表从bdata中node_min_pfn搜索空闲内存的开始地址,如果为0,则默认从node_min_pfn开始。

goal应该位于node_min_pfn和node_low_pfn所对应的页框所代表的地址之间,否则按0处理。

∙limit代表从bdata中node_min_pfn搜索空闲内存的终点地址,如果为0,则搜索到node_low_pfn结束。

alloc_bootmem_core尝试在goal和limit指定的虚拟地址范围[goal/node_min_pfn,limit/node_low_pfn]中分配size字节的内存,并且获取的内存与align对齐:

∙首先检查参数的合法性。

size不可为0;align必须为2的整数倍;如果limit不为0,那么需满足goal+size

∙根据bdata中提供的node_min_pfn和node_low_pfn,以及goal和limit的值确定需要搜索的物理页框范围。

最大页框max由node_low_pfn和limit共同确定,如果limit不为0,则取最小者。

最小页框min取node_min_pfn和goal中最大值,并且对齐到align参数决定的step。

∙根据公式step=max(align>>PAGE_SHIFT,1UL)得到步进大小。

右移PAGE_SHIFT用来计算align相对于PAGE_SIZE的倍数。

step参数用来在搜索失败的情况下,搜索下一页框时增加的页框数目:

步进大小为1,则当bitmap中biti为占用状态时搜索biti+1的状态,步进大小为2则当bitmap中一个biti为占用状态时搜索biti+2的状态。

∙比较起点和上一次搜索结束的位置,从较小的位置开始搜索,如从上一次搜索结束的位置开始搜索,则设置回滚标识,如到终点还找不到合适的空间则从之前的起点开始进行搜索。

∙根据搜索的区域和申请的内存大小,通过find_next_zero_bit函数来查找一个连续的并满足size要求的空闲内存块。

如果有足够的空闲bit则将这些bit设为占用状态,并清空bit所对应的空间的数据,返回空闲空间的起始地址。

∙更新last_end_off和hint_idx,以备下次申请内存时快速定位空闲的内存区。

其中last_end_off记录了上次申请的空间后的第一个相对于0偏移的物理地址,所以本次申请的内存总是紧邻此后的并满足goal和align要求的内存。

∙函数执行成功则通过__reserve及BOOTMEM_EXCLUSIVE标志预留对应的bitmap,memset置该区域为0,并返回申请到的内存的虚拟地址,否则返回NULL。

bootmem.c中对alloc_bootmem_core进行了一系列的扩展以完成丰富的功能。

图 56. alloc_bootmem_core调用

bootmem机制中提供的__alloc_bootmem_node和__alloc_bootmem_low_node函数被用来针对特定的节点进行内存管理。

它们均通过调用___alloc_bootmem_node来实现。

它们的第一个参数均为pg_data_t类型。

void*__init__alloc_bootmem_node(pg_data_t*pgdat,unsignedlongsize,

unsignedlongalign,unsignedlonggoal)

{

return___alloc_bootmem_node(pgdat->bdata,size,align,goal,0);

}

__alloc_bootmem_low_node和__alloc_bootmem_node类似,唯一区别在于limit参数。

ARCH_LOW_ADDRESS_LIMIT只在特定的体系架构上起作用,也即申请的内存被限制在ARCH_LOW_ADDRESS_LIMIT之下的内存中。

通常它被定义为0xffffffffUL,也即是32位系统可以支持的最大虚拟地址,此时它的作用与0参数相同。

void*__init__alloc_bootmem_low_node(pg_data_t*pgdat,unsignedlongsize,

unsignedlongalign,unsignedlonggoal)

{

return___alloc_bootmem_node(pgdat->bdata,size,align,

goal,ARCH_LOW_ADDRESS_LIMIT);

}

___alloc_bootmem_node在节点内存分配中是一个关键的函数。

∙首先通过alloc_bootmem_core在给定的节点中进行内存分配。

∙如果分配失败,则调用___alloc_bootmem尝试在bdata_list链表中的所有bdata中分配内存。

___alloc_b

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1