分享
 
 
 

FreeBSDVM---内核内存管理机制

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

本文涉及到的源码是FreeBSD5.0Release,参考4.4BSD设计与实现相关章节,Matt Dillon的文章。

VM系统涉及的主要数据结构描述

1. vmspace

该结构用于描述一个进程的虚拟地址空间,其包含了平台无关性的vm_map结构和平台相关性的pmap结构,以及该进程内存使用的一些统计计量。

2. vm_map

该结构是描述与平台无关性的虚拟地址空间的最高层数据结构,其包含了一系列虚拟地址有效地址映射实体和这些映射的属性。

3. vm_map_entry

该结构描述了一段虚拟地址空间(start ? end),以及该段地址空间代表的是一种VM对象、另一个地址映射还是一个地址子映射,及其相应的共享保护和继承等属性。

4. vm_object

该结构描述了一段虚拟地址空间的数据来源,它可以描述一个文件、一段为零的内存和一个设备等等。

5. vm_page

该结构描述了一页物理内存,是VM用于表述物理内存的低层数据结构。页尺寸是在系统启动时,由平台决定的。

6. pagerops (vm_pager)

该结构描述了VM对象的后台存储如何访问,在FreeBSD中,是通过pagerops结构描述函数指针,实现不同类型的对象的具体操作,在vm_object结构中,通过handle成员指定具体类型对象对应的数据结构,比如设备类型对应dev_t (cdev结构指针)。在一般OS描述中,采用vm_pager描述该目的的数据结构。

本文集中讨论FreeBSD内核虚拟地址空间的管理,涉及到内核地址空间分配和内核地址空间动态分配。FreeBSD的内核空间总是被映射到每一个进程的地址空间的最高部分。和任何其它进程一样,内核也是通过包含一系列的vm_map_entry结构实体的vm_map结构来管理一段地址空间的使用。子映射是内核映射特有的,用于隔离、限制一段地址空间以提供给内核子系统使用,比如mbuf操作。本文主要讨论与平台无关性的内容,涉及到平台相关性时,以i386为例简要说明。

1. SI_SUB_VM初始化

在系统启动时,mi_startup()函数会调用SI_SUB_VM初始化与平台无关的VM系统,其定义是:“SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL)”。在vm_mem_init函数初始化之后,我们就只使用虚拟内存了,现在分析该函数的实现:

vm_set_page_size();

该函数设置页面尺寸,i386是PAGE_SIZE(4K),记录在系统统计vmmeter结构类型的全局变量cnt的v_page_size成员中。

virtual_avail = vm_page_startup(avail_start, avail_end, virtual_avail);

该语句初始化常驻内存模块。分析函数vm_page_startup的参数和返回值:avail_start的值是从系统启动时,汇编语言调用init386的入参first,指明有效内存的起始地址;avail_end是在getmemsize()函数结束时,通过avail_end = phys_avail[pa_indx];语句获得,该函数是一个与平台相关的函数,这里不作详细讨论,只须明白getmemsize()函数是获得具体物理内存的尺寸;virtual_avail是指向第一个可用页面的虚拟地址,在调用vm_page_startup函数前,是在pmap_bootstrap函数中获得初始值,并在vm_page_startup函数中调整获得真实的值。函数vm_page_startup将物理内存整理、分配为页面单元,并初始化页面管理模块所需信息,每一个页面单元被放置在自由链表中,该函数实现的详细讨论在页调度中讨论,作为内核管理涉及到的区域分配器初始化的一部分是在该函数中通过调用uma_startup函数实现的,该函数的实现在随后讨论。

vm_object_init();

初始化VM的对象模块,FreeBSD是通过统一的vm_object结构使用虚拟内存,该函数完成虚拟内存对象模块所需信息的初始化。

vm_map_startup();

初始化VM地址映射模块。

kmem_init(virtual_avail, virtual_end);

该函数创建内核虚拟地址映射关系,将内核文本、数据、BSS和所有系统启动时已经分配了的空间做一个映射,插入VM_MIN_KERNEL_ADDRESS和virtual_avail之间,余下的virtual_avail和virtual_end之间的地址空间是可用的自由空间。

pmap_init(avail_start, avail_end);

该函数初始化物理内存地址空间的映射关系。

vm_pager_init();

该函数实现系统所支持的所有页面接口类型的初始化,页面接口为数据在其支持的存储空间和物理内存之间的移动提供了一种机制,比如磁盘设备与内存之间,文件系统与内存之间。

至此,vm_mem_init函数执行完成,VM系统初始化完成。

2. 内核地址空间分配

VM系统内核使用的虚拟地址空间段提供了一套用于分配和释放的函数,这些空间段可以从内核地址映射和子映射中分配获得。

根据申请的页是否可以被pageout守护进程调度,内核内存分配有两种路径。在VM子系统初始化时,调用了kmem_init函数创建了内核映射。我们分析该函数的具体实现:

函数void kmem_init(vm_offset_t start, vm_offset_t end)

m = vm_map_create(kernel_pmap, VM_MIN_KERNEL_ADDRESS, end);

函数vm_map_create根据给定了kernel_map物理地址,创建一个新的地址映射m,而VM_MIN_KERNEL_ADDRESS和end给出了该映射范围的下水位(lower address bound)和上水位(upper address bound)。

kernel_map = m;

kernel_map-system_map = 1;

(void) vm_map_insert(m, NULL, (vm_offset_t) 0,

VM_MIN_KERNEL_ADDRESS, start, VM_PROT_ALL, VM_PROT_ALL, 0);

vm_map_unlock(m);

由于函数kmem_init仅用于系统初始化,创建内核地址映射,因此,将获得的地址映射赋给全局变量kernel_map保存,通过vm_map_insert函数创建一个vm_map_entry实体记录相关值,VM_PROT_ALL和VM_PROT_ALL标识这段虚拟地址的访问权限,参见/sys/vm/vm.h定义。

2.1 Wired (nonpageable,不可被pageout调度的页)分配函数

固定页(wired page)是从来不会产生页错误(page fault)。其分配是由kmem_alloc函数和kmem_malloc函数实现的。

函数vm_offset_t kmem_alloc(vm_map_t map, vm_size_t size)

该函数用于在内核地址映射或子映射中,分配内存。

size = round_page(size);

调整申请内存的尺寸,使之为PAGE_SIZE的整数倍。

vm_map_lock(map);

if (vm_map_findspace(map, vm_map_min(map), size, &addr)) {

vm_map_unlock(map);

return (0);

}

offset = addr - VM_MIN_KERNEL_ADDRESS;

vm_object_reference(kernel_object);

vm_map_insert(map, kernel_object, offset, addr, addr + size,

VM_PROT_ALL, VM_PROT_ALL, 0);

vm_map_unlock(map);

在vm_map结构的lock锁机制保护下。通过调用vm_map_findspace函数查询map地址映射是否有足够的空间满足申请的内存尺寸,如果成功,则可用空间的起始地址存于addr中;如果失败则返回1,则kmem_alloc函数调用失败。获得该内存分配空间起始地址与VM_MIN_KERNEL_ADDRESS的偏移。通过vm_object_reference函数对内核对象kernel_obj计数器ref_count加1,kernel_obj的初始化是在VM初始化时,调用vm_object_init实现的,其类型是OBJT_DEFAULT。调用vm_map_insert函数将刚找到的虚拟地址空间插入地址映射map的vm_map_entry链表中。

for (i = 0; i

vm_page_t mem;

mem = vm_page_grab(kernel_object, OFF_TO_IDX(offset + i),

VM_ALLOC_ZERO | VM_ALLOC_RETRY);

if ((mem-flags & PG_ZERO) == 0)

pmap_zero_page(mem);

mem-valid = VM_PAGE_BITS_ALL;

vm_page_flag_clear(mem, PG_ZERO);

vm_page_wakeup(mem);

}

接下来的这段代码非常有意思,对于申请的内存空间每一页,通过调用vm_page_grab函数,查看该页是否已经被kernel_object持有,如果是,则根据vm_page成员flags标识,如果是PG_BUSY,则等待该标识清PG_BUSY,将该页重新设置为PG_BUSY,返回该地址映射(mem),如果该页没有被kernel_object持有,则分配一个新页(mem)。通过判断PG_ZERO标识,保证该页已经清零。最后通过vm_page_wakeup函数,给正在等待该页分配的线程一个唤醒的机会。这段代码在查找kernel_object持有页时,采用了自顶向下的展开算法(Sleator and Tarjan's top-down splay algorithm)。

(void) vm_map_wire(map, addr, addr + size, FALSE);

设置该段内存是wired。

函数kmem_alloc是非常低层的,一般和平台相关性的函数在申请内存会调用该函数,比如sysarch()。通常kmem_alloc是使用kernel_map地址映射和kernel_object VM对象。

函数vm_offset_t kmem_malloc(vm_map_t map, vm_size_t size, int flags)

该函数同样是用于在内核地址映射或子映射中,分配内存。区别是:

a) kmem_alloc函数在不能获得内存时,可以阻塞等待,而在中断层,分配内存是不能阻塞的,因此需要kmem_malloc以M_NOWAIT标识调用。

b) kmem_malloc函数为malloc调用(malloc(9))提供一种实现机制,即:在内核需要动态分配内存(malloc)时,当申请尺寸大于其阀值,最终通过kmem_malloc实现空间分配。

c) kmem_alloc路径是使用地址映射kernel_map和对象kernel_object;而kmem_malloc路径是使用kernel_map的子映射kmem_map和对象kmem_object,后者的具体讨论在后一节说明。

size = round_page(size);

addr = vm_map_min(map);

首先,根据入参调整、设置尺寸和地址。

vm_map_lock(map);

if (vm_map_findspace(map, vm_map_min(map), size, &addr)) {

vm_map_unlock(map);

if (map !=

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有