使用DPDK收包,想要用到TCP协议栈,可选的方案有linux原生的tun/tap口以及DPDK自带的KNI驱动,这两种都是通过将DPDK收到的报文注入到linux内核来使用TCP协议栈的功能,然后,用户态协议栈可以考虑开源的f-stack,商用的可以使用6wind的协议栈(不过价格不菲)。
前面讲过f-stack的源码编译安装,最近研究了一下f-stack,对它的实现框架有了一些认识。f-stack使用的多进程单线程模型,收包模型如下图:
上图模型中,我们配置了4个cpu core,启动了4个nginx进程,其中nginx_0是DPDK Primary进程,其它三个都是Secondary进程,nginx_N绑定coreN,负责处理网卡1和网卡2的N号队列的报文,其中N=0,1,2,3。从图中可以看到,基于f-stack的应用程序都是多进程配合,每个进程的单个DPDK收包线程绑定一个核,然后处理所有网卡上相同编号队列的报文。
f-stack的报文收发逻辑,简单一点说,就是先使用f-stack的lib库中的DPDK API接口把报文收上来,然后将报文给到移植的FreeBSD协议栈(也可以根据配置的端口,通过KNI过滤部分报文给Linux内核),上层的应用程序比如nginx或redis需要修改原有的socket的API为f-stack封装的socket API,就可以收到DPDK收上来的报文了。
将f-stack源码目录下的config.ini配置文件复制一份到examples目录,修改配置:lcore_mask=0xf,port_list=0,1,添加[port1]配置,lcore_list=0,1,2,3,指定进程编号启动helloworld_epoll:
./helloworld_epoll --proc-id=0
./helloworld_epoll --proc-id=1
./helloworld_epoll --proc-id=2
./helloworld_epoll --proc-id=3
每个进程分别可以看到打印,从打印看跟上面的分析应该是一致的。
基于以上的分析,有几点思考:
1、多进程单收包线程的模型,可以实现每个进程都有一个独立的用户态协议栈,性能肯定更高,但是也说明了内核协议栈性能较低,影响的因素,收包时候的数据拷贝是一方面,多线程之间的锁也是一方面。
2、多进程单收包线程的模式比较适合nginx或者redis这种场景,但是不太适合做类似防火墙或者网关一类的产品,因为这类产品必然要做大量的进程间共享数据的工作(比如同步会话表),可能会比较麻烦。
3、个人感觉f-stack主要是提供了比较好的应用态socket的功能,如果要想使用类似于linux的netfilter和iptables那一套hook转发的功能,可能还不太好使用,f-stack源码目录tools下有libnetgraph和ipfw目录,没有实测过,不知道怎么样。
4、应用态程序的移植是否方便,比如frr、strongswan等等开源项目移植到f-stack,是否好移植,看到f-stack正在通过LD_PRELOAD动态链接的方式进行劫持,看能不能大大降低移植的难度。
5、性能方便,f-stack在较少cpu core的表现较好,就是相同性能表现的情况下,可以比nginx用到的cpu的资源少。
没用过f-stack之前,担心的是它的稳定性以及是否会持续维护,但是了解到它是多进程单收包线程之后,觉得它并不是一种通用的应用态协议栈解决方案,对做防火墙网关之类的网络产品还不太友好,如果能支持单进程多收包线程,牺牲点应用态协议栈的性能如何呢?或者我们自己把f-stack改造成单进程多线程收包模式,然后从业务上去保证只有单个线程上的报文进协议栈处理如何呢?欢迎有相同疑问的朋友一起分享交流。
好了,关于f-stack收包框架的分析就讲到这里了。喜欢文章内容的朋友,记得加个关注哦~~