一引言
随着计算机技术的发展,CPU和系统主存的性能得到了极大的提高。然而由于IO设备的发展相对滞后,磁盘性能逐渐成为了影响计算机整体性能的瓶颈。对于具有频繁、随机磁盘10的数据密集型应用,如web服务器和数据库系统,磁盘访问的巨大延迟将严重影响应用程序的性能。为了提高系统10性能,提出了内存网格的概念,为共享和利用互联网中丰富的内存资源来解决内存密集型应用的性能问题提供了理论支持。本文结合已有的内存网格理论,对其关键技术进行了系统研究。
二动态函数截获技术
(一)内核模块
Linux内核是单一平面结构(monolithic),也就是说它是一个独立的大程序,所有的内核功能构件均可访问任一个内部数据结构和例程。为了方便使用同时又提供了模块化的机制,这就是内核模块。内核模块是可以按照需要动态载入内核和从内核中卸载的代码。内核模块可以不需要重起系统扩展内核的功能。对于Linux内核模块,可以用insmod或rmmod命令显式载入或卸载,或者由内核在需要时调用内核守护程序(kerneld)自动载入和卸载。内核模块运行在Linux内核空间,可以方便地使用内核提供的系统函数和变量。从模块程序链接的方式来看,它并不能自由地使用用户空间里定义的函数库,只能使用内核空间中定义的、资源受到限制的函数。内核在内核符号表中维护了一个模块的链表,每个符号表对应一个模块,模块加载进内核时正确地对其进行解释,并将模块作为内核的一部分来执行:加载进内核的模块具有所有的内核权限。模块可以在系统启动时加载到系统中,也可以在系统运行时加载:在不需要时,可以将模块动态卸载,这样就不用每次修改系统的配置时都要重新编译内核了。一般来说,内核模块可以应用于设备驱动、文件系统驱动程序、网络驱动、可执行文件解释器和系统调用等方面。
(二)函数截获
内存网格系统中利用了函数截获(APIHooking)的方法来动态改变系统函数执行流程。所谓动态改变,就是指在不修改程序源代码和不覆盖程序在磁盘的存储映像前提下,对程序行为进行修改。
函数截获(API Hooking)是获取指定执行代码控制权的基本方法[44,45]。它提供了一种在不修改程序源代码的前提下,动态改变操作系统行为的便捷方法。在众多现代操作系统中,使用函数截获方法的间谍软件(Spying Software)大量利用了系统中存在的窗口程序来部署自己的特殊应用。一般说来,使用函数截获方法具有如下优点:
1、便于API函数的监控
具备控制API函数调用的能力是非常有用的。它能够使程序开发者追踪一些发生在API调用过程中、通常对外部应用“不可见”的系统行为。这种方法主要用于特定环境下对函数参数的验证和检查。比如,在某些情况下,监控与内存相关的API调用可以有效的捕获内存资源泄漏的问题。
2、便于调试与逆向工程
基于函数截获的调试方法一直在调试技术中占据重要的地位。许多程序开发者使用函数截获方法来确定程序部件的实现和相互关联。API截获(APlinterception是获取二进制执行文件信息的重要方法。
3、便于窥视系统内部实现
许多程序开发者希望对非开源操作系统有深层次的了解。函数截获方法是一种有效的探索API内部细节的方法。
4、便于扩展函数的功能
为了能够改变或扩展模块功能,通常使用函数截获的方法对程序的正常执行流进行重新定向。比如,许多第三方软件产品有时候无法满足具体的安全要求,因此不得不对其进行一些调整。为了解决这种问题,应用程序开发者可以利用函数截获方法对原函数添加一些额外的前置和后置处理过程。这种方法对改变一些已经编译后的代码行为极为有用。
对于一个需要利用函数截获的系统来说,通常需要依据函数截获的目标和使用环境来决定具体采用何种函数截获方法。首先,我们需要考虑设计目标是只针对单一应用进行截获还是需要构建一个系统级的截获方案。比如,如果只需要监控一个特定应用,那么就不需要对一个系统级的函数进行截获。
一个截获系统(Hook System)通常由至少两个部分组成,即一个截获服务器
CHook Server)和一个截获驱动器(HookDriver)。截获驱动器主要完成实际的截获功能,截获服务器则负责将截获驱动器在特定时刻注入(inject到目标进程。截获服务器同时管理驱动器并且选择性的接受来自驱动器的信息。
三缓存数据组织与管理
(一)模型设计
内存网格系统扩展了传统的系统存储层次结构,在主存和磁盘之间添加了内存网格层。内存网格层主要由内存服务节点提供的空闲内存空间构成,内存客户节点通过利用这些空闲内存作为本地磁疵缓存的扩展,从而加速系统磁盘10的性能。但是对于内存服务节点提供的空闲内存空间,如何有效的对其进行组织和管理就成为了一个关键问题。
在基于协作缓存的改进模型中,内存网格的内存客户节点和提供其服务的内存服务节点比例为1:n。因此我们采用了基数树和LRU链相结合的方式来解决查找和定位的问题,基数树用于解决缓存数据的定位问题而存在于内存客户节点中,LRU链用于对缓存数据的更新和替换存在于内存服务节点中。
在RAM Grid原型系统的最初设计过程中,为了简化管理过程,我们规定一个内存客户节点在某一时刻有且尽可能使用一个内存服务节点的服务。为了能够对内存服务节点中的空闲内存资源进行有效管理,内存网格系统选择在内存客户节点中实现了缓存数据组织和管理模块。缓存数据组织和管理模块对缓存数据进行本地管理,管理信息包括内存客户节点所使用的内存服务节点上数据页帧的状态信息、存储位置等。为了能够达到这些目标,我们改变了模型中的缓存方案,使用了基于LRU和Hash双链的数据组织和管理方法。
LRU链和Hash链的双链管理结构在操作系统设计中由来已久。在开源操作系统Linux的早期版本中,对于数据结构的管理与组织就大量的使用了这种经典的双链结构。其中,LRU链主要对数据结构进行更新和替换:Hash链主要用于数据结构的快速查找和定位。通过双链结构的管理和组织,当内存客户节点通过本地文件系统进行磁盘10操作时,则可以利用远地内存服务节点的内存来获取数据,从而减小内存客户节点磁盘开销,提升系统性能。
(二)Cache一致性问题
在单处理器(uniprocessor)系统中,由于处理器与10子系统看到的内存视图不同而产生了Cache一致性问题。这是因为10子系统直接读写的是内存区域:处理器在写回(write-back)策略下,首先读写的是Cache,只有当读写的块要被替换时,才将该Cache中的块内容写回到内存。这就产生了处理器看到的内存视图与10子系统看到的不一样的情况。在多处理器(multiprocessors)系统中,Cache一致性问题是由对共享数据的缓存引发。在RAM Grid系统中。由于RAM Grid所扩展的缓存层次位于主存和磁盘之间,处理器并不会直接进行读写。所以RAM Grid系统并不存在Cache一致性问题。
四异步缓存写入
为了不影响系统效率,内存网格系统采用了异步缓存写入的方法来完成内存客户节点对内存服务节点的数据更新。内存网格系统使用函数截获方法来修改磁盘10的执行流程,异步写操作发生在函数截获过程中,而函数截获过程发生在核心态,所以异步缓存写入不得不选择在核心态实现。为了使写入过程不对原来的。系统执行流程造成较大的性能损失,采用了内核线程的方法来完成写入过程。在内核模块中,我们设立了一个全局缓存区用以记录内存客户节点磁盘读写函数中产生的“新”缓存数据,而使用一个内核线程来将内存客户节点中的新缓存数据周期性的写入到内存服务节点的虚拟块设各中。这样,整个问题演变成了传统操作系统中的读者/写者问题。缓存数据区的每一个块中存放着一个缓存数据结构信息。这个数据结构中包含有设备号、块号、块大小、缓存数据内容和使用标志位等元信息,这些信息主要用于缓存数据本身的管理和缓存数据在内存服务节点上数据的定位。