扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
这个结构是通用的,足以支持很多不同的设备范例。例如,如果一组“请求”要为网络数据包的接收提供缓冲,那么相应的后续“响应”就是数据包已经到达缓冲区的信号。重新排序在处理磁盘请求时是很有用的(//比如两个domain请求都要读同一块数据,那么就可以把这两个请求的处理放到一起完成,提高效率),它允许它们在Xen内部被调度以提高效率;使用带宽外缓冲的描述符使得能够容易地实现零复制传输(//减少了数据拷贝,如果不用描述符的话,那么就务必要将缓冲区的数据内容在I/O环中进行复制,而现在做的只是映射操作,即零复制)。
我们将请求和响应的产生和其它的通告分开:在请求的情况下,一个domain可以在调用一个hypercall陷入Xen之前将多个项排入队列;在响应的情况下,一个domain能够通过划定一个相应的阀值来推迟通告事件的递交。这使得每个domain能够权衡延迟和吞吐量的需求,类似在ArseNIC吉比特以太网接口的flow-aware(//流优化?)的中断分派[34]。
3.3 子系统虚拟化
前文描述的控制和数据传递机制被使用在我们对各个子系统的虚拟化实现中。在下面,我们讨论如何获得对CPU,定时器,内存,网络和磁盘的虚拟化。
3.3.1CPU调度
Xen当前对domain的调度采用的是Borrowed Virtual Time(BVT)调度算法[11]。我们之所以选择这个特别的算法是因为它是工作保养型的,同时还具有特殊的机制使得它能够在接收到一个事件时可以低延迟地唤醒(或者说分派)一个domain。快速的分派(//dispatch,就是类似前面说的demultiplex data)对于减少虚拟化对那些对时间敏感的操作系统子系统造成的影响是尤其重要的(//分派越快速,事件就越能及时到达操作系统);例如,TCP要依赖于确认信息的及时递交以正确地估测在网络上往返所用的时间(//如果确认信息没有及时到达,那么时间估测就有问题了)。BVT provides low-latency dispatch by using virtual-time warping, a mechanism which temporarily violates `ideal' fair sharing to favor recently-woken domains(//不很明白BVT算法的实现)。当然,其它调度算法也能够在我们的通用调度器抽象上实现。每个domain的调度参数可以由运行在Domain 0的管理软件进行调整。
3.3.2 时间和定时器
Xen为每个guest OS提供了真实时间,虚拟时间和挂钟时间(wall-clock time)等三个概念。真实时间是以纳秒为单位给出的,是从机器引导起来开始计算的时间,它记录的是精确的处理器执行的时钟周期数,时钟周期能够被外部时钟源锁频(例如,通过NTP时间服务)。domain的虚拟时间只是在它运行的时候才会被记入:这主要供guest OS的调度器使用来确保guest OS上的应用进程能够正确地共享时间片。最后,挂钟时间是一个偏移,用于加到当前的真实时间上。挂钟时间能够被调整(//比如在各个guest OS中的时间可以不同,有的是上午九点,有的是下午三点,改的就是这个值),同时不会影响真实时间的流逝。
每一个guest OS能够对一对警钟定时器进行编程,一个用于真实时间,另一个用于虚拟时间。guest OS能够维护内部的定时器队列(//很多应用程序都有定时需求,真实的或者虚拟的,特别是网络应用;guest OS要对这些定时需求排队以确定谁的时间要求最紧迫,然后就尽量依次满足它们;只要有一个定时需求没被满足,即超时了,那么系统就产生异常)并使用Xen提供的警钟定时器来触发最早的超时。超时事件将会使用Xen事件机制来递交。
3.3.3虚拟地址转换
和其它子系统一样,Xen也努力以尽可能小的开销实现存储访问的虚拟化。正如2.1.1中讨论的,由于x86架构使用的是硬件页表,因此达到这个目标有些难度。VMware中使用的方法是为每个guest OS提供虚拟的页表,这个页表对于存储管理单元(MMU)是不可见的[10]。然后由hypervisor负责陷入对虚拟页表的访问,确认更新,再将这些改变传播到虚拟页表和MMU可见的“影子”页表(//“影子”页表在第2部分提到过)。这大大增加了这个guest
OS操作的代价,比如创建一个新的虚拟地址空间,需要对“访问过的”和“脏的”比特位上的硬件更新进行显式地传播。
之所以完全虚拟化要强迫使用影子页表,是因为它要给出连续的物理内存的假象,而Xen就不拘泥于此。实际上,Xen只需要考虑对页表的更新,来防止guest OS导致不可接受的改变。因此我们避免了和使用影子页表相关的开销和额外的复杂度 — Xen中的方法是直接地由MMU记录guest OS的页表,并且限制住guest OS只能做读访问。页表更新通过hypercall传递给Xen;更新请求在被采纳以前经过确认,这么做就确保了安全性。
为了有助于确认,我们给机器的每个页框都建立了一个相关的类型和引用数。一个页框在任何时候都会排它地具有下述的某一个类型:页目录(PD),页表(PT),局部描述符表(LDT),全局描述符表(GDT)。同时,这个页框还可能是可写(RW)的类型。一个guest OS可以为它自己所属的页框创建可读的映射,而不必理会页框的当前类型。一个页框只有在它的引用数为0的时候,才能被安全地重新分配给其它任务使用。这个机制被用于满足系统对安全性一贯的需求;例如,一个domain不能够任意地对页表的某个部分建立可写的映射,如果要这么做的话就必须要求页框同时具有PT和RW类型(//什么情况下是可读的映射?什么情况下又是可写的映射呢?)。
类型系统还要被用于跟踪那些用于页表的页框中哪些是已经被确认过的(//页框用于页表,即页内容填入页框)。这时,guest OS要指出页框是否被分配用作页表 — 这就需要在页框的类型被定为PD或者PT后(//说明页框是用于页表的),由Xen对页框中的每一项做一次性地确认,直至后来由guest OS发出释放(unpin)的请求(//由guest OS释放页框)。这在改变页表基指针的时候是很有用的,因为它不需要在每一次上下文切换时都对新页表进行确认(//因为现在只确认页框了)。注意,一个页框在被释放并且引用数为0之前是不能够被重新分配给其它任务的,这样就避免了guest OS可能会绕开引用数机制而直接利用释放请求导致的危险。
为了减少所需的hypercall调用的次数,guest OS能够在利用一个hypercall进行整批处理之前在局部将更新排入一个队列。特别是在创建新的地址空间的时候,这一点是尤其有好处的。但是无论怎样,我们必须确保更新被提交得足够早以保证准确性。幸运的是,一个guest OS在第一次使用一个新的映射前要执行一次TLB刷新:这确保了任何被缓存的改变(//这些改变目前仅存于TLB中,还没有被写入内存页表项)都是没有经过确认的。因此,在TLB刷新之前立即提交那些待处理的更新就可以满足正确性(//更新的时间,解决了什么时候进行成批更新操作以保证正确性的问题)。可是,有一些guest OS会在TLB中没有陈旧表项存在(//没有陈旧表项意味着更新都已经送达内存中?)的时候省略掉这个刷新的步骤。在这种情况下,就有可能发生第一次试图使用新的映射时会发生缺页错误(//因为没有刷新TLB)。 这时,guest OS错误句柄就必须要核查是否需要更新;如果需要的话,TLB就要被刷新(//加入所缺的内容),并且将导致刚才缺页错误的指令重新执行。
3.3.4物理内存
为各个domain进行的初始的内存分配,或者说内存保留,都是在domain被创建的时候进行的;因此,内存在domain之间是静态划分的,这就提供了强大的隔离性。最大允许的内存保留量是被规定了的:如果某个domain中的内存压力增加了,那么它就可以从Xen争取额外的内存页,直到达到规定保留量的上限。反过来,如果一个domain希望节省些资源,避免不必要的开销,它也能够通过释放内存页给Xen来减少它的内存保留量。
XenoLinux实现了一个气球驱动 [42](//没看相关文献),它能够通过在Xen和XenoLinux的页面分配器之间传递内存页来调整domain的内存使用。虽然我们能够直接修改Linux的内存管理例程,但是气球驱动能够利用现有的操作系统功能来进行调整,因此简化了Linux的移植工作。无论怎样,准虚拟化能够被用于扩展气球驱动的能力。例如,guest OS中的内存越界处理机制能够被修改为通过向Xen请求更多的内存页来自动地减轻内存压力。
大部分操作系统都假定内存空间是由大量的较大规模的连续区域组成的。由于Xen并不保证它为guest OS分配的内存区域是连续的,所以guest OS需要为它们自己创建连续的物理内存的假象,即使它们实际在底层分配到的硬件内存是稀疏的。guest OS通过简单地维护一个以实际分得的页框号为索引的数组,来完全负责从实际分得的地址(physical)到硬件地址的映射。Xen提供了一个共享的地址转换数组,这个数组用于支持从硬件地址到各个domain实际分得地址的映射,它是所有的domain都可读的 — 更新这个数组是经过Xen确认的,以确保guest OS能够拥有相应的硬件页框。
即使一个guest OS在大多数情况下选择了忽略硬件地址(//因为guest OS喜欢在连续的地址空间上运行),但是在它访问它所拥有的页表的时候(这时必须使用硬件地址)必须使用转换表。硬件地址也可以暴露给操作系统存储管理系统中的有限的某些部分以优化存储访问(//具体是哪些部分可以直接“看到”硬件地址呢?)。例如,一个guest OS可以分配特定的硬件页用于优化以实际分得地址为索引的缓存中的布局,或者使用超级页将硬件内存中的连续部分作自然对齐的映射。
3.3.5网络
Xen提供了虚拟防火墙—路由器(VFR)的抽象,每个domain都有一个或多个在逻辑上附属于VFR的网络接口(VIF)。VIF看上去有些现代的网卡:具有两个I/O缓冲区描述符环,一个用于发送一个用于接收。在每个方向(//发送或者接收)上都有一些相关的规则形式(<模式>,<动作>)— 如果模式(pattern)匹配上的话,那么相关的动作(action)就会被起用。
Domain 0负责插入和删除规则。例如在典型的情况下,可以增加规则防止源IP地址欺骗,或者增加规则确保基于目的IP地址和端口的正确的数据分派(demultiplexing)。规则也可以是和VFR上的硬件接口相关的。特别是我们增加的那些用于执行传统的防火墙功能的规则,比如防止在不安全的端口上建立连接的企图。
为了发送一个数据包,guest OS简单地将缓冲区描述符排队进发送环中。Xen复制描述符,同时为了确保安全性,还要复制数据包头并执行匹配过滤规则。数据包的有效载荷不会被复制,因为我们使用的是分散—集中式(scatter-gather)的DMA;这里要注意的是,相应的页框必须被绑定,直到数据包传送完成。为了保证公平性,Xen在数据包调度器上实现了简单的循环轮转(round-robin)算法。
为了有效地实现数据包的接收,我们需要guest OS为每一个它接收到的数据包交换一个没有使用的页框(//这个招儿挺绝的,我就不将页内容复制到我自己的页框里了,直接把这个页框纳入自己的旗下,然后再换个我没用的空页框给你,反正只要修改地址转换数组就可以了);这就避免了需要在Xen和guest OS之间复制数据包内容的麻烦,但是这么做必须要求在网络接口中接收缓冲队列中的有页对齐的区域(//因为这么做是直接以页框为单位进行的,所以一次交换就是一页的内容,所以需要提供页对齐的缓冲区域才可以进行)。当一个数据包被接收,Xen立即检查接收规则组来确定目的VIF,然后将数据包缓冲区和相应的接收环(//接收环存在于各个domain中)交换一个页框。如果这时没有页框可用的话,数据包就被丢弃。
3.3.6 磁盘
只有Domain 0能够不经检查地直接访问物理(IDE和SCSI)磁盘。所有其它的domain访问永久性存储介质的时候,都要通过虚拟块设备(VBD)抽象。这个抽象是由运行在Domain 0中的管理软件来创建和配置的。由Domain 0管理VBD使得Xen中的机制比较简单,不避使用更复杂的解决方案(比如Exokernel中使用的UDF[23])。
一个VBD是由一些和所有权以及访问控制信息相关的扩展组成的,可以通过I/O环机制来访问。一个典型的guest OS磁盘调度算法将重新排定请求的优先级并把它们排进环中,这样做是为了尽量减少响应时间,并且可以有区别地进行服务(例如,由于进行投机的向前读请求的代价很高,调度器可能会选择主动先去调度后面的对同步数据的访问请求)。另外,因为Xen对真实的磁盘规划具有更完整的认识,所以我们也要在Xen中支持重新定序(//刚刚提到的是guest OS),并且可以乱序地返回响应。因此,对于guest OS来说,VBD有一点像SCSI磁盘。
在hypervisor内部还为每个VBD维护了一个转换表;整个表中的内容都由Domain 0通过特权控制接口进行安装和管理。在接收到一个磁盘请求后,Xen检查VBD标识符和偏移,并且产生相应的扇区地址和所属物理设备。许可检查也是在这个时候进行的(//检查到底能不能进行这个磁盘操作)。零复制数据传递将以DMA的方式在磁盘和绑定到该发出请求的domain上的内存页之间进行。
Xen会使用简单的循环轮转方式来成批处理各个存在竞争关系的domain发出的请求;然后,在到达磁盘硬件之前,这些请求传给标准的电梯调度器。domain可以显式地设置重定序障碍(//不许重定序)来防止在必须维持高层次语义的时候(例如,在使用了一个预写日志(write-ahead log)的时候)进行重定序。当成批的请求中能够显现出访问的公平性的时候,低层的调度能够给我们带来很好的吞吐量。下一步工作将研究使用现有的技术和调度器提供更可预测的隔离性和支持更有差别的设备。
3.4建立新的Domain
为一个新的domain建立初始的guest OS结构,这个任务很大程度上是委托给Domain 0完成的。Domain 0使用它的特权控制接口(2.3段)访问新的domain的存储空间并告知Xen该新domain的初始寄存器状态。这个方法相对于由Xen建立整个domain来说有一些优势,包括减少了hypervisor的复杂度,改进了鲁棒性(对特权接口的访问要经过完全地检查的,使得我们能够在初始阶段捕捉到大部分的bug。
最重要的是,整个建立过程易于被扩展,可以应付新的guest OS。例如,Linux内核引导时的地址空间是要比Windows XP简单得多的。我们可以为所有的guest OS指定一个固定的初始内存规划,但是这样的话就需要针对每个guest OS编写额外的引导陷阱代码用来安置操作系统所需的其它部分。不幸的是,这类代码是非常难以实现的;为了获得简单性和鲁棒性,更好的实现方法就是使用Domain 0,它能够提供比引导程序更充裕的诊断和调试支持。
Scatter-gather DMA方式是与block DMA方式相对应的一种DMA方式。
在DMA传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但是在某些计算机体系中,如IA架构,连续的存储器地址在物理上不一定是连续的,所以DMA传输要分成多次完成。
如果在传输完一块物理上连续的数据后引起一次中断,然后再由主机进行下一块物理上连续的数据传输,那么这种方式就为block DMA方式。Scatter-gather DMA方式则不同,它使用一个链表描述物理上不连续的存储空间,然后把链表首地址告诉DMA master。DMA master在传输完一块物理连续的数据后,不用发起中断,而是根据链表来传输下一块物理上连续的数据,直到传输完毕后再发起一次中断。
很显然,scatter-gather DMA方式比block DMA方式效率高。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者