DPDK基础概念和原理简单介绍

1.背景

随着网卡的快速发展,Linux内核在数据包处理方面的性能不足已成为现代网络系统中的瓶颈。然而,互联网日益增长的需求要求更高效的网络处理方案。因此,内核绕过技术逐渐受到关注。目前已有多种类似技术,例如DPDK、NETMAP和PF-ring。内核绕过的核心思想是:Linux仅用于处理控制流,所有数据流均在用户空间进行处理。这样一来,可以避免因内核数据包复制、线程调度、系统调用和中断带来的性能瓶颈。此外,通过多优化方法,内核绕过还能实现更高的性能。在众多技术中,由于与内核调度的隔离程度更高以及活跃的社区支持,DPDK得到了广泛应用。

2.DPDK是做什么的

核心作用

  • 提供高速数据平面能力:让通用 x86/ARM 服务器能够处理千万级甚至亿级 PPS(每秒数据包数),接近专用网络设备的性能。

  • 绕过内核瓶颈:传统内核网络栈存在中断、系统调用、数据拷贝、协议栈开销大等问题,DPDK 通过用户态驱动、轮询、零拷贝等技术消除这些瓶颈。

  • 提供一套完整的开发库:包括用户态网卡驱动(PMD)、大页内存管理、无锁队列、内存池、批量收发包 API 等,方便开发者快速构建高性能网络应用。

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

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

典型应用场景

  • 软件网络设备:高性能虚拟路由器、防火墙、负载均衡器、IDS/IPS、DDoS 防护等。

  • 电信核心网:5G UPF、EPC、IMS 等数据面网元。

  • 云计算与虚拟化:虚拟交换机(如 OVS-DPDK)、VPC 网关、容器网络加速。

  • SDN/NFV:网络功能虚拟化中的数据面加速。

  • 高频交易/低延迟系统:对网络延迟极度敏感的场景。

  • dpdk 作为优秀的用户空间高性能数据包加速套件,现在已经作为一个模块被用在多个网络数据处理方案中,用来提高性能。

    • 腾讯云 DNSPod 团队开源的高性能网络开发框架,

      定位非常清晰:让用户态程序拥有内核绕过的性能,同时保留成熟协议栈的兼容性

      https://github.com/F-Stack/f-stack

    • OVS虚拟交换机

      Open vSwitch 是一个多核虚拟交换机平台,支持标准的管理接口和开放可扩展的可编程接口,支持第三方的控制接入。
      https://github.com/openvswitch/ovs

    • VPP

      VPP 是 cisco 开源的一个高性能的包处理框架,提供了 交换/路由 功能,在虚拟化环境中,使它可以当做一个虚拟交换机来使用。在一个类 SDN 的处理框架中,它往往充当数据面的角色。经研究表明,VPP 性能要好于 ovs+dpdk 的组合,但它更适用于NFV,适合做特定功能的网络模块。
      https://wiki.fd.io/view/VPP

3.传统网络包路径对比DPDK路径

众所周知,我们用 Java、C 或 Python 编写的应用都跑在用户态。网络数据包都得先过网卡驱动进入内核态,再由内核丢给用户态程序。这种内核/用户态的反复横跳不仅带来了昂贵的上下文切换开销,还涉及多次数据拷贝。在高频 IO 场景下,这些‘看不见’的系统损耗往往就是拖慢程序响应的元凶。

image-20260812140242871

DPDK 框架通过 UIO (Userspace I/O)VFIO 驱动屏蔽了标准的内核协议栈,将网卡控制权直接接管至用户态。利用 DMA(直接内存访问) 技术配合大页内存 ,DPDK 实现了真正的零拷贝(Zero-copy)机制。数据包无需经过内核协议栈的层层拆解,而是直接从物理网卡投送至预分配的用户态内存空间,从而彻底消除了上下文切换与数据搬运的性能损耗。

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

  1. 中断处理。当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。
  2. 内存拷贝。正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间。 物理地址 虚拟地址 cou搬移 硬件视角
  3. 上下文切换。频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。

3.2 DPDK的数据传输

DPDK拦截中断,不触发后续中断流程,并绕过协议栈,通过UIO技术将网卡收到的报文拷贝到应用层处理,报文不再经过内核协议栈。减少了中断,DPDK的包全部在用户控件使用内存池管理,内核控件与用户空间的内存交互不用进行拷贝,只做控制权转移,减少报文拷贝过程,提高报文的转发效率。

4.DPDK总体框架

image-20260812095437100

如上图所示dpkd总体框架图,底层通过UIO技术来实现用户态和内核态的数据包交互,然后将数据包的控制权交给应用层的应用程序处理,dpdk只是一个框架它提供各种功能库。

4.1DPDK为什么能绕过内核?为什么快?

  1. 网卡交给用户态 —— 通过 UIO/VFIO 框架,网卡寄存器直接 mmap 到用户空间,程序直接操控硬件,不经过内核驱动。

  2. 数据包直落用户内存 —— 网卡通过 DMA 把收到的包直接写入预先分配的大页内存,用户态程序零拷贝读取。

  3. 轮询代替中断 —— 不用内核中断机制,DPDK 线程死循环轮询网卡,省去中断上下文切换开销。

4.1.1 网卡交给用户态----UIO (用户空间的 I/O 技术)

image-20260812102425863

内核 作用
UIO Framework Linux 内核提供的 UIO 核心框架,负责统一管理所有 UIO 设备。它屏蔽了内核内部复杂的子系统差异,为用户空间驱动提供统一的访问接口。
Driver(内核态) 这是一个极简的内核驱动,只负责最基础、必须在内核完成的工作,比如:
• 注册 UIO 设备
• 申请和管理内存资源
• 注册中断处理(通常只是收到中断后唤醒用户空间,不做实际业务处理)
internal kernel functions 内核内部的各种底层 API(如内存管理、中断子系统、设备模型等)。这些 API 不稳定,会随内核版本变化。UIO 框架的作用之一就是隔离用户空间驱动与这些不稳定 API 的直接接触
接口 作用
/dev/uioX 字符设备文件( uio0、uio1)。用户空间驱动通过它进行:
read() — 阻塞等待中断(收到中断后读取事件计数)
mmap() — 将设备的物理地址空间映射到用户进程虚拟地址空间,实现零拷贝的寄存器访问
sysfs 提供设备的配置信息和元数据(如内存映射区域的大小、偏移、名称等),通常位于 /sys/class/uio/uioX/ 下。用户空间驱动可以读取这些文件来了解硬件资源布局。
用户 作用
Driver(用户态) 真正的业务逻辑驱动。它运行在用户空间,通过 /dev/uioXsysfs 与内核交互。开发者可以用常规 C/C++ 甚至其他语言编写,无需编写内核模块,调试更方便,崩溃也不会导致系统宕机。
Application 最终的应用程序,通过用户态驱动来操作硬件。

dpdk 能够绕过内核协议栈,本质上是得益于 UIO 技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。

UIO 设备的实现机制其实是对用户空间暴露文件接口,比如当注册一个 UIO 设备 uioX,就会出现文件 /dev/uioX,对该文件的读写就是对设备内存的读写。除此之外,对设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成。

for bdf in 0002:02:00.0 0002:03:00.0 0002:04:00.0 0002:05:00.0
do
    group=$(basename "$(readlink -f /sys/bus/pci/devices/$bdf/iommu_group)")
    driver=$(basename "$(readlink -f /sys/bus/pci/devices/$bdf/driver)")
    echo "$bdf -> /dev/vfio/$group, driver=$driver"
done

4.1.2 数据包直落用户内存----内存池技术

DMA介绍

DMA(Direct Memory Access,直接内存访问) 是一种让外设不经过 CPU 直接读写内存的硬件机制。

拓展到RDMA

RDMA(远程直接内存访问) 是 DMA 技术的"网络化"延伸。如果说 DMA 是本机外设绕过 CPU 直接读写内存,那么 RDMA 就是远程机器的网卡绕过远程 CPU,直接读写远程内存

大页内存管理

dpdk 实现了一组大页内存分配、使用和释放的 API,上层应用可以很方便使用 API 申请使用大页内存,同时也兼容普通的内存申请。

  1. 普通页常见为 4 KiB。高速数据面访问大量报文缓冲区时,页表和 TLB 容量可能成为瓶颈。2 MiB 或 1 GiB Hugepage 能用较少的页表项覆盖更大的内存区域,并便于规划 DMA 内存。

    Hugepage 的主要价值是扩大 TLB 覆盖范围、避免换出并方便管理 DMA 内存。

image-20260812172854550

内存池技术

dpdk 在用户空间实现了一套精巧的内存池技术,内核空间和用户空间的内存交互不进行拷贝,只做控制权转移。这样,当收发数据包时,就减少了内存拷贝的开销。

image-20260812143124223

上半部分:收包流程(RX Path)

方向:网卡 → DPDK → 内核网络栈

步骤 动作 说明
rte_eth_rx_burst() RX Thread 从网卡批量接收数据包,放入 DPDK 的 mbuf(数据包缓冲区)。
rx_q(接收队列) RX Thread 将 mbuf 指针放入 rx_q 无锁环形队列。
mbuf to sk_buf KNI kthread(内核线程)从 rx_q 取出 mbuf,将其转换为内核的 sk_buff 结构。
netif_rx() KNI kthread 调用 netif_rx(),将包送入 Linux 内核网络栈,内核像处理普通网卡包一样处理它。
free_q(释放队列) 内核处理完后,mbuf 的释放请求放入 free_q
rte_pktmbuf_free() RX Thread 从 free_q 回收 mbuf,归还到 Mbuf mempool 复用。
下半部分:发包流程(TX Path)

方向:内核网络栈 → DPDK → 网卡

步骤 动作 说明
kni_net_tx() 内核网络栈有包要发送时,调用 KNI 驱动的发送函数。
sk_buf to mbuf Net Stack kthread 将内核的 sk_buff 转换为 DPDK 的 mbuf
mbuf_cache 转换后的 mbuf 先放入本地缓存,批量攒够后再处理,减少队列操作开销。
tx_q(发送队列) Net Stack kthread 将 mbuf 放入 tx_q 无锁环形队列。
rte_eth_tx_burst() TX Thread 从 tx_q 批量取出 mbuf,调用 DPDK API 直接发送到网卡。
rte_pktmbuf_free() 发送完成后,mbuf 释放回 Mbuf mempool
alloc_q(分配队列) TX Thread 通过 rte_pktmbuf_alloc() 从内存池申请新的 mbuf,放入 alloc_q 供内核侧后续使用。

4.1.3 轮询代替中断----poll-mode网卡驱动

DPDK网卡驱动基于轮询方式收包,避免了中断开销。

轮询的优势是报文到达后不必等待线程唤醒;代价是空闲时仍消耗 CPU。可以使用空闲检测、监控线程、频率调整或事件模式降低功耗,但这会重新引入状态切换和一定的唤醒时延。

网络空闲时CPU长期空转,会带来能耗问题。所以,DPDK推出Interrupt DPDK模式。

它的原理就是没包可处理时进入睡眠,改为中断通知。并且可以和其他进程共享同个CPU Core,但是DPDK进程会有更高调度优先级。

image-20260815150749403

4.2无锁环形队列

dpdk 基于 Linux 内核的无锁环形缓冲 kfifo 实现了自己的一套无锁机制。支持单生产者入列/单消费者出列和多生产者入列/多消费者出列操作,在数据传输的时候,提高性能的同时还能保证数据的同步。

4.3NUMA

dpdk 内存分配上通过 proc 提供的内存信息,使 CPU 核心尽量使用靠近其所在节点的内存,避免了跨 NUMA 节点远程访问内存的性能问题。

4.4CPU 亲和性

dpdk 利用 CPU 的亲和性将一个线程或多个线程绑定到一个或多个 CPU 上,这样在线程执行过程中,就不会被随意调度,一方面减少了线程间的频繁切换带来的开销,另一方面避免了 CPU 缓存的局部失效性,增加了 CPU 缓存的命中率。

4.5多核调度框架

dpdk 基于多核架构,一般会有主从核之分,主核负责完成各个模块的初始化,从核负责具体的业务处理。

image-20260812144820808

posted @ 2026-09-11 22:00  桂洛克船长  阅读(5)  评论(0)    收藏  举报