FreeBSD VM内核内存管理

本文涉及到的源码是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);

时间: 2024-08-03 18:49:24

FreeBSD VM内核内存管理的相关文章

Linux内核内存管理-内存访问与缺页中断

简单描述了x86 32位体系结构下Linux内核的用户进程和内核线程的线性地址空间和物理内存的联系,分析了高端内存的引入与缺页中断的具体处理流程.先介绍了用户态进程的执行流程,然后对比了内核线程,引入高端内存的概念,最后分析了缺页中断的流程. 用户进程 fork之后的用户态进程已经建立好了所需的数据结构,比如task struct,thread info,mm struct等,将编译链接好的可执行程序的地址区域与进程结构中内存区域做好映射,等开始执行的时候,访问并未经过映射的用户地址空间,会发生

Linux内核内存管理-内存访问与缺页中断【转】

转自:https://yq.aliyun.com/articles/5865 摘要: 简单描述了x86 32位体系结构下Linux内核的用户进程和内核线程的线性地址空间和物理内存的联系,分析了高端内存的引入与缺页中断的具体处理流程.先介绍了用户态进程的执行流程,然后对比了内核线程,引入高端内存的概念,最后分析了缺页中断的流程. 用户进程 fork之后的用户态进... 简单描述了x86 32位体系结构下Linux内核的用户进程和内核线程的线性地址空间和物理内存的联系,分析了高端内存的引入与缺页中断

Linux内存管理原理【转】

转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点.   1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻辑地址专指下文说的线性偏移前的地址)是一个概念.物理地址自不必提.内核的虚拟地址和物理地址,大部分只差一个线性偏移量.用户空间的虚拟地址和物理地址则采用了多级页表进行映射,但仍称之为线性地址. 2.

Linux内存管理【转】

转自:http://www.cnblogs.com/wuchanming/p/4360264.html 转载:http://www.kerneltravel.net/journal/v/mem.htm Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理

Linux内存管理 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书

Linux内存管理

转载:http://www.kerneltravel.net/journal/v/mem.htm Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书籍不惜笔墨重点讨论

Linux内核中的内存管理浅谈

 [十月往昔]--Linux内核中的内存管理浅谈 为什么要叫做"十月往昔"呢?是为了纪念我的原博客. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月里的文档. 十月往昔,总有一些觉得珍贵的,所以搬迁到这里来. 而这篇文章是在09.04.20-09.04.21里写的. Jason Lee   ------------–cut-line   1.基本框架(此处主要谈页式内存管理) 4G是一个比较敏感的字眼,早些日子,大多数机器(或者说操作系统)支持的内存上限

Linux内核剖析 之 内存管理

1. 内存管理区     为什么分成不同的内存管理区?     ISA总线的DMA处理器有严格的限制:只能对物理内存前16M寻址.     内核线性地址空间只有1G,CPU不能直接访问所有的物理内存.     ZONE_DMA                  小于16M内存页框     ZONE_NORMAL          16M~896M内存页框     ZONE_HIGHMEM        大于896M内存页框     ZONE_DMA和ZONE_NORMAL区域包含的页框,通过线性

十天学Linux内核之第三天---内存管理方式

原文:十天学Linux内核之第三天---内存管理方式 昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内存和内核的可用内存,还会讲到内核对内存分类的方式以及如何决定分配和释放内存,内存管理是应用程序通过软硬件协助来访问内存的一种方式,这里我们主要是介绍操作系统正常运行对内存的管理.插个话题,刚才和姐姐聊天,她快结婚了,说起了自己的初恋,可能是一句很