一、各种用户态socket的对比
1、MTCP
简单介绍:
韩国高校的一个科研项目,在DPDK的2016年的技术开发者大会上有讲,所以intel将这个也放到了官方上,所以一般搜索DPDK的用户态的协议栈的时候就能够搜索到了这个;
特点:
有准确的测试数据,我们本地也测试了其性能:在EP的单核上可以达到4W connect/sec 。然后因为内存限制,连接数当时是60W连接占用了18G的内存;
优点:
1。有准确的性能测试数据
2。有一定的关注度,有开源社区;
3. 自己实现了一套epoll+socket。与内核的epoll在同一个线程不能同时使用。
4.协议栈全面,包含了二三层的逻辑
5. 基本兼容传统的socket操作。代码改动不算大;
缺点:
1。稳定性不高,中途出现了多次异常退出;
2。内部代码逻辑清晰度还有些欠缺;
3。内存占用太大,例如接受的buff都是每一个连接一个、
4。内部有大量的锁操作~逻辑比较复杂;
5。只支持TCP协议
6.内部有很多内存copy操作。网卡数据到协议栈copy一次,协议栈缓存时copy一次,协议栈到用户态又需要copy一次数据;
目前如果要嵌入系统中需要做一下几个方面的工作:
1。因为我们已经有自己的二三层逻辑,所以需要将MTCP原来的二三层给拆除,只保留TCP;
2。需要针对其稳定性进行优化,解决各种异常退出的问题;
3。需要针对其内存占用进行优化;
4。如果需要支持系统epoll和mtcp epoll兼容需要进行逻辑框架的改进~
5。如果需要支持其他socket的时候,需要重新设计并加入udp+raw一类的socket的支持;
6. 如果需要支持多个应用同时使用MTCP的时候也需要改进MTCP
总结:
MTCP不支持多进程模型,而且在稳定性差有一些问题,然后对UDP与RAW不支持;需要进行一系列的修改后才能进入我们系统;
2.libuinet BSD协议栈用户态的库
libuinet是开源社区维护的一个使用openbsd内核协议栈编译出来的一个协议栈的库,使用的bsd的原生协议栈。
优点:
1。来源于BSD协议栈,功能支持全面,所以socket,ipv4 ipv6都支持;
2。来源于BSD协议栈,稳定性与社区活跃性都是不错的;
3。bsd协议栈提供接口与linux socket编程接口兼容性好;
缺点:
1。代码太复杂,因为是从bsd代码中porting过来的,所以有大量对我们来说无用的代码;
2。不支持DPDK。而且二三四层绑定比较紧密~想拆出来不太容易;
3。也是线程模型~与MTCP有同样的缺点;
4。除了协议栈还有大量中间代码(例如协议栈创建了4个线程,而且这个线程不是用标准的pthread创建,也就是它自己还带了一套线程库)
5. 性能没有测试数据,以及内存占用等都没有对应的数据~而且如果一旦出现性能问题~因为代码过与复杂,优化上风险比较大;
总结:bsd用户态协议栈功能强大,社区活跃,但是复杂度太高,而且没有现成的与dpdk对接的例子,移植成本比较高,性能上没有准确数据~如果有性能问题,优化难度较大;
3. 中国人写的tcp协议栈
通过不断的搜索和对比代码,找到了一个简洁版本的 TCP,是中国的一个热爱开源事业的人写的~
Xiaochen Wang <wangxiaochen0@gmail.com> 搜索过内核提交PATCH 华人中排100+
通过分析代码与其实现:
优点:
1。代码简洁,代码实现逻辑简单,没有太多复杂的信息,实现原理与内核一致;
2。代码量较少,总共TCP的代码连注释就在4K行左右~公司私有维护可以hold住。
3。功能全,支持TCP SOCKET UDP SOCKET RAW socket;
4。兼容性好~设计的api接口基本与原来的socket接口一致,便于迁移;
5。通过代码优化可以做到网卡到用户态0 copy,提升效率;
6。内存占用小~测试过1W连接不足1M的内存占用。
缺点:
1。有些查找算法需要优化~;例如查找流表;
2。也不支持DPDK(目前已经修改好DPDK对接的demo)
3。不支持多线程的协议栈(数据结构的设计的时候没有考虑线程安全)
4。只支持阻塞模型的socket。不支持epoll和非阻塞的socket调用(目前已经有设计思路移植epoll)
5。作者已经不维护,性能还没有测试过。
总结:
代码简单,便于自己维护,优化,开发~但是因为没有开源社区的支持,需要自己熟悉代码,测试过程中没有出现异常挂掉的情况,但需要自己移植epoll上去,才可以实现多路IO复用;
4.其他用户态socket
调研了其他用户态的socket;
主要有
1。mirage-tcpip 这种不是C/C++语言实现;
2。net-next-nuse linux kernel stack的用户态实现;类似于bsd 的stack而且依赖于netmap;
3。LWIP UIP完全的嵌入式设计模式~没有socket这层概念;而且代码效率没有考虑,查找全部都是链表比较。
4。picotcp 与上面的lwip与UIP类似,但代码简洁~支持socket,但socket的使用风格与linux socket的风格差别很大;
总结:其他用户态的socket使用上差别与原来linux风格的差别较大~不方便兼容原来的程序;而且都需要修改才能支持DPDK,其中picotcp还是比较活跃的开源项目;
二、关于用户态socket使用上的总结
目前所有的用户态的socket程序,都一样都是协议栈是通过一个线程来支持的。所以APP都需要运行自己的tcp协议栈(四层协议栈),然后通过线程间通信方式将数据传输到用户的业务线程中;
总结起来都有大致如下的问题:
1。都是线程协议栈模型,而且这个协议栈线程包含了二三层;
2。目前对于多进程模型都没有较好的解决方案;
3。epoll与系统的epoll不能同时使用,有些多路复用没有实现;
三、关于用户态socket模型的思考
其实我们二三层转发(其中包含NAT)都有了自己的解决方案;所以我们不需要在用户态socket这一层去再实现一次二三层~;
如果有多进程模型更好的支持,应用程序编写的时候就可以完全不用关心到底是用户态的socket,还是kernel太的socket,使用成本将会更低~
所以我提出这样的模型:
1。用户态socket是一个单独的进程,它通过ring与dpdk server进行数据交互~
2。app通过信号量与ring与用户态socket进行交互,通过dpdk的ring,可以做到数据共享,减少copy;
3。通过信号量实现数据通知机制~从而实现epoll。
优势:应用程序本身只启动应用程序的业务,而不需要启动自己的协议栈。编程模型与之前linux kernel态的编程模型一直,应用开发者无需关心用户态socket的实现;而且通过动态库的方式可以实现,一个bin加载不同的so~就可以实现用户态socket与kerenl socket自由切换~;
层次分明,不用每个app一个协议栈,可以节省cpu的消耗;
缺点:目没有现有模型支持,需要大量的代码修改开发工作;开发周期较长;
另一种思路,继续继承原来类似于MTCP这种模型。所有的进程都有一个用户态socket协议栈的线程,每个用户态socket线程一个ring,与dpdk server进行数据交互;
优点:
1。修改代码量会比之前的方案少点儿~。就可以支持多进程的模型;
2。代码逻辑更加简单;
3。性能上可以保证,因为每个app一个协议栈,而这个协议栈线程会绑定cpu,所以性能上,不同APP他们的协议栈性能是不会互相干扰的;
缺点:
1。因为每个app一个协议栈线程,app挂了,协议栈也要对应重启;
2。每个app一个协议栈线程,协议栈线程会绑定CPU,所以比较浪费CPU的资源;
3。编程模型与之前的kernel socket编程模型有差别了,需要添加额外的启动socket线程的方法;
总结:
两种方法各有优缺点,主要看具体的应用场景。而且通过修改代码可以支持epoll多路I.O的机制。多个socket可以在一个线程中进行处理~
四、关于用户态socket epoll与系统epoll兼容的思考
现在的用户态的epoll与系统的epoll不能同时使用主要原因在与epoll_wait这个函数是一个阻塞的函数,在同一个线程中不可能同时处理两个阻塞的请求;
所以思路有两种:
1。把用户态socket epoll模拟成系统的epoll。
用户态epoll实现,是通过系统的信号量来实现的阻塞与唤醒,如果把这种信号量的通知机制改成fd实现,例如用户态socket事件通知是通过unix socket来实现的,就可以使用系统的epoll监听;从而把用户态的epoll变成系统态的epoll。
2。把系统态的epoll变成用户态的epoll;
系统态的epoll的阻塞。事件等待是通过系统的epoll_wait来实现的事件等待,如果我们启动一个线程来专门监听epoll_wait的事件。等epoll_wait成功以后通过信号量通知到用户态的epoll事件来实现,系统态epoll转换成用户态的epoll;
五、关于流表查询加速的优化的思考
现在我们有的产品的server中其实已经有了流表,可以在dpdk server 把报文送上来的时候,在mbuf的私有字段中就可以带上来流表hash的结果(index)。可以减少用户态socket查询flow的时间,提高速度,目前只是一种思路;
原文地址:http://blog.csdn.net/bestboyxie/article/details/52980456