ImageVerifierCode 换一换
格式:DOCX , 页数:21 ,大小:66.24KB ,
资源ID:29964849      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/29964849.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux Bootmem机制.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Linux Bootmem机制.docx

1、Linux Bootmem机制10.Bootmem机制10.1.简介Bootmem机制是内核在启动时对内存的一种简单的页面管理方式。 它为建立页表管理代码中的数据结构提供动态分配内存的支持,为了对页面管理机制作准备,Linux使用了一种叫bootmem分配器(bootmem allocator)的机制,这种机制仅仅用在系统引导时,它为整个物理内存建立起一个页面位图。这个位图建立在内核代码映象终点_end上方的地方。这 个位图用来管理低区(可被直接一一映射的物理内存区,小于896Mb)。因为在0到896Mb的范围内,有些页面可能保留给内核代码,页目录,以及当前的 位图使用,有些页面可能有空洞,因

2、此,建立这个位图的目的就是要用一个比特位的两种状态标记物理页面的状态:已被保留;可被动态分配。Bootmem机制 的核心是对Bitmap的操作,相关代码位于mm/bootmem.c和include/linux/bootmem.h中。 图53.ARM上的Linux地址空间分布在介绍Bootmem机制之前需要对内核的地址空间分布做一个深入的了解: 32位操作系统只有4G的虚拟地址空间,通常Linux将最上的1G用于内核虚拟地址。ARM上将用户空间的3G最高处的16M用来给内核的模块使用。 Linux将物理内存完全一一映射到内核空间,这样很方便管理内存,任何页面的虚拟地址减去一个PAGE_OFFSE

3、T(0xc0000000)的偏移r然后加上物理地址的偏移PHYS_OFFSET就可以得到物理地址。 内 核还需要动态管理一些内存用于vmalloc或者设备临时映射等,因此不能将1G的虚拟空间完全一一映射物理内存,因此权衡了一个896M的大 小,0xc0000000到0xc0000000 + 896M的虚拟地址空间一一映射物理内存,从0xc0000000+896M到0xffffffff的地址空间作为动态映射的需要。对于大于896M的物理内存,是无法通过一一映射来访问的,通过vmalloc可以访问它们,但是对于大于4G的内存需要PAE的支持,否则无法访问。 10.2.bootmem_data用来存

4、放位图的数据结构为bootmem_data。 include/linux/bootmem.htypedef struct bootmem_data unsigned long node_min_pfn; unsigned long node_low_pfn; void *node_bootmem_map; unsigned long last_end_off; unsigned long hint_idx; struct list_head list; bootmem_data_t;在RAM为256M的ARM板上的测试结果输出如下.node_min_pfn:0x50000, node_low_

5、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记录了最后一次申请的空间后的一个物理页框的地址

6、。它方便下一次申请内存是使用。 list被用来连接到bdata_list。10.3.UMA和NUMALinux对物理内存的描述机制有两种:UMA和NUMA。在传统的计算机结构中,整个物理内存都是均匀一致的,CPU访问这个空间中的任何一个地址所需要的时间都相同,所以把这种内存称为“一致存储结构 (Uniform Memory Architecture),简称UMA。可是,在一些新的系统结构中,特别是多CPU结构的系统中,物理存储空间在这方面的一致性却成了问题。这是因 为,在多CPU结构中,系统中只有一条总线(例如,PCI总线),有多个CPU模块连接在系统总线上,每个CPU模块都有本地的物理内存,

7、但是也可以通过 系统总线访问其它CPU模块上的内存。另外,系统总线上还连接着一个公用的存储模块,所有的CPU模块都可以通过系统总线来访问它。因此,所有这些物理内 存的地址可以互相连续而形成一个连续的物理地址空间。显然,就某个特定的CPU而言,访问其本地的存储器速度是最快的,而穿过系统总线访问公用存储模块或其它CPU模块上的存储器就比较慢,而且还面临因可能 的竞争而引起的不确定性。也就是说,在这样的系统中,其物理存储空间虽然地址连续,但因为所处“位置”不同而导致的存取速度不一致,所以称为“非一致存储 结构(Non-Uniform Memory Architecture),简称NUMA。事实上,严

8、格意义上的UMA结构几乎不存在。就拿配置最简单的单CPU来说,其物理存储空间就包括了RAM、ROM(用于BIOS),还有图形卡上的静态 RAM。但是,在UMA中,除主存RAM之外的存储器空间都很小,因此可以把它们放在特殊的地址上,在编程时加以特别注意就行,那么,可以认为以RAM为 主体的主存是UMA结构。由于NUMA的引入,就需要存储管理机制的支持,因此,Linux内核从2.4版本开始就提供了对NUMA的支持(作为一个编译可选项)。为了对NUMA 进行描述,引入一个新的概念“存储节点(或叫节点),把访问时间相同的存储空间就叫做一个“存储节点”。一般来说,连续的物理页面应该分配在相同的存储 节点

9、上。例如,如果CPU模块1要求分配5个页面,但是由于本模块上的存储空间已经不够,只能分配3个页面,那么此时,是把另外两个页面分配在其它CPU 模块上呢,还是把5个页面干脆分配在一个模块上?显然,合理的分配方式因该是将这5个页面都分配在公用模块上。 mm/bootmem.cbootmem_data_t bootmem_node_dataMAX_NUMNODES _initdata;Linux定义了一个大小为MAX_NUMNODES类型为bootmem_data_t的bootmem_node_data数组,数组的大小根据 CONFIG_NODES_SHIFT的配置决定。对于UMA来说,NODES_

10、SHIFT为0,所以MAX_NUMNODES的值为1。 Linux把物理内存划分为三个层次来管理:存储节点(Node)、管理区(Zone)和页面(Page)。为了支持NUMA模型,也即CPU对不 同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node)。在一个单独的节点内,任一给定CPU访问页面所需的时间都是相同的。然 而,对不同的CPU,这个时间可能就不同。对每个CPU而言,内核都试图把耗时节点的访问次数减到最少这就要小心地选择CPU最常引用的内核数据结构的存 放位置。 另外,linux内核在一些特殊的单处理器上使用NUMA,这些系统的物理地址空间中拥有巨大的洞。内核通过

11、将有效物理地址的连续附属区域分配给不同的内存几点来处理这些体系结构。 每个节点中的物理内存又可以分为几个管理区(Zone)。每个节点都有一个类型为pg_data_t的描述符。对于UMA模式来说,系统中只需要描述符来定义一个节点,它被定义在mm/page_allloc.c中,名为contig_page_data。 #ifndef CONFIG_NEED_MULTIPLE_NODESstruct pglist_data _refdata contig_page_data = .bdata = &bootmem_node_data0 ;EXPORT_SYMBOL(contig_page_data);

12、#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结构体中的 struct bootmem_data类型成员bdata被用来在系统启动时通过bitmap管理该节点代表的内存。 pg_data_t discontig_node_da

13、taMAX_NUMNODES = .bdata = &bootmem_node_data0 , .bdata = &bootmem_node_data1 , .bdata = &bootmem_node_data2 , .bdata = &bootmem_node_data3 , .10.4.Debug机制bootmem.c中定义了bootmem_debug,将其置1,则可以查看Linux在使用bootmem机制时输出的信息。 mm/bootmem.cstatic int bootmem_debug = 1;bootmem.c中提供了一个名为bootmem_debug_setup的函数,它被用

14、来在系统引导期间解析Bootloader传递来的参数行,如果提供了bootmem_debug=1,那么这里的bootmem_debug开关将被置为1。 static int _init bootmem_debug_setup(char *buf) bootmem_debug = 1; return 0;early_param(bootmem_debug, bootmem_debug_setup);如果开启bootmem_debug,Bootloader中的bootargs参数看起来应该如下所示: bootargs=mem=64M console=ttyS1,115200n8 root=/dev

15、/ram0 rw initrd=0xc1180000,4M bootmem_debug=110.5.初始化函数init_bootmem_core是bootmem机制中的核心函数,如果需要使用bootmem机制来管理内存,那么首先需要使用该函数来建立Bootmem allocator,并初始化位图。并且该函数只在初始化时使用。 static unsigned long _init init_bootmem_core(bootmem_data_t *bdata, unsigned long mapstart, unsigned long start, unsigned long end); bda

16、ta存放初始化后的位图信息。 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_bootm

17、em函数将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。对于一个使用U

18、MA机制,物理内存为256Mb,且物理内存所在起始地址为0x50000000的系统,init_bootmem_core将打印以下信息: bootmem:init_bootmem_core nid=0 start=50000 map=5053c end=60000 mapsize=2000nid=0,指明当前操作的bdata对应到bootmem_node_data的数组索引,start=50000,即为0x50000000对应的物理页框。 图54.初始化后的位图为了针对特定的内存节点应用Bootmem机制,bootmem.c中在init_bootmem_core的基础上封装了针对特定节点操作的i

19、nit_bootmem_node函数。另外还有针对默认节点操作的init_bootmem函数,它们的调用关系如图所示: 图55.初始化函数调用unsigned long _init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn) return init_bootmem_core(pgdat-bdata, freepfn, startpfn, endpfn);注意到init_bootmem_node的第一个参数为pg_data_t类型。

20、init_bootmem只需要两个参数,bitmap的物理页框地址 start和物理页面数pages。NODE_DATA的作用就是取contig_page_data节点,而这里的所处理的物理页框起始地址永远为0。 include/linux/mmzone.h#define NODE_DATA(nid) (&contig_page_data)unsigned long _init init_bootmem(unsigned long start, unsigned long pages) max_low_pfn = pages; min_low_pfn = start; return init_

21、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

22、和0。所以_reserve的作用就是将对应的物理页框的比特位置为1,相当于malloc。 static int _init _reserve(bootmem_data_t *bdata, unsigned long sidx, unsigned long eidx, int flags); bdata指明当前的保留操作作用在哪个bootmem_data区。 sidx和eidx代表的是需要预留的物理页框对应的bit位在bdata中的node_min_pfn的索引值。 flags 标志,当前只支持BOOTMEM_DEFAULT和BOOTMEM_EXCLUSIVE。BOOTMEM_EXCLUSIVE

23、可以保证在将要保留的整个页 框中都是可以使用的,也即这些页框对应的bitmap连续为0,否则遇到已保留的页框,将释放已经获取的页框,并返回EBUSY。 BOOTMEM_DEFAULT则不考虑这一情况。_reserve调用test_and_set_bit来设置这一区域中的比特位,注意区域范围为sidx + bdata-node_min_pfn, eidx + bdata-node_min_pfn)。 static void _init _free(bootmem_data_t *bdata, unsigned long sidx, unsigned long eidx);_free函数在boot

24、meme机制中相当于通常使用的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_corealloc_bootmem_cor

25、e是使用bitmap分配内存空间的核心接口。 static void * _init alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit); bdata指明当前的内存申请操作作用在哪个bootmem_data区。 size指明所要申请的内存空间大小,它的单位是bytes。 align指明申请的内存空间的边界大小,通常为32,PAGE_SIZE(4K)等。 goal代表从bdata中node_m

26、in_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的整数倍;

27、如果limit不为0,那么需满足goal + size PAGE_SHIFT, 1UL)得到步进大小。右移PAGE_SHIFT用来计算align相对于PAGE_SIZE的倍数。step参数用来在搜索失败的情况下,搜索下一页框 时增加的页框数目:步进大小为1,则当bitmap中bit i为占用状态时搜索bit i + 1的状态,步进大小为2则当bitmap中一个bit i为占用状态时搜索bit i + 2的状态。 比较起点和上一次搜索结束的位置,从较小的位置开始搜索,如从上一次搜索结束的位置开始搜索,则设置回滚标识,如到终点还找不到合适的空间则从之前的起点开始进行搜索。 根据搜索的区域和申请的内

28、存大小,通过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,并返回申请到的内存的虚拟地

29、址,否则返回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, unsigned long size, unsigned long ali

30、gn, unsigned long goal) 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_bo

31、otmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal) 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