码迷,mamicode.com
首页 > 其他好文 > 详细

dpdk

时间:2020-09-12 21:38:33      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:eal   硬件   元组   第一个   device   --   sign   用户态   核心   

基于 OS 内核的数据传输有什么弊端

1、中断处理:当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。

2、内存拷贝:正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操作甚至占到了数据包整个处理流程的 57.1%。

3、上下文切换:频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。另外,在基于多线程的服务器设计框架中,线程间的调度也会产生频繁的上下文切换开销,同样,锁竞争的耗能也是一个非常严重的问题。

4、局部性失效:如今主流的处理器都是多个核心的,这意味着一个数据包的处理可能跨多个 CPU 核心,比如一个数据包可能中断在 cpu0,内核态处理在 cpu1,用户态处理在 cpu2,这样跨多个核心,容易造成 CPU 缓存失效,造成局部性失效。如果是 NUMA 架构,更会造成跨 NUMA 访问内存,性能受到很大影响。

5、内存管理:传统服务器内存页为 4K,为了提高内存的访问速度,避免 cache miss,可以增加 cache 中映射表的条目,但这又会影响 CPU 的检索效率。

综合以上问题,可以看出内核本身就是一个非常大的瓶颈所在。那很明显解决方案就是想办法绕过内核。

解决方案探讨

1、控制层和数据层分离:将数据包处理、内存管理、处理器调度等任务转移到用户空间去完成,而内核仅仅负责部分控制指令的处理。这样就不存在上述所说的系统中断、上下文切换、系统调用、系统调度等等问题。

2、多核技术:使用多核编程技术代替多线程技术,并设置 CPU 的亲和性,将线程和 CPU 核进行一比一绑定,减少彼此之间调度切换。

3、NUMA 亲和性:针对 NUMA 系统,尽量使 CPU 核使用所在 NUMA 节点的内存,避免跨内存访问。

4、大页内存:使用大页内存代替普通的内存,减少 cache-miss。

5、无锁技术:采用无锁技术解决资源竞争问题。

 

DPDK 为 Intel 处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux 系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。

也就是 DPDK 绕过了 Linux 内核协议栈对数据包的处理过程,在用户空间实现了一套数据平面来进行数据包的收发与处理。在内核看来,DPDK 就是一个普通的用户态进程,它的编译、连接和加载方式和普通程序没有什么两样。

传统收报流程

硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理完毕通知用户层

用户层收包-->网络层--->逻辑层--->业务层

dpdk 网络数据流程

硬件中断--->放弃中断流程 用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层

 

总结:

DPDK 绕过了 Linux 内核协议栈,加速数据的处理,用户可以在用户空间定制协议栈,满足自己的应用需求,目前出现了很多基于 DPDK 的高性能网络框架,OVS 和 VPP 是常用的数据面框架,mTCP 和 f-stack 是常用的用户态协议栈,SPDK 是存储性能加速器,很多大公司都在使用 DPDK 来优化网络性能。

 

DPDK技术优点

1. 通过UIO技术将报文拷贝到应用空间处理,规避不必要的内存拷贝和系统调用,便于快速迭代优化。

2. 通过大页内存HUGEPAGE,降低cache miss(访存开销),利用内存多通道交错访问提高内存访问有效带宽,即提高命中率,进而提高cpu访问速度。

3.通过CPU亲和性,绑定网卡和线程到固定的core,减少cpu任务切换。特定任务可以被指定只在某个核上工作,避免线程在不同核间频繁切换,保证更多的cache命中。

4.通过无锁队列,减少资源竞争。cache行对齐,预取数据,多元数据批量操作。

5. 通过轮询可在包处理时避免中断上下文切换的开销。

 

DPDK、网卡、用户应用程序、内核之间的关系

1.PMD:Pool Mode Driver,轮询模式驱动,通过非中断,以及数据帧进出应用缓冲区内存的零拷贝机制,提高发送/接受数据帧的效率。

2.流分类:Flow Classification,为N元组匹配和LPM(最长前缀匹配)提供优化的查找算法。

3.环队列:Ring Queue,针对单个或多个数据包生产者、单个数据包消费者的出入队列提供无锁机制,有效减少系统开销。

4. MBUF缓冲区管理:分配内存创建缓冲区,并通过建立MBUF对象,封装实际数据帧,供应用程序使用。

5.EAL:Environment Abstract Layer,环境抽象(适配)层,PMD初始化、CPU内核和DPDK线程配置/绑定、设置HugePage大页内存等系统初始化。

 
rte是指 runtime environment,eal 是指 environment abstraction layer。DPDK 的主要对外函数接口都以 rte_ 作为前缀。

初始化处理
 
初始化基础运行环境:int rte_eal_init(int argc, char **argv); 参数参考源码\lib\librte_eal\common\eal_common_options.c
rte_eal_init 本身所完成的工作很复杂,它读取入口参数,解析并保存作为 DPDK 运行的系统信息,依赖这些信息,构建一个针对包处理设计的运行环境。主要动作分解如下:

配置初始化
内存初始化
内存池初始化
队列初始化
告警初始化
中断初始化
PCI 初始化
定时器初始化
检测内存本地化(NUMA)
插件初始化
主线程初始化
轮询设备初始化
建立主从线程通道
将从线程设置在等待模式
PCI 设备的探测与初始化

DPDK 面向多核设计,程序会试图独占运行在逻辑核(lcore)上。main 函数里重要的是启动多核运行环境,RTE_LCORE_FOREACH_SLAVE(lcore_id)如名所示,遍历所有EAL 指定可以使用的 lcore,然后通过 rte_eal_remote_launch 在每个 lcore 上,启动被指定的线程。

int rte_eal_remote_launch(int (*f)(void *), void *arg, unsigned slave_id); 
第一个参数是从线程,是被征召的线程;
第二个参数是传给从线程的参数;
第三个参数是指定的逻辑核,从线程会执行在这个 core 上。
 
分配内存池 rte_pktmbuf_pool_create,入口参数是指定 rte_socket_id(),考虑了本地内存使用的范例。调用 port_init(portid, mbuf_pool) 初始化网口的配置,最后调用lcore_main() 进行主处理流程。
 
网口设置:初始化配置结束后,启动端口 int rte_eth_dev_start(uint8_t port_id);
完成后,读取 MAC 地址,打开网卡的混杂模式设置,允许所有报文进入。
/* Configure the Ethernet device. */

retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
/* Allocate and set up 1 RX queue per Ethernet port. */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, rte_eth_dev_socket_id(port), NULL, mbuf_pool);
}
/* Allocate and set up 1 TX queue per Ethernet port. */
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, rte_eth_dev_socket_id(port), NULL);
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
/* Display the port MAC address. */
struct ether_addr addr;
rte_eth_macaddr_get(port, &addr);
/* Enable RX in promiscuous mode for the Ethernet device. */
rte_eth_promiscuous_enable(port);

基于端口队列的报文收发函数:
static inline uint16_t rte_eth_rx_burst(uint8_t port_id, uint16_t queue_id, struct rte_mbuf **rx_pkts, const uint16_t nb_pkts)
static inline uint16_t rte_eth_tx_burst(uint8_t port_id, uint16_t queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)

 

rte_eal_cleanup()

rte_malloc

rte_malloc(),

rte_memzone_reserve()

rte_eth_dev_count();

 

 

 

dpdk

标签:eal   硬件   元组   第一个   device   --   sign   用户态   核心   

原文地址:https://www.cnblogs.com/xuperior/p/13579706.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!