虚拟内存管理子系统是操作系统的核心之一. 有了虚拟内存(Virtual Machine-VM), 操作系统中诸如进程间隔离, 文件缓存, 存储交换(swapping)等一系列高级的功能才得以实现. 因此, 系统管理员只有在掌握操作系统中的虚拟内存管理的原理以及如何配置虚拟内存相关参数, 才能在一定的工作负载下配置出机器的最优的性能. 读完了本篇文章后, 你应该能基本掌握红帽公司企业级的Linux系统(RHEL3)的虚拟内存控制及其背后的实现算法. 不仅如此, 你更应该对通用的Linux虚拟内存管理的参数配置有一定的心得. 你也应该注意到, Linux作为操作系统其设计的革新是值得称道的. 内核中不适用的设计被摒弃了, 新的更好的设计也取代了旧的设计. 因而本文中所描述的配置参数对新版或者老版的Linux内核可能不再适用. 但是也不要气馁. 对虚拟内存管理有了深入的认识后, 配置其它的虚拟内存系统也就是小菜一碟了, 因为基本的原理是相通的. 对于特定的Linux内核版本, 可以在内核源码中Documentation/sysctl/vm.txt文件中找到虚拟内存相关的帮助, VM可供配置的参数也会描述于此文件中.
为了正确的理解虚拟内存管理器的工作原理,磨刀不误砍柴工,我们先来了解一下虚拟内存的组成。虽然对于虚拟内存低层级组成概念很有益处,但是有必要更深入地了解虚拟内存如何工作以及怎样才能优化其性能。
图表1. 高级虚拟内存子系统组成图
Linux系统中的虚拟内存子系统复杂极其复杂,但是我们可以通过下面的组件更深入地了解虚拟内存:
内存管理单元(MMU, Memory Management Unit,下面简称MMU)是作为实现虚拟内存系统的物理硬件基础,MMU可以允许软件通过一个别名的地址跟物理地址建立映射,通常是多于一个。这是通过使用分页(pages)和分页表(分页表:分页表是一种数据结构,为使用电脑操作系统之虚拟内存技术,将内存空间切割成分页的形式,用于储存虚拟内存及实体内存间的对应). MMU再使用一部分内存,通过一系列的查找表(Table lookups)来翻译虚拟地址到物理地址的映射。
Zoned Buddy Allocator (暂译为:区域内存分配器 没有找到中文标准的翻译, Buddy Allocator暂译为友内存分配器)
区域内存分配器负责整个虚拟内存系统分页存储管理。 这部分代码管理连续物理内存分页的链表并且让他们映射到MMU的分页表(page tables),当其他系统和核心子系统请求分配物理地址的时候,由其提供有效的物理地址(物理地址到虚拟内存地址的映射是被虚拟内存系统较高层处理的)。通过友内存分配器的名字我们就可以推断出子系统用来维护空闲列表的算法。所有在内存中的物理分页是被友内存分配器分类和分组进入列表的。每一个列表代表了2n分页个的簇,这里的n会随着每个逐步自增。如果在请求列表中没有任何请求,下一个里诶包的请求将会被分在两个隔离的簇中并且在下一个请求到达的时候返回给请求者。当分配返回请求给到好友分配器友内存分配器的时候,反转处理便开始了;注意到友内存分配器也管理着定义不同用途的内存池的内存区域。目前友内存分配器能够管理进入一下三种内存池:
Slab分配器提供了一种可用性更高的前端实现来配合Buddy(伙伴算法)分配器,它主要用来应对内核中某些部分需求大小更加灵活内存(并非常用的4KB)的请求。Slab分配器允许内核组件创建给定大小的内存对象缓存。Slab分配器负责将尽可能多的缓存对象放在一页并且监控哪些对象已经释放,哪些内存已经被分配。当有内存分配请求但是页面中没有内存可用时,Slab分配器会向Buddy分配器请求更多的页来满足分配请求。这就使得内核组件用一种更简单的方法来使用内存。使用这种方法,很多只利用一小部分内存的组件就不需要各自独立实现内存管理的代码,从而不需要浪费很多的页。Slab分配器只可能从DMA和NORMAL区域分配内存。
有关于slab分配器请参考:http://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
最后一个虚拟内存子系统的组件是内核线程,包括:kscand, kswapd, kupdated, 和bdflush。这些线程负责正在使用的内存的恢复和管理。虚拟内存中的所有页面都有一个关联的状态(更多关于内存状态机的信息请参考"页面的生命周期"章节)一般来说,内核中虚拟内存相关的活跃线程负责尝试将页面移出RAM的操作。它们定期的检查RAM,尝试识别和释放非活跃的内存,从而使得这一部分内存可以在系统中另作他用。
所有由虚拟内存管理的内存都会被一个状态标记。这些状态帮助虚拟内存知道在各种各样的情形下对给定的页面该做些什么。依赖于当前系统的需要,虚拟内存可能依据状态机(图示2. "虚拟内存页面状态机")将页面从一种状态转移到下一个状态。利用这些状态, 虚拟内存可以决定操作系统在某个时间对某个页面做了什么,并且它还可以决定对这个页面做什么操作。这些有特殊意义的状态如下所示
1.FREE —— 所有可被分配的页面从这个状态开始。这个状态告诉虚拟内存本页面没有被用于任何目的,并且可分配。
2.ACTIVE —— 页面已经被Buddy分配器分配了之后进入ACTIVE状态。这个状态告诉虚拟内存本页面已经被分配,并且它已经被内存进程或者用户进程所使用。
3. INACTIVE DIRTY —— 这个状态预示着本页面已经被要求分配它的进程所抛弃,并且它成为将要从主存中被剔除的候选者。kscand任务会定期扫描内存中的页面,并记下页面自从最后一次访问的到当前呆在内存的总时间。如果kscand任务发现自从上次它扫面这个页面以来,这个页面有被访问,它会增加这个页面的年龄计数器的值,否则,它会减少这个页面的年龄计数器的值。当kscand任务发现这个页面的年龄计数器的值为0,它会将这个页面的状态置成INACTIVE DIRTY状态。在INACTIVE DIRTY状态下的页面被保存在将要被清除的页面列表里面。
4. INACTIVE LAUNDERED —— 这是一个临时的状态,在这个状态下的页面已经被选择出要从主存中剔除,与此同时这个页面的内容将被保存在磁盘上。只有在INACTIVE DIRTY状态下的页面才能进入这个状态。一旦磁盘I/O操作(写磁盘操作)完成,这个页面的状态转移到INACTIVE CLEAN,在INACTIVE CLEAN 状态下,这个页面可能会被释放或者由于其他目的而被重写。如果在(写)磁盘操作期间,这个页面被访问了, 它的状态将变成ACTIVE。
5. INACTIVE CLEAN —— 这个状态下的页面已经被从内存中清除了。这意味着此页面的内容已经同步到磁盘上。从而,此页面可能会被虚拟内存释放或者由于其他目的而被重写。
图示2. 虚拟内存页面状态机
VM调优
上图充分描述了VM的工作机制, 那么它是经过怎样的调整来适应特定的工作负载?在Linux VM中有两种方法可以修改一些可调参数。第一个是sysctl接口。 这个sysctl接口是一个面向对象的编程接口, 它可以让我们的应用程序直接修改各种系统的可调参数。 sysctl非常实用,它允许管理员通过命令行为任何一个可调VM参数指定一个值。举个例子:
sysctl -w vm.max map count=65535
sysctl工具同样支持配置文件(/etc/sysctl.conf), 写入此文件中的配置可以保存起来, 下次开机的时候会自动加载. 使用这种方式, 你可以保证你的配置设置一次后, 常久有用. 配置文件语法使用键-值对的格式, 辅以一定的注释(译者注: 注释以#号开头), 一目了然, 示例如下:
#Adjust the min and max read-ahead for files vm.max-readahead=64 vm.min-readahead=32 #turn on memory over-commit vm.overcommit_memory=2 #bump up the percentage of memory in use to activate bdflush vm.bdflush="40 500 0 0 500 3000 60 20 0"还有第二种内存参数调优的方法是通过proc文件系统来实现。 这种方式里,每一个可以调整的内存参数被对应到不同的虚拟文件,然后通过系统中通用的文件读写命令来修改这些文件的内容以达到调整参数的目的。 内存相关的参数文件在/proc/sys/vm/目录下,一般都是用cat和echo命令来分别完成参数文件的读写操作。 举个例子, 执行命令 cat /proc/sys/vm/kswapd就可以查看kswapd参数的值,输出结果类似如下:
512 32 8
然后通过下面的命令可以修改这个参数:
echo 511 31 7 > /proc/sys/vm/kswapd
再使用cat /proc/sys/vm/kswapd 命令来核实一下参数是否被修改,这次的输出应该是下面这样:
511 31 7
proc文件系统接口十分便捷,有助于于我们快速调整内存参数以使系统性能达到最佳。为了方便起见,接下来的小节中将会列出/proc/sys/vm/目录里的文件所对应的各个参数及其含义。在无特殊说明的情况下这些参数适用于RHEL3 2.4.21-4版本的内核。
bdflush文件包含9个参数,其中6个是可以调整的。这些参数影响交换到硬盘的页的比率(存贮文件内容的缓冲页)。通过调整文件里的这些值,可以使需要进行大量I/O操作的系统获得更好的性能。表1,“bdflush参数”以在文件出现的顺序描述了bdflush的参数。
参数 | 描述 |
---|---|
nfract | 缓冲中脏页所占半分比。用来激活bdflush任务 |
ndirty | 每次执行bdflush时,缓存中将要写入硬盘的脏页的最大数量 |
reserved1 | 预留 |
reserved2 | 预留 |
interval | 每次bdflush之间的延迟量jiffies(以10毫秒为单位) |
age_buffer | 缓冲区被认为可以写回到硬盘的时间 |
nfract_sync | 内存中脏页占缓冲区的百分比,用来引发把脏页写入到磁盘 |
nfract_stop_bdflush | 缓冲中脏页所占半分比,这会要求bdflush停止工作 |
reserved3 | 预留 |
2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务