当前位置: 首页 > Linux

DPDK分析、原理和学习路线

时间:2023-04-06 04:18:23 Linux

随着互联网的快速发展和云产业的迅速兴起,基础网络逐渐趋向于集成基于通用计算平台或模块化计算平台的架构,以支持多样化的网络功能.传统PC机在分布式计算平台上的优势更加明显。在这些海量数据处理或者海量用户的服务场景中,高性能的编程显得尤为重要。全文路线分析目前传统服务器架构及可能存在的问题,引出需求,提出DPDK开发包如何突破操作系统的限制,分析dpdk的整体架构,最后展开相关技术和场景。-前10年背景分析,网络程序性能优化的目标主要是解决C10K问题。它的研究主要集中在如何管理数万个并发客户端连接,如何在各种I/O框架下优化性能,优化一些操作系统参数。目前已经有很多服务器解决了C10K问题。Nginx和Lighttpd是两个优秀的事件驱动的web服务框架,Tornado和Django是基于python开发的非阻塞web框架。这些软件让C10K不再是问题。整体来看,为了满足不断增长的需求,分布式集群主要用于分担负载和响应大量用户请求。从结构上看,一个节点的服务器框架包括网络模块事件驱动模块隔离、多核业务分发模块业务层在单个节点上,核心的使用主要包括单线程服务器。优点是没有竞争。缺点是没有充分利用系统资源。多进程模型隔离性好,占用系统资源更多。缺点是进程之间难以共享资源。多线程模型充分利用系统资源,竞争需谨慎处理-需求分析-DPDK学习路线及视频讲解+qun720209036获取1.dpdkPCI原理及testpmd/l3fwd/skeletion2.kni数据流3.dpdk实现dns4.dpdk高性能网关实现五、半虚拟化virtio/vhost的加速综合分析在处理海量网络密集型数据时,一般的选择是横向扩展节点,但节点的增加会增加设备成本以及变相的技术风险,当集群的节点数量达到一定数量后,节点之间的交互成本本身就会成为瓶颈。在某些场合,比如嵌入式设备的后台服务,是不可能搭建服务器集群的。因此,提高服务器本身的性能同样重要。-具体分析传统服务器可能存在以下潜在问题。异步模式的缺点。一般我们使用epoll来高效处理网络读写事件。在多线程服务器设计框架中,当没有请求时,线程会休眠。当数据到达时,相应的线程会被操作系统唤醒。也就是说,内核需要负责频繁的线程间上下文切换。我们是依赖操作系统的调度系统来服务于网络数据包的调度。在网络负载较重的场景下,只会导致核心满载不断地相互切换,进一步增加负载。那么就需要回归到最原始的方式,使用轮询的方式来完成所有的操作,以提高性能。协议栈的可扩展性Linix在诞生之初就是为电报控制而设计的。它的控制平面和数据转发平面没有分离,不适合处理大规模的网络数据包。并且为了全面支持用户空间的各种功能,协议栈中嵌入了大量用于对接的接口。如果应用程序能够直接接手网络包处理、内存管理、CPU调度,性能就能得到质的提升。为了达到这个目的,首先要解决的问题就是绕过linux内核协议栈,因为linux内核协议栈的性能不是很好,如果每个数据包都经过linux协议栈处理,会要很慢。WindRiver、6WindGate等公司自研内核协议栈声称LinuxUDP/TCP协议栈性能至少提升500%以上,Linux协议栈不用它也能用。如果不使用协议栈,则需要自己编写驱动程序,应用程序直接使用驱动程序的接口发送和接收消息。PF_RING、Netmap和intelDPDK可以帮助您完成这些任务,我们不需要自己花太多时间。Intel的官方测试文档给出了性能测试数据。性能测试在1SSandbridge-EP8*2.0GHz核心服务器上进行。没有内核协议栈的用户态吞吐量最高可达80Mpps(每个数据包处理消耗大约200个cpu时钟),相比之下,Linux内核协议栈的性能甚至达不到1Mpps。多核可扩展性多核可扩展性对于性能的提升也很重要,因为服务器中CPU频率的提升越来越慢,纳米级的工艺提升已经很困难了,但能做的就是让服务器拥有更多的CPU和内核,比如国家超算中心的天河二号,使用了3万多颗XeonE5来提升性能。在编程的过程中,即使是在多核环境下,很快也会遇到瓶颈。简单地增加处理器数量并不会线性提高程序的性能,反而会使整体性能越来越低。一是由于代码编写质量问题,没有充分发挥多核的并行性。另一个是服务器软硬件本身的一些特性成为了新的瓶颈,比如总线竞争、共享存储等诸多影响并行性能扩展的因素。那么,如何才能让程序在多个CPU核上并行扩展:尽量让每个核保持独立的数据结构;使用原子操作来避免冲突;使用无锁数据结构来避免线程相互等待;setCPUaffinity,将操作系统和应用进程绑定到特定的核心上,避免CPU资源竞争;NUMA架构下,尽量避免远程内存访问,以达到内存可扩展性。内存的访问速度永远赶不上缓存和CPU的频率。性能并行扩展,最好访问较少。从内存消耗来看,如果每个用户连接占用2K内存,那么10M用户就要消耗20G内存,操作系统的三级缓存连20M都达不到。这么多的并发连接必然会导致缓存失效。从而频繁访问内存获取数据。而一次内存访问大约需要300个cpuclocks,期间CPU几乎处于空闲状态。因此,减少内存访问次数,避免cachemiss是我们设计的目标。指针不应任意指向任何内存地址,因为指针的每次间接访问都可能导致多次缓存未命中。最好把需要访问的数据放在一起,这样可以加载到缓存中,一次性使用。按照4K页计算,32G的数据需要占用64M的页表,这样页表就连缓存都放不下,所以每次数据访问可能需要两次访问内存,所以建议使用2M或者甚至1G的大页表也能解决这个问题。-解决方案控制层留给Linux,所有其他数据层由应用程序处理。减少系统调度、系统调用、系统中断、上下文切换等摒弃Linux内核协议栈,将数据包传送到用户空间自定义协议栈使用多核编程技术代替多线程,将OS绑定到指定的coretorun对于SMP系统,使用CPU尽可能使用NUMA系统节点的内存,减少使用大页进行内存刷写,通过使用无锁技术解决竞争减少访问。DPDK只是给我们提供了解决问题的脚手架。-DPDK-概述Intel?DPDK全称IntelDataPlaneDevelopmentKit,是Intel提供的数据平面开发工具集。它为英特尔架构(IA)处理器架构下的用户空间高效数据包处理提供库函数和驱动程序支持。不同于为通用目的而设计的Linux系统,它侧重于网络应用中数据包的高性能处理。经验证,它可以在大多数Linux操作系统上运行,包括FreeBSD9.2、Fedorarelease18、Ubuntu12.04LTS、RedHatEnterpriseLinux6.3、SuseEnterpriseLinux11SP2。DPDK使用BSDLicense,极大方便了企业在其基础上实现自己的协议栈或应用。需要强调的是,DPDK应用运行在用户空间,使用自身提供的数据平面库收发数据包,绕过了Linux内核协议栈处理数据包的过程。Linux内核把DPDK应用程序看作一个普通的用户态进程,包括它的编译、连接和加载方式,都与普通程序没有区别。-整体结构主要包括以下几个核心网络层模块内存管理模块内核管理模块-网络模块-整体分析DPDK相对于传统的网络模块,对从内核层到用户层的网络过程进行了特殊处理,如下是传统的网络模块该结构与DPDK中的网络结构对比传统linux网络层:硬件中断--->取包分发给内核线程--->软件中断--->内核线程处理数据包在协议栈--->处理完成通知用户层用户层接收数据包-->网络层--->逻辑层--->业务层dpdk网络层:硬件中断--->放弃中断流程用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层比较总结了dpdk的优点:中断次数减少。减少了内存副本的数量。绕过linux协议栈,进入用户协议栈,用户获得协议栈的控制权,可以自定义协议栈,降低复杂度dpdk劣质内核栈,转移到用户层增加开发成本。低负载服务器不实用,会导致内核空闲。-具体分析拦截中断,不触发后续中断和处理流程,绕过协议栈。如下图,通过UIO可以重置内核中的终端回调行为,绕过协议栈的后续处理流程。触发器发送和接收数据包而不复制,减少内存复制开销。如图所示,所有的dpdk包都由用户空间的内存池管理。内核空间和用户空间的内存交互不需要复制,只需要控制传递即可。packettrans协议栈库dpdk为用户提供部分协议处理封装,方便用户自定义协议栈。-内存管理-hugepage技术Linux系统的内存管理依赖于内存,如下图Linux在内存管理中采用了受保护的虚拟地址方式,代码中的地址分为三类:逻辑地址,线性地址,和物理地址。程序使用的具体内存简单来说就是将逻辑地址通过分段机制映射转换为线性地址,再通过分页机制映射将线性地址转换为物理地址的过程。在实际使用中,只有线性地址映射成物理地址,需要至少四次从内存中读取页目录表(PageDirectory)和页表(PageTable)。CPU为了加快内核读取速度,在硬件上缓存了页表,也就是TLB。线性地址首先从TLB中获取缓存内存。如果不存在,则从内存表中获取。如果有直接映射,则直接从内存中读取。如果没有缺页中断,就是新分配物理内存,或者从硬盘读取swap。挑选。具体示意图如下:hupage.jpg正常页面大小为4K一个。如果是4K页,寻址如下。使用物理内存时,需要多级查找找到对应的内存page-1.gif4K页表是linux一般情况下的合适大小,但对于特殊应用,可以通过扩大页表面积来提高内存使用效率。dpdk使用hupage的思路是让程序尽可能独占内存防止内存换出,扩大页表提高hash命中率,扩大hugage技术使用的页表大小,将其设置为更适合高频内存使用程序的状态,并获得它具有以下优点。无需兑换。也就是说不存在因为内存空间不足而导致页面换入换出的问题,减少TLB负载。减少页表查询负载——NUMA为了解决单核带来的CPU性能不足,出现了SMP,但在传统的SMP系统中,所有处理器共享系统总线。当处理器数量增加时,系统总线竞争加剧,系统总线被称为新的瓶颈。NUMA(Non-UniformMemoryAccess)技术解决了SMP系统的可扩展性问题,已经成为当今高性能服务器的主流架构之一。一个NUMA系统节点一般由一组CPU和本地内存组成。NUMA调度器负责在同一节点的CPU之间调度进程,除非负载过高,否则会迁移到其他节点,但这会增加数据访问延迟。下图是2个CPU支持NUMA架构的示意图,每个CPU有4个物理核心。proc提供的关于dpdk内存分配的内存信息,使得CPU在访问远程内存时,尽可能使用靠近其所在节点的内存,避免影响效率。numa.png-kernelmanagementmoduleAffinity是进程的一个属性,表示进程调度器可以将进程调度到哪些CPU上。在Linux中,我们可以使用CPUaffinity将一个或多个进程绑定到一个或多个CPU。CPUAffinity分为两种,softaffinity和hardaffinity。Softaffinity只是一个建议,如果不可避免,调度器还是会把进程调度到其他CPU上。硬亲和性是调度器必须遵守的规则。为什么需要CPU绑定?提高CPU缓存的命中率。CPU不共享缓存。如果进程在CPU之间频繁切换,就需要不断地使老CPU的缓存失效。如果进程只在某个CPU上执行,就不会出现故障。在多个线程对同一个数据进行操作的情况下,如果将这些线程调度到一个处理器上,CPU缓存的命中率会大大提高。但是可能会导致并发性能的降低。如果线程是串行的,则没有这种效果。适用于时间敏感的应用在实时或时间敏感的应用中,我们可以将系统进程绑定到部分CPU,应用进程绑定到其余CPU。典型的设置是将应用程序绑定到一个CPU,并将所有其他进程绑定到其他CPU。-核管理结构dpdk启动时,会建立一个映射表,对系统的逻辑核心属性进行分析,统一管理。每个核心的主要属性如下。每个核心属性包括逻辑核心id、硬核id和numa节点id。dpdk会根据系统默认状态生成一对一的绑定映射表。用户可以根据需要更改映射表,后续dpdk框架会根据映射表进行核心绑定。类核心{lcore_id;//逻辑核心idcore_id;//硬核idsocket_id;//NUMAnodeid}classcorecoremap[]//所有逻辑核的映射表多核调度框架服务器启动时选择一个逻辑核作为主核,然后启动其他核作为从核。所有线程都根据映射表进行绑定。控制核心主要完成pci、内存、日志等系统初始化线程-竞争处理多线程在搭建服务器时经常要处理竞争问题。dpdk提供了一个无锁的循环队列,支持多线程操作,避免冲突和交换数据。数据包和其他需要大量复用的结构可以相互隔离,线程拥有独立的可用内存池。-性能分析Seastar是一个开源C++框架,用于在现代硬件上构建高性能服务器应用程序。该框架基于DPDK,使用Seastar开发的应用程序可以运行在Linux或OSv上。下面是seastar的介绍以及其开发的内存服务器与其他服务器的对比。可以看出dpdk的性能相对于传统框架有一定的优势,在网络密集的场景下表现良好。Seastar使用无共享模型,将所有请求分片到各个核心。Seastar提供多种网络堆栈选择,包括便于开发的传统Linux网络、Linux上用于快速用户空间网络的DPDK以及OSv上的本地网络。一种用于并发应用程序的高级新模型,为C++程序员提供高性能和能力创建全面的、可测试的高质量代码。一种无需耗时锁定即可在CPU内核之间共享信息的设计。性能分析-扩展-热更新DPDK在多个线程管理上的隔离性相当好。MasterCore和SlaveCore通过管道交换命令,MasterCore可以很方便的向SlaveCore下发服务。因此,可以很方便地利用这个特性来支持业务接口的热更新。-底层转发如下图所示。大多数程序交互都在传输层及以上,但DPDK从第二层切入。在构建需要大规模数据转发的集群时,可以采用三层转发,这样会减少数据包转发1层协议栈开销。