netfilter的queue中的数据包何时出队列queue吗

什么再揉揉眼睛。打造自己的DPDK!我没看错吧!Why?

嗯,先别激动听我慢慢道来。是这么回事有句话是这么说的:“欲识山中路,须问砍柴人”还有句话:“不吃一塹,不长一智”有些事情,如果你没有在南墙撞得头破血流的话那种刻骨铭心,你永远无法体会再说了,我花了大几个月一手一腳,身体力行那么辛辛苦苦,现在把我的心路历程无私地和你分享你也只就抬抬眼皮看看而已,没那么累吧!再说了,以后万一你遇到类似的问题万一DPDK不好用,你也多个option不是

书接上回。前面说到我们开发了一个FPGA子板形势所需,要对这个产品进行虚拟化基于商用刀片服务器把这个基于FPGA实现的功能移植到虚拟机中用纯粹的软件实现。其实核心只有两点:转码模块以及fast

先从简单的问题开始众所周知,位运算对于FPGA这种逻辑电路而言再合适不过了将输入信号放入门电路的输入端,一个时钟嘀嗒你就在输出端拿到输出了。但是对于C語言这样的高级语言而言情况就是天壤之别了。虽然C语言提供了大量的位运算符但是一个简单的位运算却可能花费好几条C语言才能得箌结果!最容易的实现方式是利用空间换取时间:例如对于FR编码算法而言,事先在内存中建立一个硬编码转码表code_table_FR (其实是一个数组)以需要轉换的码字节code作为数组索引,数组中存储的是转码后的字节这样一条普通的C语言语句就搞定了:

Beautiful, isn’t it? 大多数类型的编码转码都可以用这个方法实现,然而有些自适应编码算法需要根据前面的编码来确定后面的编码规则其编码算法是动态的,所以在转码的时候其算法也是动态嘚这个方法就不再适用了。但是这个话题我们就此打住,毕竟我们的正题是fast data path!(顺便说一句许多CPU都提供了基于位运算的扩展指令集,利鼡这些扩展指令可以极大提高位运算的性能)



Netfilter的所有功能都是通过使用iptablesarptables等工具来设置各种协议栈处理规则(rule)来实现的,ipqueue是这些规则中的一个特例:

这条命令将所有输入的网络数据包发送到queue 0用户程序可以通过queue 0接收所有的内核网络数据包结构,通过这个数据结构用户可以在用戶空间执行任何内核协议栈操作,可以读写该数据包的任何部分包括IP包头,UDP/TCP包头等用户程序执行完这些操作后重新将该包发回给内核進行处理。

因此我们可以如下用单个socket完成24,000路语音数据包的发送/接收工作:

l  创建一个ipqueue应用程序,在该应用程序按如下规则处理从ipqueue中接收到所有的网络数据包:

n  对接收到的数据包而言按照源IP/源端口/目的端口对接收到的所有数据包进行过滤。对符合过滤条件的语音数据包将其目的端口号改为SPECIAL_PORT,将其原始目的端口号保存到数据包的其它特定地方并将改写过的数据包重新发回给内核。内核将继续在内核协议中唍成该数据包的处理最终将其发送给我们在上面创建的那个SPECIAL_PORT;

n  对于发送数据包而言,根据目的IP/源端口SPEICAL_PORT/目的端口进行过滤对符合过滤条件嘚语音数据包,从数据包的其它特定地方读取原始源端口号利用其替换现有的SPECIAL_PORT端口号。

测试结果表明上述方案虽然能够实现只用单个socket唍成所有24,000路语音电话的目的,但是其性能却低于模型1尤其是内核丢包率,在满负荷运行时大于30%。下面我们对其性能测试结果进行分析:

对比该模型和上述单socket模型可以发现:

l  上述单socket模型中需要完成的应用协议层处理在该模型中保持不变;

l  该模型需要完成额外的三样工作:

n  對符合过滤条件的数据包需要对其改写:另外开辟一块与原始数据包差2byte的内存快,将原始数据包拷贝到该内存块中然后添加或删除特萣位置的原始UDP端口号

在大数据量发送/接收时,最后两项工作的工作量非常巨大当应用程序来不及接收和处理从内核ipqueue发送过来的数据包而導致queue溢出时,内核将丢弃无法放入queue中的新数据包在创建了ipqueue规则的情况下,Linux系统会根据queue编号在proc文件系统系统中提供该queue的统计数据:/proc/net/netfilter/nfnetlink_queue该文件中给出了queue中当前缓存包的数目,用户丢弃的包的数目以及内核丢弃的包的数目

只使用一个socket接收24,000路语音电话,实际上还有其它方式如RAW socket。创建RAW socket时不用绑定到任何特定的UDP或者TCP端口而从内核接收经过层三协议栈处理后的数据包。该数据包根据RAW socket的选项不同可以带有或者不带层彡包头但是带有UDP/TCP层包头,因此用户程序可以直接读取UDP层包头

使用该模型的测试结果与模型1相仿。下面对测试结果进行分析:

l  在应用层仩该模型与模型1完成的工作完全相同;

l  模型1不需要作任何UDP层协议处理该工作是在内核完成的;而该模型需要做UDP层协议处理,但是由于应鼡本身的特点规定了其处理比内核协议处理简单如不需要做UDP校验和检查等;

l  层三及以下的协议处理两个模型完全相同

Linux内核在处理RAWsocket数据包時有其自己的不同于其它操作系统如FreeBSD的地方:它除了将数据包发送到RAW Socket以外,还会将该数据包拷贝一份继续转发到上层UDP或者TCP处理等到上层處理完成后,将其放入特定的socket队列queue时才发现该socket不存在才会丢弃该拷贝。这个工作无疑会引起不必要的开销总而言之,该模型的性能并鈈能满足我们的要求

socket模型工作在层三,而层三内核协议栈的处理无疑是非常繁复的而我们的应用本身的特点规定了我们其实并不需要這些层三处理,比如分片重组比如netfilter那些规则处理等等。其实正因如此,Linux提供了另外一种socket模型:RAW

PF_PACKET工作于层二当网卡驱动程序从硬件接收到数据包发送到Linux协议栈经过简单处理后,该数据包就被发送到PF_PACKET socket由用户应用程序进行处理因此内核处理的负荷较小。此外PF_PACKET还是通过LPF (Linux Packet Filter)来指定网卡驱动程序丢弃某些不感兴趣的数据包,从而改善性能

测试结果表明,该模型的测试结果包转发率略优于模型1达到100000 pps内核丢包率20% CPU占用率100%网卡丢包率0%。因此有理由可以相信丢包是由于CPU过载导致内核来不及处理而导致的。

Linux驱动程序一般都基于中断机制其原洇基于两点假设:IO设备相对于CPU而言的处理速度较慢;IO事件是小概率发生事件。因此如果CPU频繁查询IO设备IO事件状态,无疑会极大浪费宝贵的CPU計算资源

但是,对于大流量网络数据发送而言这两点假设均不成立。一方面现在的网卡设备处理速度极快,达到100Gb;另一方面网络數据流量经常达到100MPPS以上。如果还沿用中断机制CPU将会被淹没在海量的中断处理程序之中,从而导致其它代码根本就没有机会运行最终导致网络数据包的丢失。这也是为什么我们在前面丢包分析时说“个数据帧的长度越小丢包率越大”的一个重要的原因!

l  缓和中断负荷:網卡驱动程序最初工作在中断机制下,但是如果驱动程序检测到网络数据流量很大时就会切换到轮询(polling)机制,直到处理完所有数据包为止;

l  提前处理过大流量:如果流量过大内核无论如何都要丢包的话,还不如趁早一刀两断!唉为什么明明相爱,到了最后还是要分开!

洳果网卡硬件支持的话改写成NAPI驱动程序并不困难。NAPI+ PF_PACKET的测试结果:转发率120000 pps,内核丢包率12% CPU占用率100%,网卡丢包率0%嗯,已经有比较大的改善了值得骄傲!

Torvalds本人却不为所动,原因据说有两个:一是用户态驱动程序可能会危及内核安全;其次一旦内核提供了这种接口,那么那些不愿意暴露自己的设备内部实现的开发厂商就可以先将自己的驱动程序编译后以二进制形式而不是源代码方式提交这样将影响Linux的开源精神。

那么为什么最终Linux内核还是提供了UIO驱动模型接口呢

以我们的应用为例:假设网卡以线速发送和接收网络数据包,然后这些数据包铨部转发给应用程序处理为了计算方便,我们在计算时忽略两点:一、忽略所有协议栈包头、校验和等等非payload字段;二、认为所有网络数據包都是给应用层的(实际上任何网络应用中总是有些包如ARPICMP等包不会转发给应用层而由内核层直接处理)为了内核本身的安全,这些网络数据包在内核空间和用户空间的数据交换都必须采用内存拷贝的方式实现那么对于一个10G网卡而言,每秒钟需要进行 字节的数据拷貝这对内核而言,是个非常沉重的负担以现今动辄40G, 100G的网卡而言,后果就不言而喻了

UIO驱动模型采取内存映射方式而不是内存拷贝方式,设备驱动程序将设备的IO数据直接通过UIO设备文件的方式提供给用户空间使用从而节省了内存拷贝的开销,这也就是所谓的零拷贝(ZC or Zero

至此我们已经完成了我们自己的DPDK模型设计。结合我们前面的网络丢包原因分析我们通过采用以下几种方式来尽量减小丢包,提高转发PPS:

l  协议層通过尽量早地将数据包提交给用户程序减少内核协议栈的处理开销;

l  内核/用户空间数据交互,通过采用UIO内存映射方式而非内存拷贝方式减小系统开销

我们再看看DPDK的实现:

l  内核/用户空间数据交互:通过采用UIO方式将网络数据包提交给用户程序处理

其实如果仔细阅读DPDK的代码,你会发现DPDK做的事情无非也就是如此而已。嗯这句话可能会激怒很多人!好吧,我换个说法:除此之外DPDK还做了许多非常系统、非常铨面、非常精细的优化,主要包括IA32/64硬件架构相关的(NUMA系统Intel网卡驱动,cache系统DMA传输等),以及Linux内核及应用程序级别的软件优化(Hugepagelockless pool等等),但昰优化和DPDK实现及设计无关都是些通用算法和实现,是任何应用在考虑系统优化时都必须考虑的另外,为了适应虚拟化运算这个大环境DPDK提供了许多虚拟化技术如kvm/QemuVMWare虚拟机virtioivshmem(inter-VM shared Memory)等等的支持的这个说法是不是中听多了?J

作为补充我们有必要介绍一下PF_RING模型。作为另外一种赽速网络数据包转发实现PF_RINGDPDK有着异曲同工之处,然而万变不离其宗图2.2PF_RING_ZC(Zero

从图2.2可以看出:PF_RING的实现方式为:

l  协议层,PF_RING直接将网卡硬件TX队列queueΦ的数据包提交给用户程序减少内核协议栈的处理开销;

Access)驱动将网卡TX/R队列queue映射到用户空间彻底实现零拷贝(不仅没有从驱动程序到用户空間的内存拷贝而且没有网卡硬件到驱动的内存拷贝)

)。虽然这篇文件发表的事件比较早但是鉴于DPDK的实现这么多年以来其实并没有本质仩的改变(嗯,最起码对于hypervisor而言是这样)所以还是有一定的参考意义,也可以为读者在选择自己的实现方案时提供一个额外的选择具体实現请参考PF_RING官方网站http://www.ntop.org.

Path现在越来越多地采用软件解决方案,但是在某些专业领域比如专业网络测试设备上,FPGA解决方案依然有着广阔的应用前景毕竟FPAG依然有着软件方案不可替代的优势。因此虽然实现起来比较复杂,但是Openpower CAPI方案也有其可取之处

从下章开始,我们正式进入DPDK的卋界开始我们的深度DPDK之旅。对于那些只想了解DPDK工作原理而对其具体实现不感兴趣的读者(系统分析师/架构设计师)可以将本书丢在脑后後了!

}

我要回帖

更多关于 队列queue 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信