1. 传统linux网络协议栈流程和性能分析
Linux网络协议栈是处理网络数据包的典型系统,它包含了从物理层直到应用层的全过程。
html
- 数据包到达网卡设备。
- 网卡设备依据配置进行DMA操做。(第1次拷贝:网卡寄存器->内核为网卡分配的缓冲区ring buffer)
- 网卡发送中断,唤醒处理器。
- 驱动软件从ring buffer中读取,填充内核skbuff结构(第2次拷贝:内核网卡缓冲区ring buffer->内核专用数据结构skbuff)
- 数据报文达到内核协议栈,进行高层处理。
- socket系统调用将数据从内核搬移到用户态。(第3次拷贝:内核空间->用户空间)
研究者们发现,Linux内核协议栈在数据包的收发过程当中,内存拷贝操做的时间开销占了整个处理过程时间开销的65%,此外层间传递的系统调用时间也占据了8%~10%。linux
协议栈的主要问题:shell
-
针对单个数据包级别的资源分配和释放
每当一个数据包到达网卡,系统就会分配一个分组描述符用于存储数据包的信息和头部,直到分组传送到用户态空间,其描述符才被释放。此外,sk_buff庞大的数据结构中的大部分信息对于大多数网络任务而言都是无用的.编程 -
流量的串行访问
现代网卡包括多个硬件的接收端扩展(receiver-side scaling, RSS)队列能够将分组按照五元组散列函数分配到不一样的接收队列。使用这种技术,分组的捕获过程能够被并行化,由于每一个RSS队列能够映射到一个特定的CPU核,而且能够对应相应的NAPI线程。这样整个捕获过程就能够作到并行化。
可是问题出如今之上的层次,Linux中的协议栈在网络层和传输层须要分析合并的全部数据包
①全部流量在一个单一模块中被处理,产生性能瓶颈;
②用户进程不可以从一个单一的RSS队列接收消息.
这就形成了上层应用没法利用现代硬件的并行化处理能力,这种在用户态分配流量前后序列的过程下降了系统的性能,丢失了驱动层面所得到的加速.
此外,从不一样队列合并的流量可能会产生额外的乱序分组缓存 -
从驱动到用户态的数据拷贝
从网卡收到数据包到应用取走数据的过程当中,存在至少2次数据包的复制安全 -
内核到用户空间的上下文切换
从应用程序的视角来看,它须要执行系统调用来接收每一个分组.每一个系统调用包含一次从用户态到内核态的上下文切换,随之而来的是大量的CPU时间消耗.在每一个数据包上执行系统调用时产生的上下文切换可能消耗近1 000个CPU周期.网络 -
跨内存访问
例如,当接收一个64 B分组时,cache未命中形成了额外13.8%的CPU周期的消耗.另外,在一个基于NUMA的系统中,内存访问的时间取决于访问的存储节点.所以,cache未命中在跨内存块访问环境下会产生更大的内存访问延迟,从而致使性能降低.数据结构
2. 提升捕获效率的技术
目前高性能报文捕获引擎中经常使用的提升捕获效率的技术,这些技术可以克服以前架构的性能限制.架构
-
预分配和重用内存资源
这种技术包括:
开始分组接收以前,预先分配好将要到达的数据包所需的内存空间用来存储数据和元数据(分组描述符).尤为体如今,在加载网卡驱动程序时就分配好 N 个描述符队列(每一个硬件队列和设备一个).负载均衡一样,当一个数据包被传送到用户空间,其对应的描述符也不会被释放,而是从新用于存储新到达的分组.得益于这一策略,在每一个数据包分配/释放所产生的性能瓶颈获得了消除.此外,也能够经过简化sk_buff的数据结构来减小内存开销.
-
数据包采用并行直接通道传递.
为了解决序列化的访问流量,须要创建从RSS队列到应用之间的直接并行数据通道.这种技术经过特定的RSS队列、特定的CPU核和应用三者的绑定来实现性能的提高.这种技术也存在一些缺点:
①数据包可能会乱序地到达用户态,从而影响某些应用的性能;
②RSS使用Hash函数在每一个接收队列间分配流量.当不一样核的数据包间没有相互关联时,它们能够被独立地分析,但若是同一条流的往返数据包被分配到不一样的CPU核上时,就会形成低效的跨核访问. -
内存映射.
使用这种方法,应用程序的内存区域能够映射到内核态的内存区域,应用可以在没有中间副本的状况下读写这片内存区域.
用这种方式咱们可使应用直接访问网卡的DMA内存区域,这种技术被称为零拷贝.但零拷贝也存在潜在的安全问题,向应用暴露出网卡环形队列和寄存器会影响系统的安全性和稳定性 . -
数据包的批处理.
为了不对每一个数据包的重复操做的开销,可使用对数据包的批量处理.这个策略将数据包划分为组,按组分配缓冲区,将它们一块儿复制到内核/用户内存.运用这种技术减小了系统调用以及随之而来的上下文切换的次数;同时也减小了拷贝的次数,从而减小了平摊处处理和复制每一个数据包的开销.
但因为分组必须等到一个批次已满或定时器期满才会递交给上层,批处理技术的主要问题是延迟抖动以及接收报文时间戳偏差的增长. -
亲和性与预取.
因为程序运行的局部性原理,为进程分配的内存必须与正在执行它的处理器操做的内存块一致,这种技术被称为内存的亲和性.
CPU亲和性是一种技术,它容许进程或线程在指定的处理器核心上运行.
在内核与驱动层面,软件和硬件中断能够用一样的方法指定具体的CPU核或处理器来处理,称为中断亲和力.每当一个线程但愿访问所接收的数据,若是先前这些数据已被分配到相同CPU核的中断处理程序接收,则它们在本地cache可以更容易被访问到.
3. 典型收包引擎
3.1 libpcap
libpcap的包捕获机制是在数据链路层增长一个旁路处理,不干扰系统自身的网路协议栈的处理,对发送和接收的数据包经过Linux内核作过滤和缓冲处理,最后直接传递给上层应用程序。
- 数据包到达网卡设备。
- 网卡设备依据配置进行DMA操做。(第1次拷贝:网卡寄存器->内核为网卡分配的缓冲区ring buffer)
- 网卡发送中断,唤醒处理器。
- 驱动软件从ring buffer中读取,填充内核skbuff结构(第2次拷贝:内核网卡缓冲区ring buffer->内核专用数据结构skbuff)
- 接着调用netif_receive_skb函数:
5.1 若是有抓包程序,由网络分接口进入BPF过滤器,将规则匹配的报文拷贝到系统内核缓存 (第3次拷贝)。BPF为每个要求服务的抓包程序关联一个filter和两个buffer。BPF分配buffer 且一般状况下它的额度是4KB the store buffer 被使用来接收来自适配器的数据; the hold buffer被使用来拷贝包到应用程序。
5.2 处理数据链路层的桥接功能;
5.3 根据skb->protocol字段肯定上层协议并提交给网络层处理,进入网络协议栈,进行高层处理。
6. libpcap绕过了Linux内核收包流程中协议栈部分的处理,使得用户空间API能够直接调用套接字PF_PACKET从链路层驱动程序中得到数据报文的拷贝,将其从内核缓冲区拷贝至用户空间缓冲区(第4次拷贝)
3.2 libpcap-mmap
libpcap-mmap是对旧的libpcap实现的改进,新版本的libpcap基本都采用packet_mmap机制。PACKET_MMAP经过mmap,减小一次内存拷贝(第4次拷贝没有了),减小了频繁的系统调用,大大提升了报文捕获的效率。
3.3 PF_RING
参考:PF_RING学习笔记
咱们看到以前libpcap有4次内存拷贝。
libpcap_mmap有3次内存拷贝。
PF_RING提出的核心解决方案即是减小报文在传输过程当中的拷贝次数。
咱们能够看到,相对与libpcap_mmap来讲,pfring容许用户空间内存直接和rx_buffer作mmap。
这又减小了一次拷贝(libpcap_mmap的第2次拷贝:rx_buffer->skb)
PF-RING ZC实现了DNA(Direct NIC Access 直接网卡访问)技术,将用户内存空间映射到rx_buffer。经过这样的方式,减小了一次拷贝(libpcap的第3次拷贝,每一个BPF过滤器有一个拷贝)。这就是零拷贝。
其缺点是,只有一个应用能够在某个时间打开DMA ring(请注意,如今的网卡能够具备多个RX / TX队列,从而就能够在每一个队列上同时一个应用程序),换而言之,用户态的多个应用须要彼此沟通才能分发数据包。
3.5 DPDK
pf-ring zc和dpdk都可以实现数据包的零拷贝,二者均旁路了内核,可是实现原理略有不一样。pf-ring zc经过zc驱动(也在应用层)接管数据包,dpdk基于UIO实现。
1、UIO+mmap 实现零拷贝(zero copy)
UIO(Userspace I/O)是运行在用户空间的I/O技术。Linux系统中通常的驱动设备都是运行在内核空间,而在用户空间用应用程序调用便可,而UIO则是将驱动的不多一部分运行在内核空间,而在用户空间实现驱动的绝大多数功能。
采用Linux提供UIO机制,能够旁路Kernel,将全部报文处理的工做在用户空间完成。
2、UIO+PMD 减小中断和CPU上下文切换
DPDK的UIO驱动屏蔽了硬件发出中断,而后在用户态采用主动轮询的方式,这种模式被称为PMD(Poll Mode Driver)。
与DPDK相比,pf-ring(no zc)使用的是NAPI polling和应用层polling,而pf-ring zc与DPDK相似,仅使用应用层polling。
3、HugePages 减小TLB miss
在操做系统引入MMU(Memory Management Unit)后,CPU读取内存的数据须要两次访问内存。第一次要查询页表将逻辑地址转换为物理地址,而后访问该物理地址读取数据或指令。
为了减小页数过多,页表过大而致使的查询时间过长的问题,便引入了TLB(Translation Lookaside Buffer),可翻译为地址转换缓冲器。TLB是一个内存管理单元,通常存储在寄存器中,里面存储了当前最可能被访问到的一小部分页表项。
引入TLB后,CPU会首先去TLB中寻址,因为TLB存放在寄存器中,且其只包含一小部分页表项,所以查询速度很是快。若TLB中寻址成功(TLB hit),则无需再去RAM中查询页表;若TLB中寻址失败(TLB miss),则须要去RAM中查询页表,查询到后,会将该页更新至TLB中。
而DPDK采用HugePages ,在x86-64下支持2MB、1GB的页大小,大大下降了总页个数和页表的大小,从而大大下降TLB miss的概率,提高CPU寻址性能。
4、其它优化
SNA(Shared-nothing Architecture),软件架构去中心化,尽可能避免全局共享,带来全局竞争,失去横向扩展的能力。NUMA体系下不跨Node远程使用内存。
SIMD(Single Instruction Multiple Data),从最先的mmx/sse到最新的avx2,SIMD的能力一直在加强。DPDK采用批量同时处理多个包,再用向量编程,一个周期内对全部包进行处理。好比,memcpy就使用SIMD来提升速度。
cpu affinity
3.6 XDP
参考:DPDK and XDP
xdp表明eXpress数据路径,使用ebpf 作包过滤,相对于dpdk将数据包直接送到用户态,用用户态当作快速数据处理平面,xdp是在驱动层建立了一个数据快速平面。
在数据被网卡硬件dma到内存,分配skb以前,对数据包进行处理。
请注意,XDP并无对数据包作Kernel bypass,它只是提早作了一点预检而已。
相对于DPDK,XDP具备如下优势:
- 无需第三方代码库和许可
- 同时支持轮询式和中断式网络
- 无需分配大页
- 无需专用的CPU
- 无需定义新的安全网络模型
XDP的使用场景包括:
- DDoS防护
- 防火墙
- 基于XDP_TX的负载均衡
- 网络统计
- 复杂网络采样
- 高速交易平台
4. 无锁队列技术
在报文捕获的流程中,无锁队列是一个很重要的数据结构。生产者(网卡)写数据和消费者(用户态程序)读数据,不加锁,能极大提高效率。
无锁队列实现主要依赖的技术有:
-
CAS原子指令操做
CAS(Compare and Swap,比较并替换)原子指令,用来保障数据的一致性。
指令有三个参数,当前内存值 V、旧的预期值 A、更新的值 B,当且仅当预期值 A和内存值 V相同时,将内存值修改成 B并返回true,不然什么都不作,并返回false。 -
内存屏障
执行运算的时候,每一个CPU核心从内存读到各自的缓存中,结束后再从缓存更新到内存,这会引发线程间数据的不一样步,故须要内存屏障强制把写缓冲区或高速缓存中的数据等写回主内存。
主要分为读屏障和写屏障:读屏障可让 cache中的数据失效,强制从新从主内存加载数据;
写屏障能使cache 中的数据更新写入主内存。
在实现 valotitle关键字中就用到了内存屏障,从而保证线程A对此变量的修改,其余线程获取的值为最新的值。
5. 基于pfring/dpdk的应用
按照传统的观念,中间网络节点只能按照协议栈的层次一层一层地解析数据包,所谓路由器是三层设备,交换机是二层设备,防火墙分为二层防火墙和三层防火墙。
使用PF_RING/DPDK的设备,它能够将数据包直接从网卡的芯片DMA到你机器上的内存,而后你经过一个应用程序而不是内核协议栈来处理数据包。
至于说你的应用程序怎么处置数据包,我来列举几个:
1.深度解析数据包,按照各类你能够想到的粒度来解析会话,而后记录审计信息;
2.提供高性能的入侵检测功能;
3.转发数据包,按照路由器的方式。可是再也不仅仅经过查询路由表的方式进行IP路由,而是能够经过各类各样的方式,转发表彻底由你本身定义,好比实现一个通用的SDN流表;
4.根据上面第2点的含义,你能够决定哪些包被丢弃,这就是一个高性能的防火墙。
相比内核协议栈的串行解决方案,使用PF_RING/DPDK是一个更加高效的方案,不但高效,并且灵活。若是你拥有多核心的处理器,你甚至能够在用户态并行处理数据包的各个层信息。
参考:
http://crad.ict.ac.cn/fileup/HTML/2017-6-1300.shtml https://coolshell.cn/articles/8239.html https://cloud.tencent.com/developer/article/1521276 https://blog.csdn.net/dandelionj/article/details/16980571 https://my.oschina.net/moooofly/blog/898798 https://blog.csdn.net/dog250/article/details/77993218