acm-header
登录

ACM通信

实践

重温网络I/O api: Netmap框架


五彩纸屑

来源:Un mot, une semaine

回到顶部

今天,千兆接口越来越多地用于数据中心和服务器。在这些链路上,信息包以每67.2纳秒一个的速度流动,然而现代操作系统仅在线路和应用程序之间移动一个信息包就需要1020倍的时间。我们可以做得更好,不是通过更强大的硬件,而是通过修改很久以前关于设备驱动程序和网络堆栈设计的架构决策。

netmap框架是朝着这个方向迈出的有希望的一步。由于精心的设计和新的数据包I/O API工程,netmap消除了许多不必要的开销,传输流量的速度比现有操作系统快40倍。最重要的是,netmap在很大程度上与现有应用程序兼容,因此可以增量部署。

在当前的主流操作系统(Windows、Linux、BSD及其衍生产品)中,网络代码和设备驱动程序的架构很大程度上受到近30年前的设计决策的影响。当时,记忆是一种稀缺资源;以低速度(以今天的标准)运行的链路;并行处理是一个超前的研究课题;并且在所有可能的条件下以线路速率工作的能力受到了NIC(网络接口控制器)硬件限制的影响,甚至在软件涉及之前。

在这样的环境中,设计人员在使用便利性、性能和精简内存使用之间进行权衡。包由描述符(命名为mbuf,5skbuf,9NDISbuffer,取决于操作系统)链接到固定大小的缓冲区链。mbuf和缓冲区是从一个公共池动态分配的,因为它们的生命周期超过了单个函数的作用域。缓冲区也是引用计数的,因此它们可以由多个使用者共享。最终,包的这种表示实现了网络堆栈所有层之间的消息传递接口。

mbuf包含元数据(数据包大小、标志、对接口的引用、套接字、凭据和数据包缓冲区),而缓冲区包含数据包的有效负载。使用固定大小的缓冲区简化了内存分配器,即使当数据超过单个缓冲区的大小时需要链接。允许共享缓冲区可以在网络堆栈上的许多常见操作中节省一些数据复制(因此,时间和空间)。例如,在传输TCP报文时,协议栈必须保留该报文的副本,以防传输丢失,共享缓冲区可以节省复制成本。

现代网卡的设计就是基于这种数据表示。它们可以发送或接收分成多个内存缓冲区的数据包,这是由操作系统实现的。网卡使用它们自己的描述符,比操作系统使用的描述符简单得多,通常排列成一个圆形数组,称为NIC环(见图1).NIC环是静态分配的,它的槽指向作为mbuf链一部分的缓冲区。

回到顶部

Packet-Handling成本

网络I/O有两个主要的成本组成部分。的每字节成本来自数据操作(复制、校验和计算、加密),并与处理的流量成比例。的每个包的成本来自对描述符(分配和销毁、元数据管理)的操作以及系统调用、中断和设备驱动程序函数的执行。每个数据包的成本取决于数据流如何被分割成数据包:数据包越大,组件越小。

要了解速度限制的概念,请考虑10Gbit/s以太网接口,这将是本文的参考点。包的最小大小是64字节或512位,包之间的间隙和序言有额外的160位。在10Gbit/s的速度下,这意味着每67.2纳秒就有一个数据包,最坏情况下的速率为14.88Mpps(每秒100万个数据包)。在最大以太网帧大小(1518字节加上帧)下,传输时间变为1.23微秒,帧速率约为812Kpps。这比峰值速率低了大约20倍,但仍然相当具有挑战性,如果TCP要饱和10Gbit/s链路,就必须保持这种方案。

在典型的操作系统中,分组处理时间的分割是了解如何改进它的有用信息。考虑以下两种情况。

使用用户数据报协议(UDP)或原始套接字的应用程序通常对每个数据包发出一个系统调用。这将导致分配mbuf链,该链使用元数据和用户提供的有效负载的副本进行初始化。然后NIC被编程来传输数据包,最终mbuf链被回收。类似的过程发生在接收上。在总处理时间中,大约50%用于系统调用:30%用于mbuf分配和回收,其余部分平均分配给内存复制和硬件编程。下图是分裂的图形表示:

ins01.gif

TCP发送方和接收方的情况略有不同:系统调用可以将较大的段从/移动到内核,而分段发生在内核内部。在这种情况下,系统调用对单个数据包的影响减小;另一方面,内核内处理的开销更大,这也是因为需要处理反向的确认流。这将导致不同的工作分配,如图所示:

ins02.gif

在这两种情况下,总体数据包处理能力是相似的:最先进的系统每个核心可以处理大约1Mpps。这比10Gbit/s链路的峰值速度要低得多,仅能满足1500字节数据包的饱和。

为什么mbuf处理如此耗时?在30年前的单处理器系统中,内存分配器和引用计数相对便宜,特别是在存在固定大小的对象时。现在的情况不同了:在多核环境下,分配会争夺全局锁,引用计数会对共享内存变量进行操作;这两种操作都很容易导致非缓存DRAM访问,这需要50100纳秒。4

回到顶部

处理延迟

在尝试饱和高速链路时,处理成本只是等式的一部分。延迟也会严重影响性能。对延迟的敏感并不是10Gbit/s网络所独有的,但是当链路速度不再是系统中的瓶颈时,这种现象变得尤为重要。

高内存读取延迟(前面提到的几十纳秒)通常发生在从NIC寄存器或NIC环读取时。这些内存区域由NIC更新,从而使CPU缓存失效。为了避免停顿(这会消耗所有可用的包处理时间)并实现所需的吞吐量,包处理代码必须在实际需要数据之前发出预取指令。反过来,这可能需要重大的代码重组:代码应该分批工作,而不是一次处理一个包,首先进行一轮预取(在等待读取完成时保持CPU繁忙),然后进行实际的包处理。

内存写受到延迟的影响较小,因为缓存和写缓冲区可以吸收许多未完成的写请求而不会阻塞。另一方面,在传输协议级别上,应用程序可能会暂停等待写入完成。众所周知,在任何通信协议中,当传输的数据量(“窗口”)受到某个最大值W的限制时,最大吞吐量为min(B、W / RTT),其中B为瓶颈带宽,RTT为系统往返时间。即使是非常乐观的100微秒RTT,也需要1Mbit (125KB)的数据才能充分利用链路的容量。如果通信实体在等待响应之前没有足够的数据要发送,那么实现全速可能是不可能的,或者需要对应用程序本身进行重大重组。

回到顶部

高速传输协议

对于具有实际RTT值的路径(在10ms到100ms范围内),窗口大小会变得很大,并使整个系统处于压力之下。大窗口与传统TCP拥塞控制算法的交互很差,1对丢包的反应非常保守。一个实验性的RFC(请求评论)提出了一个在丢包后更积极的窗口增加策略,从而减少了达到全速的时间。2使用大窗口的第二个影响是内存占用和缓存使用的增加,可能会溢出可用的缓存空间并减慢数据访问。最后,通常用于存储包的数据结构(线性列表)在出现包丢失或重排序时引入了线性代价。


由于精心的设计和新的数据包I/O API工程,netmap消除了许多不必要的开销,传输流量的速度比现有操作系统快40倍。


请记住,协议有时不仅要处理协议头,还要访问数据包的有效负载。例如,TCP校验和计算需要读取数据包数据(在发送端和接收端),这将消耗内存带宽并污染缓存。在某些情况下(例如,对于本地生成的数据),可以通过将校验和计算与将数据带入内核的复制合并来优化此操作。对于接收路径来说,这是不可能的,因为确认(依赖校验和验证)不能延迟到用户空间进程读取数据。这解释了为什么校验和计算是硬件卸载的自然候选。

回到顶部

已经昭然若揭的技术

数据包I/O机制的效率。允许系统调用在每个调用中处理多个包可以平摊它们的成本,这是总成本的很大一部分。一些高性能的包捕获api采用了这种技术。另一种常见的选择是使用比大多数以太网接口缺省使用的1500字节更大的数据包。较大的最大传输单元(MTU)降低了每个数据包的成本;在TCP流量的情况下,它也加快了丢包后的窗口增加过程。较大的MTU是有效的,但是,只有在源和目的地之间的整个路径上允许它;否则,通过路径MTU发现将MTU减小;或者更糟的是,MTU不匹配可能导致ip级碎片。

将一些任务外包给硬件是提高吞吐量的另一种流行选择。典型的例子是IP和TCP校验和计算以及虚拟LAN (VLAN)标签的添加/删除。这些任务可以在NIC中完成,只需要少量的额外电路,可以节省一些数据访问或副本。两种流行的机制,特别是在TCP上下文中,是TCP分段卸载(TCO)和大接收卸载(LRO)。TSO是指网卡可以将单个大数据包拆分为多个mtu大小的TCP段(而不是IP片段)。节省的是对整个数据包只遍历一次协议栈,而不是对每个MTU字节的数据都遍历一次。LRO在接收端起作用,将多个传入段(对于同一个流)合并为一个,然后交付给网络堆栈。

现代网卡还支持某些形式的包过滤和加密加速。这些是在特定情况下应用的更专门的功能。

多年来,硬件加速的有效性一直被频繁地争论。在链接速度的每一次飞跃(通常是10倍),系统发现自己无法应付线路速率,供应商增加硬件加速功能来填补空白。然后,随着时间的推移,cpu和内存变得更快,通用处理器可能达到甚至超过硬件加速器的速度。

虽然使用硬件校验和可能是有意义的(它们在硬件上几乎没有成本,并且可以节省大量的时间),但TSO和LRO等特性即使在软件中实现也相对便宜。


Netmap是一个新颖的框架,它采用了一些已知的技术来降低数据包处理成本。它的主要特点是能够与现有的操作系统内部和应用程序顺利集成。


如今,几乎无处不在的多CPU核心可用性可以被利用来增加包处理系统的吞吐量。现代网卡支持多个发送和接收队列,不同的核心可以独立使用,而不需要协调,至少在访问网卡寄存器和环方面是这样。在内部,NIC将数据包从发送队列调度到输出链路,并提供某种形式的解复用,以便根据一些有用的密钥(如MAC地址或5元组)将传入的流量交付到接收队列。

回到顶部

Netmap

回到提高包处理性能的最初目标,我们正在寻找一个较大的因素:从1Mpps到14.88Mpps及以上,以达到线路速率。根据阿姆达尔定律,只有在随后削减任务中最大的成本因素,才能实现如此大的加速。在这方面,迄今为止所展示的技术都没有解决这个问题的潜力。使用大数据包并不总是一种选择;硬件卸载对系统调用和mbuf管理没有帮助,求助于并行是解决问题的一种暴力方法,如果没有消除其他瓶颈,则无法扩展。

Netmap7是一种新颖的框架,它采用了一些已知的技术来降低包处理成本。除了性能之外,它的关键特性是能够与现有的操作系统内部和应用程序顺利集成。这使得仅用有限数量的新代码就可以实现巨大的速度提升,同时构建健壮且可维护的系统。

Netmap定义了一个API,它支持在每次系统调用时发送和接收大量的数据包,因此使得系统调用(UDP最大的开销组件)几乎可以忽略不计。Mbuf处理成本(下一个最重要的组件)被完全消除,因为缓冲区和描述符只在网络设备初始化时分配一次。在许多情况下,内核和用户空间之间共享缓冲区可以节省内存副本并减少缓存污染。

数据结构.当查看用于表示数据包和支持应用程序与内核之间通信的数据结构时,描述netmap体系结构会更容易一些。该框架是围绕一个共享内存区域构建的,内核和用户空间应用程序可以访问该区域,该区域包含一个接口管理的所有数据包的缓冲区和描述符。包缓冲区有一个固定的大小,足以存储最大大小的包。这意味着没有碎片和固定而简单的数据包格式。每个缓冲区的描述符非常紧凑(每个8个字节),存储在一个一对一映射到NIC环的圆形数组中。它们是数据结构的一部分netmap_环,其中还包含一些附加字段,包括索引(坏蛋)要发送或接收的第一个缓冲区的数目(效果)用于传输或接收的缓冲器。

Netmap缓冲区和描述符只在接口启动时分配一次,并保持与接口的绑定。Netmap有一个简单的规则来仲裁对共享数据结构的访问:Netmap环总是由应用程序拥有,除非在执行系统调用期间,当应用程序被阻塞时,操作系统可以自由地访问结构而不发生冲突。之间的缓冲区坏蛋而且坏蛋+效果遵循相同的规则:它们属于用户空间,除非在系统调用期间。剩余的缓冲区(如果有的话)则由内核拥有。

用户API.使用netmap对程序员来说非常简单和直观。首先,通过调用来创建未绑定的文件描述符(类似于套接字)打开(“/ dev / netmap”).对象将描述符绑定到给定的接口ioctl (),将接口名作为参数的一部分传递。返回时,参数指示共享内存区域的大小和netmap环在该区域中的位置。随后mmap ()将使进程可以访问共享内存区域。

要发送数据包,应用程序将填充到效果缓冲区与包数据,设置len字段中对应的槽位netmap_环,并提出坏蛋索引的数据包的数量发送。在此之后,一个非阻塞NIOCTXSYNC ioctl (fd)告诉内核传输新数据包,并回收已完成传输的缓冲区。接收端使用类似的操作:非阻塞NIOCRXSYNC ioctl (fd)将netmap环的状态更新为内核已知的状态。在返回时,坏蛋指示带有数据的第一个缓冲区,告诉有多少缓冲区可用,以及缓冲区和槽中有数据和元数据可用。用户进程通过前进向消耗的数据包发出信号坏蛋,再下一个ioctl ()内核使这些缓冲区可用于新的接收。

让我们重新审视使用netmap的系统中的处理成本。系统调用仍然存在,但现在可以在大量的数据包上摊销,可能是整个环,所以它的成本可以忽略不计。下一个最高的组件mbuf管理完全消失了,因为缓冲区和描述符现在是静态的。数据副本同样被删除,惟一剩下的操作(由ioctl ()s)是在确认信息后更新网卡环netmap_环并通过写入网卡的一个寄存器来开始传输。通过这些简化,netmap可以实现比标准API更高的传输和接收速率就不足为奇了。

许多应用程序需要阻塞I/O调用。Netmap描述符可以传递给poll () / select ()系统调用,当环有可用插槽时,该调用将被解除阻塞。使用poll ()的描述符具有相同的复杂性ioctl ()之前的S,和poll ()具有一些额外的优化,以减少典型应用程序中所需的系统调用数量。例如,poll ()可以推出任何未决传输,即使POLLOUT不是参数的一部分;它可以更新时间戳netmap_环,避免额外的gettimeofday ()我们称它为许多应用程序问题poll ()

支持多环图2显示一个接口可以有多个netmap环。支持现代高速网卡的多环架构。在将文件描述符绑定到NIC时,应用程序可以选择将所有环附加到文件描述符上,或者仅将一个环附加到文件描述符上。对于第一个选项,相同的代码可以用于单队列或多队列NIC。对于第二种选择,可以使用每个环一个进程/核心构建高性能系统,从而利用系统中可用的并行性。

中显示了一个可以处理多环网卡的流量生成器的工作示例图3.设备连接顺序遵循到目前为止讨论的结构。负责发送数据包的代码围绕poll (),当缓冲区可用时返回。代码简单地填充了所有环中的所有可用缓冲区,然后是下一个poll ()调用将推出数据包,并在有更多可用缓冲区时返回。

主机堆栈访问和零拷贝转发.在netmap模式下,网卡与主机堆栈断开连接,可直接被应用程序访问。然而,操作系统仍然认为网卡存在并且可用,因此它将尝试从它发送和接收流量。Netmap附加两个软件netmap环向主机堆栈,使它可以使用netmap API访问。主机栈生成的报文从mbuf中提取,并存储在输入环,类似于用于来自网络的流量的环。去往主机栈的报文由netmap客户端排队进入输出netmap环,然后从那里封装成mbuf并传递给主机堆栈,就好像它们来自相应的netmap启用的NIC。这种方法为构建流量过滤器提供了理想的解决方案:netmap客户端可以将一个描述符绑定到主机环,将一个描述符绑定到设备环,并决定在两者之间转发哪些流量。

由于netmap缓冲区的实现方式(所有映射在同一个共享区域),构建真正的零复制转发应用程序很容易。应用程序只需要在接收环和发送环之间交换缓冲区指针,将一个缓冲区排队等待传输,同时用一个新的缓冲区补充接收环。这适用于网卡环和主机环之间,以及不同接口之间。

回到顶部

表现及实施

netmap API主要提供了发送和接收原始报文的快捷方式。一类应用程序(防火墙、流量分析器和生成器、网桥和路由器)可以通过使用本机API轻松地利用性能优势。这需要在应用程序中进行更改,尽管更改很小,但这通常是不可取的。构建一个libpcap然而,这意味着你可以在我们的新API上运行大量未经修改的应用程序。

netmap的改进最好地体现在简单的应用程序上,如流量生成器或接收器。这些应用程序将大部分时间用于处理原始数据包I/O。图4使用netmap或标准api,比较不同时钟速度和核数的各种流量生成器的性能。下面的曲线说明了这一点netsend这是一个使用传统套接字API的包生成器,单核只能在满时达到1Mpps。性能图上的下一个是pktgen它是一个专门的Linux应用程序,完全在内核中实现包生成。即使在这种情况下,峰值速度约为4Mpps,远低于10Gbit/s接口上可实现的最大速率。

下面两条曲线显示了netmap生成器的性能图3:它不仅可以达到线率(14.88Mpps与最小尺寸帧),而且即使在最大时钟速度的三分之一。这比使用本机API快40倍,比内核内Linux工具快10倍。上面的曲线使用了四个核心,显示API在多个cpu上的伸缩性相当好。

图5展示各种包转发应用程序如何从netmap的使用中受益。两个极端是本地桥接(在Free-BSD上),达到0.69Mpps;以及一个自定义应用程序,使用netmap API实现跨接口的最简单的数据包转发,达到超过10Mpps。如图所示,a的使用libpcap在这种情况下,netmap上的仿真只牺牲了25%的性能(在cpu密集型应用程序中更少)。类似的巨大速度已经实现8有两个流行的转发应用程序。Open vSwitch6即使在经过大量优化以消除原始实现中的一些性能问题之后,仍可获得四倍的加速。点击3.使用netmap比原始版本快10倍。事实上,带有netmap的Click比内核版本要快得多,而内核版本多年来一直被认为是构建软件包处理系统最有效的解决方案之一。


我们使用netmap的经验表明,通过操作系统使用数据包处理的方式可以实现巨大的性能改进。


netmap的关键设计目标之一是使其易于集成到现有操作系统中,并且同样易于维护和移植到新的(或闭源的、第三方的)硬件。如果没有这些功能,netmap将只是另一个研究原型,没有被广泛使用的希望。

Netmap最近被导入到FreeBSD发行版中,Linux版本正在开发中。核心由不到2000行文档密集的C代码组成,并且没有对操作系统的内部数据结构或软件接口进行任何更改。Netmap确实需要单独的设备驱动程序修改,但是这些更改很小(每个更改大约500行代码,而组成一个典型的设备驱动程序需要3000到10000行代码),而且是分区的,因此只修改了原始源代码的一小部分。

回到顶部

结论

我们使用netmap的经验表明,通过操作系统使用数据包处理的方式可以实现巨大的性能改进。这一结果不需要特殊的硬件支持或对现有软件进行深度更改,而是通过关注传统数据包处理体系结构中的瓶颈,并通过修改设计决策,以尽量减少对系统的更改。

ACM队列的q戳相关文章
queue.acm.org

套接字将走向何方?
乔治·v·内维尔-尼尔
http://queue.acm.org/detail.cfm?id=1538949

DAFS:一种新的高性能网络文件系统
史蒂夫·克雷曼
http://queue.acm.org/detail.cfm?id=1388770

虚拟现实
西蒙·克劳斯比,大卫·布朗
http://queue.acm.org/detail.cfm?id=1189289

回到顶部

参考文献

1.Allman, M., Paxson, V.和Blanton, E. RFC 5681: TCP拥塞控制,2009。

2.Floyd, S. RFC 3649:用于大拥塞窗口的高速TCP, 2003。

3.科勒,E.,莫里斯,R.,陈,B., Jannotti, J.和Kaashoek, F. Click模块化路由器。ACM计算机系统汇刊, 3 (2000);http://dl.acm.org/citation.cfm?id=354874

4.Levinthal, D.英特尔酷睿i7处理器和英特尔至强5500处理器的性能分析指南(2008-2009),22;http://software.intel.com/sites/products/collateral/hpc/vtune/performance_analysis_guide.pdf

5.麦克库西克,M.K.和内维尔-尼尔,G。FreeBSD操作系统的设计与实现.Addison-Wesley,波士顿,马萨诸塞州,2004年。

6.Open vSwitch;http://openvswitch.org/

7.里佐,L. netmap主页;http://info.iet.unipi.it/~luigi/netmap

8.里佐,L.,卡彭,M.和卡特利,G.使用netmap的透明加速软件包转发。信息通信(2012);http://info.iet.unipi.it/~luigi/netmap/20110729-rizzo-infocom.pdf

9.鲁比尼,A.科贝,J. 2001。Linux设备驱动程序,第二版(第14章)。奥莱利,塞瓦斯托波尔,加利福尼亚州;http://lwn.net/Kernel/LDD2/ch14.lwn

回到顶部

作者

路易吉里索rizzo@iet.unipi.it)是意大利比萨大学Università di Pisa信息学院的副教授。他的研究主要集中在计算机网络,最近的研究方向是快速分组处理、分组调度、网络仿真和磁盘调度。

回到顶部

脚注

这项工作由EC项目CHANGE资助。

回到顶部

数据

F1图1。常规操作系统中的mbuf和NIC环。

F2图2。共享内存结构和NIC环。

F3图3。使用netmap的包生成器的代码。

F4图4。使用netmap与传统api相比,数据包生成速度更快。

F5图5。有或没有netmap的各种应用程序的性能。

回到顶部


©2012 acm 0001-0782/12/0300 $10.00

本论文部分或全部的电子版或硬拷贝供个人或课堂使用的许可是免费的,前提是副本不是为了盈利或商业利益而制作或分发的,并且副本的第一页上必须有本通知和完整的引用。除ACM外,本作品的其他组件的版权必须受到尊重。允许有署名的摘要。以其他方式复制,重新发布,在服务器上发布,或重新分发到列表,需要事先特定的许可和/或费用。请求发布权限permissions@acm.org或传真(212)869-0481。

数字图书馆是由计算机协会出版的。版权所有©2012 ACM, Inc.


评论


匿名

嗨,路易吉,
精彩及时的文章,谢谢!只是想知道你是否看过Virtio (http://lwn.net/Articles/239238/),或者可以评论一下它作为一个优化的网络I/O API的适用性?
再次感谢罗伯特·tw。


匿名

我没有第一手经验,但Virtio-net上发布的所有性能数据都表明,即使是小数据包,速度也在每核200Kpps左右。不要试图忽视Virtio对于虚拟化的重要性,netmap性能的两个数量级的差异表明Virtio不太适合非常高速的网络。

使netmap适应虚拟化环境是我们未来计划探索的一个领域。

路易吉里索


显示所有2评论

Baidu
map