Linux 虚存的性能问题
虚存子系统是所有 UNIX 系统的核心组件。下面讨论虚存系统的实现及其对操作系统中几乎其他所有子系统的作用和影响。首先详细说明一些基本的内存管理问题;然后具体分析 Linux 操作系统如何实施虚存管理任务。进程(也标记为任务或默认线程)通过虚存子系统能够查看地址空间中的线性字节范围,这个功能与物理内存中的物理布局或者分片情况无关。 线程可以在一个呈现为 CPU 全部地址空间的虚拟环境中执行。这种(支撑)执行框架为进程提供了一种大型编程模型。在这种情形下,作为地址空间的内存虚拟视图被呈现给应用,而虚存子系统透明地管理虚拟存储器基础架构,从而对物理内存子系统以及辅助存储器加以一致管理 。
内核映像部分也称为标识映射段,而内核模块部分经常称为页表映射段。对高端地址空间(0xFFFF ..)的访问机制与平台相关。在 32位系统上,每个进程的虚址空间为 4GB;而在 64 位系统上,每个进程在理论上的虚址空间大小2^64通常并未全部利用。 某些系统(实质上是真正的处理器)只允许每个进程的虚址空间大小为 2^44。
一、内存与地址空间
由于物理内存子系统的延迟低于磁盘子系统, 虚存子系统所面临的挑战之一是将访问最频繁的内存部分保持在速度更快的主存中。当物理内存短缺时, 虚存子系统需要释放出部分物理内存。这通过将较少使用的内存页面输出到备份存储器上来完成。因此,进程无需管理物理内存分配的细节,从这个意义上说, 虚存子系统提供了资源虚拟化功能。进程也无需对信息和故障的隔离加以管理,因为每个进程都在自己的地址空间中执行。大多数情况下,通过阻止进程访问其合法地址空间之外的内存, 内存管理部件中的硬件机制可以执行内存保护功能。其例外情形是在多个进程之间显式共享的内存区域。
进程虚址空间的定义是作为运行环境呈现给进程的内存地址范围。在进程生命周期的任何时间点上,都会有一些进程地址被映射到物理地址,而另一些进程地址则不被映射。 当对fork()系统调用进行初始化时, 内核创建进程虚址空间的基本框架。进程内部的虚址空间布局由动态链接器建立,可以随着硬件平台的不同而变化。一般地, 虚址空间
由称为虚拟页面的同等大小的内存容量构成。在IA-32环境中,页面大小为4KB;在IA-64体系结构中, 页面大小可配置为 4KB、 8KB、 16KB或 64KB。 任何 Linux进程的虚址空间又进一步划分为两个主要区域: 用户空间和内核空间。 用户空间驻留在地址空间的较低部分,从地址零开始,其上限为在 processor.h中规定的与平台有关TASK_SIZE取值。 其余地址空间则保留给内核。 所示。由于页表假定为空的,该读操作会导致一个页面故障。为了响应这个页面故障, Linux内核会搜索该特定进程的 VM区域(area)列表, 以便定位包含该故障地址的 VM区域。在确定了针对该特定请求必须访问的页面之后, Linux发起一个磁盘文件读操作,如序号 2所示。当I/O子系统提供该文件后, 操作系统将数据复制到一个可用的页帧中,如序号3所示。完成这个读页面故障处理所需的最后步骤是对页表进行更新以便将虚址映射至包含数据的物理页帧。之后系统可以重新初始化这个读请求。此时该请求将成功完成,因为所需的数据已经可用。
诸如 kswapd或 pdflush线程等只访问内核地址空间的任务使用了一个匿名的地址空间,因此这些情况下的 mm指针引用值为 NULL。因为 mm结构包含了两个用于建立虚存环境的主要数据结构指针,所以被看作是虚存子系统核心的入口点。第一个结构是页表,第二个结构称为虚存区域。从内核的角度而言, 系统范围内的页表足以实现虚存机制。 一些更传统的大型页表, 包括分簇页表机制, 在表示大型地址空间时的效率并不高。
为了避免大型页表带来的问题, Linux并不使用页表本身来表示地址空间,而是使用了 VM区域结构的一组列表。 该方法的思想是将一块地址空间划分成可按照相同方式处理的多个连续页面范围, 其中每个范围都可以通过单个 VM区域结构来表示。 如果进程访问一个在页表中没有转换项的页面, 负责该特定页面的 VM区域拥有建立和安装该页
面所需的所有信息。如图 上图所示,通过 VM区域列表, Linux内核为映射到该特定进程地址空间中的任何具体地址创建实际的页表项。该场景的后果是每个进程的页表都可看成一个 cache子系统。换句话说,如果存在着转换项, 内核即可使用;如果该转换项不存在时,则内核可以基于相应的 VM区域来创建。项)时, 系统创建另一个数据结构, 将VM区域组织成自平衡的二叉搜索树。基于二叉树搜索算法,可以通过一系列步骤来定位与虚址相匹配的 VM区域结构。 这些步骤与地址空间中的 VM区域数目存在着对数关系。为了加快系统访问所有 VM区域结构的速度, Linux内核同时维护(在到达门限之后)线性列表和二叉树结构。
整个内核地址空间可以分解成内核映像空间(也称为实体映射段)和内核模块空间(也称为页表映射段)。
内核模块空间由内核私有页表映射,主要用于实现内核的 vmalloc()区域。 这允许系统分配连续的大量虚存范围。例如,可以在这个地址空间中分配用于加载特定内核模块所需的内存。与 vmalloc()相关的地址范围由两个与平台相关的参数 VMALLOC_START和 VMALLOC_ END控制。 vmalloc()区域并不一定占据整个页表映射段, 因此有可能将
该内存段的部分空间用于平台相关的目的和功能。
内核映像空间在以下意义上是唯一的:在该内存段中的某个虚址及其所转换成的物理地址之间存在着直接关联或映射。这种映射与平台相关,但一对一的对应关系为该内存段赋予了名称。这个内存段可以通过页表机制实现,但也可以使用与平台相关的更高效的技术。换句话说, 系统可以使用一个类似于(pfn = (addr - PAGE_OFFSET) /PAGE_SIZE)的简单映射公式。该公式能够最小化完全基于页表机制的系统实现的开销。尽管存在着这种简单方法,但某些 Linux系统使用了一个称为页帧位图(page frame map)的表来记录系统中物理页帧的状态。该表对于每个页帧都提供一个页帧描述符(pageframe descriptor, pfd), 其中包含了各种与资源有关的系统维护数据。 这些信息包括正在使用该页帧的地址空间计数或数量,以及各种指示页帧是否能换出到磁盘上或者页面是
否标记为脏状态的标志。
在 Linux中, 物理地址空间的实际大小和虚址空间的大小之间不存在着直接关联,但其容量都是有限的。为了更好地管理地址空间, Linux提供了对高端内存的支持。
二、高端内存支持
当代计算机系统上, 虚址空间的大小通常超出物理地址空间的大小。但物理地址空间容量的增长大致符合莫尔定律(Moore's Law), 该定律指出每隔 18个月, 芯片容量将翻倍增长。另外, 虚址空间的大小与平台相关,因此无法轻易改变。当物理内存空间接近虚址空间大小时,这个场景对 Linux系统提出了一个特有挑战:实体映射段的大小可
能不足以映射整个物理地址空间。
因此支持高端内存功能是 Linux内核的可选组件。 例如, 该功能在 Linux IA-64系统上是禁用的。
为了更高效地使用内存, Linux还提供了分页和交换机制。
系统需要创建该页面的一个私有副本并将其分配给发起该更新操作的进程。私有数据页面最初称为写时复制(copy-on-write)页面或按需填零(zero-fill-on-demand)页面。当出现页换出情况时,需要区别对待这些页面。大多数应用程序都会分配比其在任何特定时刻所用的内存更多的虚存。 例如,程序的文本段经常包含大量很少执行或从不执行的错误处
理代码。为了避免将内存浪费在从未访问的虚拟页面上, Linux(以及大多数其他 UNIX操作系统)使用了按需页面调度(demand paging)的方法。 在这种方法中, 虚址空间在最初时为空, 即所有虚拟页面在页表中都标记为不存在(not present)的状态。 当访问一个并不存在的虚拟页面时, CPU会生成个数据位, 称为访问(access)标志位和脏(dirty)标志位。访问位指示自从最近一次清空该位以来,是否访问过相应页面;脏标志位指示该页面自从最近一次换入之后是否曾被修改过。 Linux的 kswapd线程定期检查这两个数据位。在检查之后, kswapd清空访问位。如果 kswapd检测到内核将要面临内存不足的情形, 则抢先将最近未用的内存页换出。如果一个页面设置了脏标志位后,则在释放该页帧之前,需要将该页面写入磁盘。由于这是一种相对昂贵的操作, kswapd更趋向于释放已清除了访问位和脏位(置为 0)的页面。根据定义, 这些页面最近未被访问过并且在释放页帧之前不需要被写入磁盘,因此可以较低的性能开销回收。
四、Linux 页表
Linux系统在物理内存中为每个进程维护一个页表,并通过实体映射内核段来访问实际页表。 Linux中的页表无法被换出到交换空间中,这意味着一个分配了大量地址空间的进程有可能会导致内存子系统饱和,因为页表本身就将耗尽所有可用的内存。类似地,由于系统里包含数百个同时活跃着的进程,所有页表的组合大小也有可能消耗全部可用的内存。当今计算机系统上提供的大型内存子系统使得这种情形很少见,但仍然反映了一个需要解决的容量规划问题。将页表保持在物理内存中可以简化内核设计,并且无需处理嵌套的页故障。进程的页表布局基于三层树结构。第一层由全局目录(global directory,pgd)组成,第二层由中间目录(middle directory,pmd)组成,第三层由页表项(page
tableentry, pte)组成。 通常, 每个目录结点占用一个页帧并包含固定的项数。 pgd和 pmd 目录中的各项或者不存在或者指向下一层中的某个目录。 Pte项表示该树的叶子结点,包含实际的页表项。由于 Linux中的页表布局类似于一棵多层的树,其空间需求与使用中的实际虚址空间成比例。 因此, 该空间需求不是虚址空间的最大容量。 另外, 由于 Linux将内存作为一组页帧来管理,基于固定结点大小的方法并不需要基于线性页表的系统实现所需的物理连续的大型内存区域 。
当实现虚页面至物理页面转换时, 虚址被分解成多个部分。用于页表查询操作的各个虚址部分依赖于 pgd、 pmd和 pte索引(见下图)。存储于 mm结构中的页表指针发起一个查询操作。该页表指针引用作为页表树根目录的全局目录。 pgd索引所标识的项包含了中间目录地址。利用该地址与 pmd索引的组合能够定位 pte目录的地址。对该机制展一个额外层次, 可以确定该虚址实际映射到的页面的 pte。 通过 pte可以计算出物理页帧的地址,在利用虚址的偏移量取值即可标识出正确的内存位置。 Linux中的多层页表实现方法代表了一种与平台无关的解决方案。若某个特定实现不需要整个树结构来支持的话,该方案允许将中间目录转变成全局目录。 IA-32环境中可以采用这种方法,将页面中间目录的大小置为 1。换句话说, IA-32体系结构中将 32位的虚址如下分解: 10位用于页面目录, 10位用于页表项,其余 12位用于偏移量部分。这个地址转换过程是硬件(内存管理单元(memory management unit, MMU))和软件(内核)之间协作完成的。 内核与 MMU通信,为每个用户地址空间标识出映射至物理页面的虚拟页面。 MMU能够将该过程中的任何错误条件通知给内核。最常见的错误条件与页故障有关,这时内核需要从辅助存储器中获取所需页面。其他错误条件可能与任何潜在的页面保护问题有关或由其触发。从物理地址的角度来看, Linux会区分不同的内存存储区(ZONE_DMA、ZONE_NORMAL和 ZONE_ HIGHMEM),每个存储区都具有不同功用。大多数的内存分配操作发生在 ZONE_NORMAL区域中, 而 ZONE_HIGHMEM表示大于 896MB的物理地址空间 。
Linux 虚存的性能问题的更多相关文章
- Linux 虚存 linux2.6内核特性
一.大型页面的支持 当代计算机体系结构大都支持多种页面大小,例如,IA-32体系结构支持4KB或4MB的页面, Linux操作系统只是将大型页面用于映射实际的内核映像.大型页面的使用主要是为了改进高性 ...
- linux top命令看到的实存(RES)与虚存(VIRT)分析
近期在公司中解决程序使用的内存高问题,将一部分之前无法回收的内存进行了回收,实现降内存效果(降实存). 在统计效果时, QA问是统计RES(实存)还是VIRT(虚存). 在网上学习看了一些博客,这里自 ...
- 光荣之路测试开发面试linux考题之四:性能命令
Hi,大家好我是tom,I am back.今天要给大家讲讲linux系统一些性能相关命令. 1.fdisk 磁盘管理 是一个强大的危险命令,所有涉及磁盘的操作都由该命令完成,包括:新增磁盘.增删改磁 ...
- Linux服务器的那些性能参数指标
Linux服务器的那些性能参数指标 一个基于Linux操作系统的服务器运行的同时,也会表征出各种各样参数信息.通常来说运维人员.系统管理员会对这些数据会极为敏感,但是这些参数对于开发者来说也十分重要, ...
- 宿主机无法访问linux虚机中的网站
问题现象: Nginx服务已启动 80端口被nginx监听 宿主和linux虚机可相互ping通 Linux虚机可用curl访问网站 宿主无法用浏览器访问网站 排查: 1. 查看nginx的acce ...
- linux虚机配置开发/Server环境全集
linux虚机配置开发/Server环境全集 9. centos 升级githttp://www.cnblogs.com/grimm/p/5368777.htmla. 下载git2.2.1并将git添 ...
- Linux测试硬盘读性能的常用工具-hdparm和dd俩搭档
Linux测试硬盘读性能的常用工具-hdparm和dd俩搭档 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.hparm # 它用来在基于 Linux的系统上获取或 ...
- 【精】Linux磁盘I/O性能监控之iostat详解
[精]Linux磁盘I/O性能监控之iostat详解 Linux命令详解----iostat 使用iostat分析IO性能
- Azure Linux 虚机上配置 RAID 的常见问题及解决方案
简介 独立硬盘冗余阵列(RAID, Redundant Array of Independent Disks),简称磁盘阵列.能增强数据集成度,增强容错功能,增加处理量或容量.详情参见这篇文章. 配置 ...
随机推荐
- 06、NetCore2.0依赖注入(DI)之整合Autofac
06.NetCore2.0依赖注入(DI)之整合Autofac 除了使用NetCore2.0系统的依赖注入(DI)框架外,我们还可以使用其他成熟的DI框架,如Autofac.Unity等.只要他们支持 ...
- 学习linux的一些指令
简单说一下我对linux的理解,linux只有一个根目录,所有目录都挂在该根目录上,磁盘进行分区,然后生成文件系统,挂到目录上,/etc/fstab用于记录系统配置,比如分区挂载点,开机自动挂载等等. ...
- c++简单线程池实现
线程池,简单来说就是有一堆已经创建好的线程(最大数目一定),初始时他们都处于空闲状态,当有新的任务进来,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他 ...
- 关于Java中的Null
什么是Java中的Null? null在Java中是一个非常重要的概念,它最初是为了表示缺少某些东西,例如缺少用户.资源或任何东西而发明出来的.但是这也为Java程序员带来了很多麻烦,比如最常见的空指 ...
- [LeetCode] Subarray Sum Equals K 子数组和为K
Given an array of integers and an integer k, you need to find the total number of continuous subarra ...
- Linux提示字符设置
当我们登陆linux后,显示的提示字符究竟是什么意思呢?又可不可以设置呢. 首先来看看默认的显示: 普通用户: [fuwh@localhost ~]$ root用户: [root@localhost ...
- python3全栈开发-什么是粘包、粘包现象、如何解决粘包
一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.decode( ...
- P2520 [HAOI2011]向量
题目描述 给你一对数a,b,你可以任意使用(a,b), (a,-b), (-a,b), (-a,-b), (b,a), (b,-a), (-b,a), (-b,-a)这些向量,问你能不能拼出另一个向量 ...
- poj2331 (IDA*)
题意:给你k种管道,然后是每种的长度,每种的数量,求(x1,y1)到(x2,y2)所用管道的最少数量 思路: 最开始考虑的是直接bfs,但是没有成功. 然后发现可以先找x轴x1 到 x2 ,再找y轴y ...
- hdu2795 线段树 贴广告
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...