课程简介:.docx

上传人:夺命阿水 文档编号:100552 上传时间:2022-12-12 格式:DOCX 页数:13 大小:29.67KB
返回 下载 相关 举报
课程简介:.docx_第1页
第1页 / 共13页
课程简介:.docx_第2页
第2页 / 共13页
课程简介:.docx_第3页
第3页 / 共13页
课程简介:.docx_第4页
第4页 / 共13页
课程简介:.docx_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《课程简介:.docx》由会员分享,可在线阅读,更多相关《课程简介:.docx(13页珍藏版)》请在课桌文档上搜索。

1、端节点算法学端节点算法学是网络算法学在端节点尤其是服务器上的运用。我们可以认为 它是建立高速服务器的一组系统性技术。随着网络功能虚拟化的提出,将来数据中心绝大部分的网络设备都会在通用 服务器上实现,因此端节点算法学的重要性就更突出了。消除不必要的拷贝如果我们跟踪一个网络包从进入网卡到完成处理的整个过程,会发现网络包 在终端中被拷贝了多次,具体场景稍后会看到。为什么我们要把数据拷贝这个问题单独提出来讨论,是因为在计算机中进行 拷贝会消耗两个非常宝贵的资源:内存带宽和内存本身。内存带宽反映了系统读写数据的能力,而访存又是现代计算机系统最大的性 能瓶颈。粗略地说,如果处理一个报文要被拷贝k次,那么系

2、统的吞吐量就可能 降至l/ko计算机内存的容量是确定的,如果一个报文在内存中保存k份,就会降低内 存的有效容量,使得数据在内存和磁盘之间交互的几率增大,访存性能降低。所以说数据拷贝是影响计算机系统性能的重要因素。本章关注如何消除不必 要的拷贝(Pl一个拷贝如果不是由硬件要求的,该拷贝是不必要的。比如, 网络适配器将收到的数据拷贝到计算机内存,这是硬件要求的;然而,数据在内 存之间搬来搬去,比如应用程序和操作系统之间的拷贝,主要是由操作系统的结 构化产生的要求,除此之外没有什么特别的理由,这个拷贝是可以考虑消除的。除了拷贝之外,这一章还将讨论其它影响内存使用效率的操作。5.1 为什么要拷贝数据我

3、们首先看看在终端中数据的多次拷贝是怎么发生的。考虑用户向Web服务 器请求一个静态文件,该文件在磁盘上;Web服务器从磁盘中读出文件,发送到 网络上。我们关注在此过程中,数据在Web服务器中的转移过程。图5.2给出了涉及到的计算机系统的主要部件和拷贝过程。涉及到的主要硬 件为CPU、存储器、内存总线、南桥(10 HUb)、I/O总线、硬盘和网卡,主要 软件为web应用程序和内核程序。这里涉及到两个主要的内核子系统,文件子 系统和网络子系统。文件子系统负责文件管理,网络子系统负责报文收发和协议 处理。为简单起见,图中只画出了一个CPU (许多服务器有多个处理器),并且只 关注对静态内容的请求,即

4、文件直接从磁盘中读出。如果请求的是动态内容, Web服务器后面还需要一个数据库,由服务器侧的一个CGI程序处理请求,从 数据库中读数据,构造网页,再传给Web服务器。注意,图中将文件子系统和网络子系统的代码画在处理器中。实际上,这些 代码也存放在主存中,由处理器从主存中获取。然而,位于处理器指令CaChe中 的代码可以看成是在处理器中的。一个简单的故事直观上,这个故事是很简单的。首先web应用程序通过一个系统调用(如 read()将文件从磁盘读入到它的缓冲区(应用缓冲区);然后构造一个HTTP响 应头,通过一个系统调用(如write()将响应头和缓冲区内容交给网络子系统(TCP/IP程序);网

5、络子系统将数据划分成适当大小的块,加上各层协议头后交 给网络适配器发送。如果讲原理,一般就讲到这个程度。一个真实的故事实际上,这个故事的细节要复杂得多。首先,文件一般先从硬盘读入到一个 称为文件缓冲区的内核空间,我们称COPy 1。这个做法是值得提倡的,这样随后 对该文件的请求就可以直接从内存得到,而不需要每次都去读硬盘。所有的Web 服务器都是这么做的,这样可以极大地减小响应时间。其次,文件数据从文件缓冲区拷贝到Web程序的应用缓冲区,我们称copy 2。接着,Web程序对一个TCP套接字执行一个Write()系统调用。在Writeo调用 中描述的应用缓冲区被拷贝到一个套接字缓冲区,这是内核

6、中不同于文件缓冲区 和应用缓冲区的另一个缓冲区,这称为COPy 3。最后,每个数据块在加上了各层协议头之后,由驱动程序将数据包拷贝到位 于网卡的缓冲区中,发送出去。这称为COPy 4。在传输到网络之前,TCP程序还要扫描一遍数据,计算TCP检查和。以上过程涉及到4次拷贝和1次涉及全部包数据的检查和计算。资源消耗情况我们看一下资源消耗情况。不同内存区域之间的拷贝(COPy 2和COPy 3)需要CPU的参与,每个字都要 通过内存总线读一次和写一次,是消耗资源最多的。计算TCP检查和需对每个字读一次,最后写一个简单结果,因此使用内存总 线一次。涉及外设的拷贝(COPyI和CoPy 4),如果由CP

7、U来做拷贝(称程序输入输 出),其代价与内存区域之间的拷贝一样,每个字都要通过内存总线读、写各一 次。如果由设备来做拷贝(DMA),则每个字只需通过内存总线读一次或写一次。涉及外设的拷贝还会消耗I/O总线带宽(copy 1和COPy 4)。对服务器吞吐量的影响在Web服务器的例子中,即使使用了 DMA,每一个字也要使用内存总线7 次。因此,Web服务器的吞吐量不会超过T/7,其中T为内存速度和内存总线速 度中的较小值。其次,额外的拷贝消耗了内存。在图中,一个文件被存储在文件缓冲区、应 用缓冲区和套接字缓冲区。通常web服务器希望使用尽可能多的文件缓冲区来 避免频繁地访问硬盘。一个文件存在3处,

8、将减少文件缓冲区可用的容量,降低 缓存命中率,从而极大地降低整个服务器的性能。总而言之,多余的拷贝在两个重要的方面损害了服务器的性能:1)由于使 用了过多的总线和内存带宽,使得服务器的运行速度低于总线速度;2)由于使 用了过多的内存,使得服务器不得不大量地去从磁盘读文件,而不是从主存中读 文件。需要注意的是,我们只描述了获取静态内容的情形,实际上相当一部分请求 是关于动态内容的。动态内容通常由一个专门的CGI程序生成,然后通过某种 进程间通信机制交给Web服务程序去发送,这里又会涉及到一次拷贝。下面我们将讨论这些拷贝,哪些是可以避免的,怎么避免。5.2 利用适配器内存消除COPy 4我们首先关

9、注与网络相关的拷贝,即CoPy 4。为什么需要COPy 4? 一个简单的解释是,因为适配器内存位于适配器上,而 内核存储空间位于存储子系统,不在同一个硬件上。但是这个理由并不充分。我们知道在一个内存映射的体系结构中,设备的寄 存器被映射到一块内存区域,CPU通过读写这块内存区域与设备通信。因此从 理论上说,在一个内存映射的体系结构中,内存可以位于总线上的任何地方。所以,尽管内核存储空间通常是位于存储子系统的,但没有理由认为部分内 核空间就不能位于网络适配器中(网络适配器通常包含一定容量的存储器)。利用网络适配器中已有的存储空间(P4)和内核存储空间放置的自由度 (P13),我们可以将套接字缓冲

10、区放在网络适配器中。这样应用缓冲区的内容直 接拷贝到网络适配器的内存中。如何计算检查和?但是这里有一个问题,网络子系统在收发TCP包时需要计算TCP检查和, 如果数据在应用缓冲区和适配器之间直接传递,那么TCP检查和怎么计算呢?这里的一个想法是运用P2c (共享开销)。数据从应用缓冲区拷贝到适配器上 的缓冲区,假如这个拷贝由CPU完成,那么可以让CPU捎带着同时计算检查和。 比如,使用一个寄存器,对已经传输的数据进行累加。但是这个方法有个致命的 问题,就是在接收数据包的时候,CPU边拷贝数据边计算检查和,当发现数据 接收错误时,数据已经进入到应用缓冲区中,这是违背TCP语义的。所以这个 方法从

11、未被实施。后来这个方法被HP实验室的研究人员采纳,应用到他们的网络适配器(称 为Afterburner)中。数据传输不是由CPU完成,而是由网卡通过DMA完成。 由于CPU不参与DMA过程,因此检查和计算是由网卡完成的。这种网卡称为TCP卸载引擎,它将TCP的数据传输下放到网卡上完成,以 减轻CPU的负担。需要注意的是,考虑到TCP连接管理的复杂性,TCP连接的 管理(建立、关闭等)仍由主CPU完成,只是将建立好的TCP连接移交给网络 适配器。这种方法的问题是,网络适配器需要很大的内存空间和较强的处理器来支持 大量的TCP连接,网卡成本很高。5.3 消除 copy 3下面考虑消除COPy 3。

12、注意,我们现在是独立地考虑每一种数据拷贝的消除 方法,这些措施能否组合使用需要仔细考虑。应用和内核之间需要一次拷贝有两个原因。(1)应用和内核使用不同的虚拟 地址空间,需要通过拷贝传递数据。但是这个理由不是充分的,因为完全可以通 过内存映射,将套接字缓冲区映射给应用程序使用。(2) SOCketAPl使用拷贝语 义,应用程序在将缓冲区的内容写入套接字(交给网络子系统)后,它可以往缓 冲区中写入新的数据。但是TCP通常需要缓存数据以备重发,因此TCP不能与 应用共用一块缓冲区,需要通过数据拷贝来解除应用和内核之间的耦合。第2个理由使得拷贝看起来是必要的。如果不能避免拷贝,那我们就考虑能 否减小拷

13、贝的开销?我们可以换个角度想,如果应用不需要立即写缓冲区,或者 很多情况下不需要立即写缓冲区,那么其实应用和内核共用一块缓冲区是安全 的;仅当应用需要写缓冲区、而缓冲区中的数据还没有发走时,这时候才会出问 题。那我们能否等到这种情况发生时才去执行拷贝呢?这就是写时拷贝。写时拷贝有些操作系统(如Mac)提供写时拷贝(copy-on-write)功能,在很多情况 下可以避免实际的拷贝。写时拷贝的实现写时拷贝的基础是虚拟内存。我们用一个例子说明写时拷贝的实现。假定进 程Pl的虚拟页X映射到物理页L,需要复制X的内容到进程P2的虚拟页Yo写时拷贝(续):不幸的是,许多操作系统(如UNIX和WindoW

14、S)不提供写时拷贝。但是理 解了写时拷贝的原理之后,类似的效果是可以实现的。因为现代计算机都使用虚拟内存,通过修改页表来避免物理拷贝是能够做到 的,只是需要找到一种替代CoW位的保护机制。(COW保护机制采用硬件检测、 触发操作系统异常来解决)5.4 优化页面重映射写时拷贝的例子忽略了很多细节,以致于让人觉得页面重映射是很简单的一 个操作,一个大的缓冲区通过一个写页表操作就可以从应用程序传递到内核(或 反过来)。比如在图5.5中,假定操作系统希望将进程Pl的虚拟页10 (VT 10)拷贝到 进程P2 (如内核)的虚拟页8 (VT8),其中VTlO指出一个存放包数据的物理 页。过于简单的想法是只

15、需要修改VT 8的页表表项,使其指向存放包数据的物 理页就可以了。这种过于简单的看法是很误导人的。页面重映射的开销有好几个额外的开销被这种简单的描述给掩盖了。(1)多级页表大多数现代的计算机系统使用多级页表映射(至少有两级),实际映射时可 能要求修改多级页表,由此可能涉及多次写。为了实现跨平台运行,有些操作系统同时维护了机器无关页表和机器相关页 表,则涉及的写操作更多。当页表不在内存中时,还需要在内存中为页表分配一个物理页帧,将页表调 入物理页帧中,为此还需要修改目录页。(若使用32位的虚拟地址和4KB的页,一个进程可以有约一百万个页,所以页 表也要分页。32位机器一般使用两级页表:每1024

16、个页表项组成一个页表,这 些页表的起始地址(即页表所在的物理页帧号)保存在一个目录页中;目录页有 1024个表项,目录页的起始地址保存在一个全局寄存器中。虚拟地址的前10位 用于查找目录页,得到页表的地址;虚拟地址的中间10位用于查找页表,得到 虚拟页的物理页帧号。当包含虚拟页页号的页表已经在内存中时,页面映射只需 修改对应的页表项;当页表不在内存中时,还需要在内存中为页表分配一个物理 页帧,将页表调入物理页帧中,为此还需要修改目录页。)(2)要求锁页表是共享资源,修改页表必须用锁保护,因此修改页表前后要有请求锁和 释放锁的开销。(3)刷新TLB为节省地址转换时间,常用的页表映射缓存在TLB中

17、。当一个新的虚拟页 的地址映射写入页表时,相关的TLB表项要清除或修正。(TLB只包括最近用到 的虚拟页号,指令和数据页分开记录。TLB分别保存64个最常用的指令和数据 的虚拟页号,每个TLB项包括一个虚拟页号和相应的物理页帧号。)(4)在目标域中分配虚拟内存需要为目标进程分配一个新的虚拟页(malloc)。新的虚拟页需要向系统去申 请,为此,系统需要做一些计算在目标进程中找到一个空闲的页表表项。(5)锁住相应的页物理页可能会被换出到磁盘上。为防止页被换出,页必须被锁住,这也需要 开销。所有这些开销在多处理器系统中会被放大(以上不少操作都需要加锁,如 malloc)o其结果是,尽管页表映射看起

18、来非常好,即无论包大小如何,页面重 映射只需常数时间,但这个常数因子实际上是一个很大的开销(Q4),有研究人 员在90年代早期的实验中证实了这一点。也就是说,如果只是简单地使用页表 重映射来避免拷贝,结果可能不像预期的那么好。那么怎么办?数据拷贝开销很大,但页面重映射看起来开销也不小,搞不好 比拷贝一个数据包的开销还要大。那是不是说页面重映射就不能用了呢?不能一 概而论,一次页面重映射的开销很大,有没有办法分摊这个开销呢?研究人员发明了一个称为fbufs (fast buffers的简称)的操作系统设施,可以 消除前4种页面映射开销的全部或大部分。Fbufs (fast buffers)Fbu

19、fs基于这样的观察:如果一个应用正在通过内核向网络发送大量的数据 包,那么一个缓冲区可能会被重用多次。方法一:提前分配好需要的包缓冲区,并计算好所有的页面映射信息(P2a), 发送时重复使用这些包缓冲区。问题是不好预先估计所要的缓冲区数量。方法二:当数据传输开始时才按需分配包缓冲区并计算页面映射(P2b),然 后将其缓存起来(PHa)供后续数据包使用。尽管前几个包的延迟较大,但后续 的包就免除了页面映射的开销。不管哪一种方法,都可以做到“映射一次,重复使用二当需要传输的数据 量很大时,页面映射的开销被平摊,可以认为开销为零。为应用分配一组固定的物理页为避免用户空间和内核空间之间的拷贝,将一组物

20、理页Pl、P2同时映射到内核和应用的页表中,供它们使用。考虑到端系统上可能同时运行多个应用。为进行安全隔离,设计者将数据包 经过的一系列处理程序(一个有序的安全域序列)定义为一条路径,并为每一条 路径预留不同的一组物理页。图5.6是一个路径的例子,以太网软件实现为一个内核级的驱动,TCP/IP栈 实现为一个内核级的安全域,Web应用实现在应用层。图中存在两条接收路径: (以太网,TCP/IP, web),(以太网,OSI, FTP)o将一定数量的物理页映射给 接收路径(以太网、TCP/IP和Web应用),再将另一组物理页映射给另一条接收 路径。这样,每一个应用使用一组固定的物理页。为使该设计发

21、挥作用,当一个数据包到达时,最底层的驱动(甚至是网络适 配器)必须立即判断该数据包要被映射到哪条路径,从而将数据包拷贝到分配给 这条路径的物理页中,这个功能称为提前解复用(early demultiplexing)o在图5.6 中,这是通过检查数据包的所有头部来确定的,比如,具有以太帧头、IP头、 TCP头和端口 80的包属于路径Io映射到同一个物理页的虚拟页号相同在前面的例子中,在同一条路径的进程间传递的是缓冲区描述符。想一想, 在进程间传递缓冲区描述符会有什么问题?理论上,各个进程映射到同一个物理页上的虚拟页号可能不同。比如,第一 个进程的虚拟页10可能和第二个进程的虚拟页8映射到同一个物

22、理页上。这是 很麻烦的,因为第二个进程读入VTlO的描述符后,它必须要知道它对应了自己 的虚拟页8o为避免这种情况,设计者规定映射到同一个物理页的虚拟页号必须相同。为 此,系统将所有进程的虚拟内存中一定数量的起始页预留为fbuf页。收包处理过程图中是只有两个安全域的一条路径。进程I(Writer)收到数据包后,从free fbufs队列取一个空闲缓冲区的描述符,将数据包写入该空闲缓冲区,然后将该 缓冲区的描述符写入written fbufs队列;进程2从written fbufs队列中取包缓冲 区描述符,从相应的包缓冲区中读数据处理,将释放的包缓冲区描述符写回到 free fbufs 队列。到

23、目前为止,接收路径的问题解决了。网卡驱动将数据包装入一个空闲的包 缓冲区,包缓冲区的描述符沿着接收路径传递,每个进程访问同一个物理内存页 进行数据包处理,数据包处理完后,包描述符归还给驱动程序进行重用,在此过 程中没有发生物理拷贝。那么发送路径呢?如何添加包头?在发送路径上,每一个安全域都要给数据包加上一个包头。然而为了实现安 全保护,我们对于每条路径只允许一个写者,其余为读者,这意味着页只能由写 者来修改。那么,怎么允许其它进程添加包头呢?应用如何使用fbufs?现在还剩下一个重要的问题。大量已有的应用软件是使用标准拷贝语义的 SoCketAPl书写的,即应用执行了 Write()系统调用后

24、,就可以重用包缓冲区甚至 释放了。然而使用了 fbufs之后,在包缓冲区被其它进程使用完之前,应用是不 允许写或释放包缓冲区的。这个问题怎么解决呢?修改应用API该问题最终的解决方案是:APl不再保持拷贝语义,应用必须判断能不能写 fbufso当一个fbuf被交给内核后,应用不能写这个fbuf,直至其被归还到空闲 链表中。为防止出错或恶意的代码,当一个fbuf从应用转递到内核后,内核翻转一个 写允许比特,归还fbuf时再重新设置该位。如果应用在不允许写的情况下做了 一次写操作,则会产生一个异常,应用崩溃,但不会影响其它的进程。已有的网络应用软件必须重写吗?改变APl听起来是一个很大的改动,这是

25、否意味着大量已有的网络应用软件 必须重写呢?这显然是不可能的,这里有几种解决方案。方法一:已有的APl增加新的系统调用。比如,增广了 fbufs的SOlariS除了 标准的Write()外,还增加了一个uf_writer()调用。要求高性能的应用可以用新的 调用进行重写。方法二:用新的扩展实现一个公共的I/O库。其实,这个问题最终的考虑不是APl是否要变,而是修改应用程序来使用该 APl有多难。实践表明,将应用移植到类fbuf的API,对应用所做的修改不大, 且是局部的。也就是说,FbUfS方案是可行的。(可行性很重要,一个再好的方案 如果推广起来难度大,也没有人使用)5.5 使用RDMA避免

26、拷贝在前面考虑的Web服务器场景中,Web服务器接收请求,然后将文件传输到 网络上,Web服务器作为接收端并不需要保存请求消息。下面考虑在两个终端之 间传输一个大文件,接收端需要保存收到的包。采用fbufs收包假如采用fbufs,包到达网卡后被拷贝到一个包缓冲区,包缓冲区描述符在路 径上传递,被各个安全域处理,在此过程中无数据拷贝。最后应用进程将数据拷 贝到其应用缓冲区中,释放包缓冲区。为什么要拷贝?因为文件内容需要保存或被应用处理,而包缓冲区要被重 用。(http请求消息不需要保存,但是文件内容是需要保存的)采用Tc)E网卡收包假设采用TOE网卡。包到达网卡后,被放入相应的套接字缓冲区进行协

27、议 处理和重组。DMA引擎将数据送入应用缓冲区,向CPU发出中断。网卡驱动程 序通知应用接收数据,应用将数据拷贝到文件缓冲区,将应用缓冲区归还给网卡 使用。这里进行拷贝是为了归还应用缓冲区,为什么要重用应用缓冲区而不是重新 分配一个呢?因为准备DMA缓冲区是一种耗时的操作,内核需要将应用缓冲区 映射到连续的物理页上,将缓冲区的虚拟地址翻译成能够被DMA使用的总线地 址,然后将地址传给网卡。将一大块应用缓冲区映射到连续的物理页上,并不总 是可以做到的,所以DMA缓冲区在初始化时准备好,然后重复使用。由于给每个套接字分配的缓冲区是有限的,在传输一个大文件的过程中,需 要多次中断CPU来接收数据和拷

28、贝数据。远程直接内存访问RDMA通过网络进行DMA的愿景称RDMA。其意图是也就是说,数据一次传输到位,不需要CPU再去移动数据。RDMA要解决的问题毫无疑问,网络适配器应卸载全部的协议处理,TOE网卡已经可以做到这一 点。除此之外,还有两个问题需要解决:(1)接收端适配器如何知道应将数据放 在哪儿?它不能向主机求助,否则就违背了这个意图;(2)如何保证安全,因为 恶意的数据包可能会覆盖关键的内存区域。5.5.1 VAX 的 RDMARDMA在VAX集群中就已经被使用。计算机集群就是用一批廉价的计算机 来代替昂贵的大型机,许多Web服务器实际上就是服务器集群。VAX系统的核心是一个140Mbs

29、的网络,称为计算机互联(Computer Interconnect, CL),使用一个以太网风格的协议。用户可以将许多VAX计算机 和网络硬盘连接到CL上。VAX的RDMA解决方案接收方应用进程锁住一些物理页,用作文件传输的目标存储区域,但呈现出 来的逻辑视图是由一些地址连续的虚拟页组成的一个缓冲区。然后,缓冲区ID 被传送到发送端应用。发送端应用将缓冲区ID及包存放的偏移量随同数据包一起发送。数据包到达后,接收端适配器根据缓冲区ID和偏移量,将数据包内容拷贝 到正确的位置。包全部到达后,接收端不需要进行任何的页面重映射。5.5.2 RDMA在SAN中的应用VAX集群引入了一个非常早期的存储区

30、域网络。存储区域网络是一个后端 网络,它将大量计算机和网络硬盘连接在一起。现在Infiniband在数据中心非常火,因为数据中心的节点之间需要传输大量 数据。尤其是现在数据中心部署了很多的分布式机器学习应用,如进行深度神经 网络的训练,需要高吞吐低延迟的通信支持。总之,如果在两台计算机之间需要传输大量的数据,采用RDMA是非常高 效的方法,它可以节省两侧CPU的时间,并且数据存放一次到位,没有多余的 拷贝。5.6 把避免拷贝技术扩展到文件系统前面我们讨论了如何消除与网络通信有关的拷贝,现在我们重新回到Web服 务器应用场景,考虑文件系统中的拷贝。为了提高响应速度,服务器通常将请求的文件先读入一

31、个文件缓冲区(COPy 1),然后从文件缓冲区拷贝到应用缓冲区(COPy 2),这是必要的,因此COPy 1 不在我们的考虑范围内。本节的主要目的是消除copy 2o我们把之前讨论的避免拷贝技术扩展到文件 系统,介绍三种消除文件系统冗余拷贝的技术。5.6.1 共享内存方法有了前面通过页面重映射消除拷贝的经验(fbufs),我们自然想到是不是也 能用内存映射的方法消除copy 2呢?类UNlX操作系统提供一个方便的系统调用,称为mm叩0,它允许应用(如 Web服务器)将一个文件映射到它的虚拟内存地址空间。其它操作系统也提供类 似的功能。概念上,当一个文件被映射到一个应用的地址空间,这个应用就好像

32、在它的 内存中缓存了这份文件。实际上,这个缓存的文件只是一组映射。也就是说,如 果Web程序将文件映射到了自己的地址空间,则它和文件cache访问的是同一组 物理页,从而免除了拷贝的需要。Flash Web服务器我们看一下FIaShWeb服务器的做法。为加快响应速度,Web服务器将经常 用到的文映射到自己的内存空间。注意,受到可分配给文件页的物理页数量及页表映射的限制,FlaShWeb服 务器只能缓存和映射最近常用的文件,不能用来长期存放大量的文件。事实上,FlaShWeb服务器并没有缓存整个文件,只是缓存了一些文件分片, 并使用LRU (Least Recently Used)策略将最近一段

33、时间未用的文件取消映射。只缓存一些文件分片有什么意义呢?可以提高响应速度。如果要访问的文件 有一些分片在缓存中(通常是头几个分片),那么这些分片就立即可以发出去, 然后再将其余的分片映射进来。尚未解决的问题Flash Web没有消除CGI程序和web服务器之间的拷贝。文件缓存只能缓存 静态内容,动态网页要由CGI程序生成。CGI程序生成的动态内容通过UNIX 管道传给Web服务器;典型地,管道要在两个地址空间之间拷贝内容。其次,到目前为止我们的方案都没有涉及TCP检查和。假如同一个文件不断 地被CaChe命中,那么返回的文件分片对所有请求都是一样的,为什么TCP检 查和不能缓存呢?当然,这里的

34、检查和是指数据部分的检查和,TCP头是不同的, 但是检查和可以增量计算。然而,这需要一个能够从数据包内容映射到检查和的高速缓存,这在传统缓 存方案中是很低效的。高速缓存实际上是一个a,f(a)数组,这里a为数据包 内容,f(a)为检查和。可以结合fbufs和mmap吗?我们介绍了 fbufs,可以消除应用-网络之间的多余拷贝;我们介绍了 mmap, 可以消除文件系统的冗余拷贝。那么把fbufs和mmap结合起来,是不是就能消 除 copy2 和 copy3 了呢?如果采用fbufs,所有进程的虚拟内存中一定数量的起始页预留为fbuf页, 应用进程的应用缓冲区不能被映射到这些物理页上。因此,如果

35、应用将文件映射 到其虚拟地址空间的一个缓冲区,那么这个缓冲区不能用fbuf发送,必须要有 一次物理拷贝!5.6.2 IO-Lite那么我们能够同时消除copy2和copy3吗?这里我们介绍一个称为IO-Lite 的方案,它可以一揽子解决前面所有的问题,它可以消除CoPy2和COPy3,可以 消除CGI程序和Web服务器之间的拷贝,还可以缓存之前传送过的数据包的检 查和。由于fbufs和mmap不能同时使用,所以IO-Lite扩展fbufs到文件系统,使 得可以不必使用mmapoIO-Iite响应get请求的步骤图5.10给出了响应GET请求的步骤。当文件第一次从磁盘读入文件系统的 高速缓存时,

36、文件页被保存为IO-LitebUffer。当应用通过一个调用来读文件时,没有进行物理拷贝,而是创建了一个缓冲 区聚合体(以方便添加包头),指针指向IO-LitebUffer。当应用发送文件给TCP进行传输时,网络系统得到一个指向相同IO-Lite页 的指针。为防止出错,IO_Lite系统为每个缓冲区维护一个引用计数器,仅当所有用 户都使用完后才重新分配缓冲区。图5.10还给出了另外两个优化。应用将常用文件的HTTP响应维护在一个高 速缓存中,通常只需简单地加上做了最小修改的标准响应。其次,10-Lite给每个缓冲区分配一个唯一的编号(P12,增加冗余状态), TCP模块维护一个以缓冲区编号为索

37、引的检查和高速缓冲。当一个文件被传输多 次时,TCP模块在第一次计算了检查和之后,就可以避免再计算检查和。注意到这些改变消除了图5.2中的copy2和copy3,加快了响应的处理。实现 IO-Lite尽管IO-Lite的核心思想很简单,但要与文件系统集成需要解决一些困难的 问题。首先,IO-Lite必须处理复杂的共享模式,。其次,IO-Lite页既可能是虚拟内存页又可能是文件页,.第三,需要找到一种干净的方法将IO-Lite集成到操作系统中,避免对OS 进行外科手术式的大改动。IO-Lite已经在UNIX中实现了。5.6.3 使用I/O拼接避免文件系统拷贝对商用web服务器的商业测试表明,基于

38、IO-Lite思想的web服务器技术领 先,但是其它一些Web服务器也有优秀的性能。基于I/O拼接的web服务器技术也可以消除copy2和copy30 I/O拼接技术 的基本思想是引入一个新的系统调用,将读文件的调用和向网络发送消息的调用 合并,达到避免冗余拷贝的目的。内核2.1版本中的sendfile实现SendfiIe()是在内核2.1版本引入的,其运行流程如下,与传统的read/write方式相比,sendfile减少了文件缓冲区到应用缓冲区、 再由应用缓冲区到socket缓冲区的拷贝,简化为从文件缓冲区直接拷贝到套接 字缓冲区,减少了一次拷贝。内核2. 4版本之后的Sendfile实现

39、在内核版本2. 4之后,修改了文件描述符结构,SendfiIe实现了更简单的方 式。系统调用方式仍然一样,细节与2.1版本不同之处在于,当文件数据被复制 到内核缓冲区后,不再将所有数据拷贝到SOCket缓冲区,而是将数据位置和长 度的信息保存到SOCket缓冲区,而实际数据通过DMA直接发送到适配器。又减 少了一次拷贝操作。尽管这个机制是成功的,但基于sendfile的机制不能很好地推广到与CGI 程序通信。SendfileO已用于apache、nginx IighttPd等WCb服务器中。5. 8扩展到数据操作之外到目前为止,我们关注的是减少由数据操作引起的内存和总线带宽的消耗。 这些技术的

40、共同点是通过避免冗余的读/写来减少对内存和总线的压力。由于访存是现代计算机系统的最大瓶颈,接下来我们要进一步扩展我们的视 野,除了数据操作之外,还有哪些因素会影响内存总线的使用效率呢?我们想一下,除了数据,还有什么会放在内存里呢?程序代码。CPU从内存 取代码执行,因此代码移动也要使用内存总线。下面就来讨论代码移动对总线使 用效率的影响。5. 8.1有效使用I-cache图5. 2的架构模型回避了一个重要的细节,就是处理器中还有CaChc,有一 个或多个数据CaChe (d-cache),以及一个或多个指令CaChe (cache)。当需 要的数据或指令已经在CaChe中了,就不需要消耗内存带

41、宽了,因此我们要仔细 研究一下数据包处理能否从这些cache中获得好处。一般而言,包数据几乎不能从数据CaChe获得好处,因为包数据几乎不被重 用,不存在对同一个内存地址进行重复读写。当然这是指大多数的情况,如果需 要对包数据进行加解密、内容反复扫描等操作,是可以从CaChe得到好处的。然而,另外两个存储在内存中的东西可以从CaChe获益。处理数据包需要的 状态,比如一个连接表项、一个路由表项。对于同一个流中的数据包,处理第一 个包查找到的表项如果在cache中,则下一个到来的包就可以直接使用该表项。 还有一个就是处理数据包的程序代码,因为程序代码较多时候是顺序执行的。相比于数据包和状态,代码

42、对内存带宽的竞争更严重,我们看一下数据包、 状态和代码的大小就知道了。显然,将协议栈代码从内存中读入所需的内存带宽, 比拷贝几次数据包所需的内存带宽大多了。现代计算机的内存虽然越来越大,但cache的大小并没有显著增大,一些 CPU仍然只有几百KB,况且计算机中运行的其它程序也要使用bcache。因此, 处理数据包的协议代码不可能全部都在I-cache中,这样高效地利用指令CaChe 就成为提高性能的一个关键。为了高效地使用指令cache,我们需要了解I-cache的实现特点。I-CaChe的实现特点(D大多数处理器使用直接映射的I-cache,即用内存地址的低位比特检索 I-cache条目,

43、如果高位比特匹配,就直接从CaChe返回内容;否则进行一个主 存访问,并用新的内容替换原来的条目。也就是说,对于32KB的I-CaChe而言, 内存中最低15位地址相同的指令会被存储在I-cache的同一个位置。图5. 3给出了这种实现的后果。右图是两个包处理函数的代码在内存中的布 局,黑色表示不常使用的代码。X和Y是两个经常被访问的代码行,如果它们的 地址对cache的大小取模后相同(即具有相同的低位地址),则X和Y会被映 射到I-cache的同一位置。如果每一个包的处理都要用到X和Y,它们会被相互 地替换出去,即使它们都是经常被使用的代码。I-Cache的实现特点(2)每一条bcache都

44、包含了几条指令,可以看成是一个代码块数组。当取一条 指令时,同一个代码块中的全部指令都会被读入。这是基于空间局部性假设而做的一个优化:当顺序执行时,后续若干条指令 也可能要被执行,一次读入多条指令可以分担开销。举例这会有什么问题呢?我们知道,包处理代码有许多的条件分支。比如,许多 网络代码包含错误检查,比如“if error E do X, else do Z。Z几乎从不被 执行,但是编译器通常会将Z的代码紧接着放在X的后面。假设Z的代码紧接在X的后面,如果X和Z位于同一个指令块中,那么取经 常使用的代码X,会把不经常使用的代码Z也取进来,这使得取代码的工作变得 低效(做了无用的工作,浪费了内

45、存带宽),也使得CaChe的作用降低了(不常 使用的代码在cache中,浪费了 cache空间)。问题与解决方法这两个结果和我们对于CaChe的一般预期不同。我们通常认为,经常使用的 指令会驻留在CaChe中,不经常使用的指令很少会被调入CaChe,而这两个结果 恰恰相反。第一个结果是由一个不完美的哈希函数造成的,它导致两个经常被访 问的地址发生冲突,经常使用的代码不在cache中。第二个结果是由于cache 针对空间局部性优化引起的,导致不经常使用的代码调入cacheo以上两个问题都可以通过重新组织代码来减轻,其方法是将经常使用的代码 连续放置,避免它们的内存地址发生冲突,也避免将不常使用的

46、代码调入CaChe。重新组织代码当然,如果工作集(即几乎所有的数据包都要使用的代码集合)超过了 I-cache的大小,每次处理一个包都需要将全部代码调入,第一个问题仍会出现。 但是,第二个问题仍然能够缓解,即每次只会装载有用的代码,不会将不用的代 码装入。代码布局的主要思想是通过安排代码在内存中的位置,减少代码的换入换 出。不过,很少有程序员在写网络代码时会意识到代码布局的问题。新的问题尽管我们可以通过代码布局来提高常用代码驻留在I-cache中的可能性,但 是由于I-cache的容量非常有限,并且计算机上的所有程序都要竞争I-cache, 因此,我们几乎可以肯定,处理一个包的协议代码是不可能

47、都装入I-cache中的。那么问题就来了,如果我们收到一个包,调用协议栈代码把这个包处理完,然后再收下一个包进行处理,这个效率肯定非常的低。拷贝数据包的开销和将代 码装入指令CaChe的开销比起来,简直就是微不足道的了。那我们有什么办法可 以提高处理效率呢?局部性驱动的协议层处理局部性驱动的协议层处理的思想是,只要网络协议栈中每一层的代码可以装 入I-cache,就可以有效地使用I-CaChc。方法是每层一次处理多个包,分摊装 载 I-cache 的开销(P2c)o软件工程方面的考虑采取以上两项优化措施的时候,我们都要先评估一下它们对代码的模块化和 可维护性的影响。当我们做系统优化的时候,一定

48、要考虑到工程方面的可行性问 题,否则理论上再好的东西都没有意义。代码的重新组织可以让编译器来做。比如,可以让程序员对处理错误的代码 进行标注,表明哪个分支经常使用,然后由一个增广的编译器为bcache重新组 织代码。有文献描述了相应的算法。局部性驱动的协议层处理需要修改层间通信方法,相邻两层之间通过队列来 传递数据包。当一个协议层被调用时,从自己的读队列中取数据包处理,直至队 列取空。当每一层可以重用另一层的包缓冲区(就像UNIX的Hibufs)时,该策 略工作很好。代码修改量不大。Pl原则的运用本章反复应用Pl原则来消除明显的浪费,这个浪费就是不必要的内存读写, 它们消耗了宝贵的内存和总线带宽。初看起来原则Pl显得很空洞,但运用这个原则的困难在于如果不把视野尽 可能放宽到整个系统,浪费并不是显而易见的。在每个子系统内部,比如应用到内核、内核到适配器、文件系统到应用,并 没有浪费内存带宽。当我们跟随着一个数据包的处理过程时,我们发现了应用到内核、内核到适 配器

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 在线阅读 > 生活休闲


备案号:宁ICP备20000045号-1

经营许可证:宁B2-20210002

宁公网安备 64010402000986号