dpdk常用接口指北


难度 中等

基于dpdk18.11版本,其他版本注意区别。

一般用dpdk做网口管理、网口收发包、内存管理等操作。

dpdk环境初始化

int rte_eal_init(int argc, char **argv);

通用参数配置

-b blacklist
-w whitelist
-c coremask
-l corelist
-s service coremask
-S service corelist
-m size of memory
-n force number of channels
-r force number of ranks
-d force loading of external driver

DPDK多进程使用的关键启动参数:

--in-memory :在这种模式下,DPDK不会在任何文件系统(hugetlbfs或其他文件系统)上创建文件
--proc-type:指定一个dpdk进程是主进程还是副进程(参数值就用上面的primary或是secondary,或者是auto)
--file-prefix:允许非合作的进程拥有不同的内存区域。主副进程默认文件路径/var/run/.rte_config,同一个处理组的主副进程使用相同的参数,
如果想运行多个主进程,这个参数就必须指定!
--socket-mem:设置从hugepages分配多大的存储空间。默认会用掉所有的hugepages,所以建议指定这个参数,不管是单cpu还是在NUMA中。
eg:单socket,--socket-mem=512;在numa中,--socket-mem=512,512;多个socket间用‘,’号隔开;

网口管理

内存管理

DPDK通过使用hugetlbfs,减少CPU TLB表的Miss次数,提高性能。

初始化

DPDK的内存初始化工作,主要是将hugetlbfs的配置的大内存页,根据其映射的物理地址是否连续、属于哪个Socket等,有效的组织起来,为后续管理提供便利。

 /*
 * when we initialize the hugepage info, everything goes
 * to socket 0 by default. it will later get sorted by memory
 * initialization procedure.
 */
int
eal_hugepage_info_init(void)

eal_hugepage_info_init()主要是获取配置好的Hugetlbfs的相关信息,并将其保存在struct internal_config数据结构中。

  1. 读取/sys/kernel/mm/hugepages目录下的各个子目录,通过判断目录名称中包含”hugepages-“字符串,获取hugetlbfs的相关子目录,并获取hugetlbfs配置的内存页大小。
  2. 通过读取/proc/mounts信息,找到hugetlbfs的挂载点
  3. 通过读取/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages,获取配置的hugepages个数
  4. 以打开文件的方式,打开挂载点目录,为其FD设置互斥锁
  5. 将internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]按内存页的大小排序。
/* create memory configuration in shared/mmap memory. Take out
 * a write lock on the memsegs, so we can auto-detect primary/secondary.
  * This means we never close the file while running (auto-close on exit).
 * We also don't lock the whole file, so that in future we can use read-locks
  * on other parts, e.g. memzones, to detect if there are running secondary
 * processes. */
static void
rte_eal_config_create(void)

rte_eal_config_create()主要是初始化rte_config.mem_config。如果是以root用户运行dpdk程序的话,rte_config.mem_config指向/var/run/.rte_config文件mmap的一段sizeof(struct rte_mem_config)大小的内存

int
rte_eal_hugepage_init(void)

rte_eal_hugepage_init()主要是在/mnt/huge目录下创建hugetlbfs配置的内存页数(在本文中就是64)的rtemap_xx文件,并为每个rtemap_xx文件做mmap映射,保证mmap后的虚拟地址与实际的物理地址是一样的.

  1. 创建nr_hugepages个struct hugepage_file数组,即有多少个内存页,创建多少个struct hugepage_file数据结构
  2. 有多少个内存页,在挂载点目录下创建多少个rtemap_xx文件,如下所示,并为每一个文件mmap一个hugepage_sz大小的内存区域
  3. 通过读取/proc/self/pagemap页表文件,得到本进程中虚拟地址与物理地址的映射关系。使用上一步中,每个rtemap_xx文件mmap得到的虚拟地址,除以操作系统内存页的大小4k,得到一个偏移量。根据这个偏移量,在/prox/self/pagemap中,得到物理地址的页框,假设为page,那么,物理页框page乘以操作系统内存页的大小4K,再加上虚拟地址的页偏移,就是物理地址。每个rtemap_xx映射的物理地址保存在对应的hugepage_file->physaddr中
  4. 读取/proc/self/numa_maps,得到每个rtemap_xx文件mmap得到的虚拟地址在哪个Socket上,即,哪个CPU上。其socketid保存在对应的hugepage_file->socket_id中
  5. 在hugepage_file数组中,根据物理地址,按从小到大的顺序,将hugepage_file排序
  6. 根据按物理地址排序后的结果,判断物理地址是否连续,重新mmap /mnt/huge/retmap_xx文件,使得物理地址等于第二次mmap后的虚拟地址。第二次mmap得到的虚拟地址保存在对应的hugepage_file->final_va中
static int
rte_eal_memdevice_init(void)

rte_eal_memdevice_init()初始化rte_config.mem_config->nchannel和rte_config.mem_config->nrank。

  rte_config.mem_config->nchannel = 启动参数中“-n”指定的值,不能为0,不能大于4。

  rte_config.mem_config->nrank = 启动参数中“-r”指定的值。不能为0,不能大于16。

/*
 * Init the memzone subsystem
 */
int
rte_eal_memzone_init(void)

rte_eal_memzone_init()主要负责初始化rte_config.mem_config->free_memseg[]及rte_config.mem_config->memzone[]。其中,rte_config.mem_config->free_memseg[]记录空闲的rte_config.mem_config->memseg[]。

rte_mempool内存管理

DPDK以两种方式对外提供内存管理方法,一个是rte_mempool,主要用于网卡数据包的收发;一个是rte_malloc,主要为应用程序提供内存使用接口。

rte_mempool由函数rte_mempool_create()负责创建,从rte_config.mem_config->free_memseg[]中取出合适大小的内存,放到rte_config.mem_config->memzone[]中。

struct rte_mempool *
rte_mempool_create(const char *name, unsigned n, unsigned elt_size,
       unsigned cache_size, unsigned private_data_size,
           rte_mempool_ctor_t *mp_init, void *mp_init_arg,
       rte_mempool_obj_cb_t *obj_init, void *obj_init_arg,
       int socket_id, unsigned flags);

struct rte_mempool *
rte_mempool_create_empty(const char *name, unsigned n, unsigned elt_size,
  unsigned cache_size, unsigned private_data_size,
  int socket_id, unsigned flags);

rte_mempool由函数rte_mempool_create()负责创建。首先创建rte_ring,再创建rte_mempool,并建立两者之间的关联。

  1. rte_ring_create()创建rte_ring无锁队列
  2. 创建并初始化rte_mempool

rte_malloc内存管理

rte_malloc()为程序运行过程中分配内存,模拟从堆中动态分配内存空间。

rte_malloc_socket():指定从哪个socket上分配内存空间,默认是指定SOCKET_ID_ANY,即,程序在哪个socket上运行,就从哪个socket上分配内存。如果指定的socket上没有合适的内存空间,就再从其它socket上分配。

malloc_heap_alloc():从rte_config.mem_config->malloc_heaps[]数组中找到指定socket对应的堆(使用struct malloc_heap描述堆),即,从这个堆中分配空间。如果该堆是第一次使用,还没有被初始化过,则调用malloc_heap_init()初始化;首先,调用find_suitable_element()在堆中查找是否有合适内存可以分配,如果没有,则调用malloc_heap_add_memzone()在rte_config.mem_config->memzone[]中给堆分配一块内存。最后,调用malloc_elem_alloc()在堆中,将需要分配的内存划分出去。

收发包

队列设置

// 创建收发包队列使用的内存
struct rte_mempool *
rte_pktmbuf_pool_create(const char *name, unsigned n,
  unsigned cache_size, uint16_t priv_size, uint16_t data_room_size,
  int socket_id);

// 检查Rx和Tx描述符(mbuf)的数量是否满足网卡的描述符限制,不满足将其调整为边界(改变其值)
// nb_rx_desc和nb_tx_desc默认值为1024
int rte_eth_dev_adjust_nb_rx_tx_desc(uint16_t port_id,
             uint16_t *nb_rx_desc,
             uint16_t *nb_tx_desc);

int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
    uint16_t nb_rx_desc, unsigned int socket_id,
    const struct rte_eth_rxconf *rx_conf,
    struct rte_mempool *mb_pool);

int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
    uint16_t nb_tx_desc, unsigned int socket_id,
    const struct rte_eth_txconf *tx_conf);
  • 配置下发 rte_eth_dev_configure

    rte_eth_dev_configure接口使得应用层可以对网卡进行配置下发操作,将应用层提供的网卡信息保存到网卡数据空间struct rte_eth_dev_data中,这样pmd用户态驱动就可以根据应用层提供的配置对网卡进行设置操作。另外这个接口还会为网卡的发送队列,接收队列开辟二级指针空间;

  • 网卡接收队列设置 rte_eth_rx_queue_setup

    网卡接收队列设置,主要是为网卡开辟接收队列空间,也就是一级指针。上面的rte_eth_dev_configure接口只是开辟一个二级指针空间,这里是为网卡接收队列开辟一级指针空间。

收发包接口

  • 收包
rte_eth_rx_burst
  • 发包
rte_eth_tx_burst

样例

int main(int argc, char **argv)
{
    int ret = rte_eal_init(argc, argv);
}

reference

  1. https://www.sdnlab.com/24519.html
  2. https://blog.csdn.net/shaoyunzhe/article/details/7887096
  3. https://blog.csdn.net/hejin_some/article/details/72146794
  4. https://blog.csdn.net/wangquan1992/article/details/104061618

文章作者: growdu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 growdu !
  目录
分类导航
随笔2 AI27 算法1 计算机基础13 博客搭建7 ChatGPT2 集群63 计算机通信1 数据库34 数据库深入80 DPDK26 Docker11 Elasticsearch4 编辑工具4 FAQ1 Go Web1 hometown2 编程语言16 网络9 OPC1 Linux38 openGauss4 页面12 PostgreSQL54 程序员自我修养1 协议11 成长之路1 stock1 存储5 工具20 VPP18 视频作品1 Vue13 Web1 代码示例11 数据库15 BenchmarkSQL1 PostgreSQL 源码修炼之路14
最热文章
1
13 逻辑复制深入
数据库深入🔥 1570
2
0 Postgresql存储、索引及系统优化、主备切换
PostgreSQL🔥 1495
3
一文读懂openguass dcf网络模块
集群🔥 1420
4
逻辑复制源码分析
数据库深入🔥 1327
5
PostgreSQL 分区表:从一行 `PARTITION BY` 到路由热路径的全链路拆解
数据库🔥 1094
6
applyparallelworker.c 之 LA 端源码深度解析:Leader Apply Worker 的指挥中枢
数据库深入🔥 1082
7
PostgreSQL Background Worker 全解:从 `RegisterBackgroundWorker` 到逻辑复制 4 类 worker 的全生命周期
数据库🔥 1078
8
PostgreSQL的后台进程walsender分析 - 关系型数据库 - 亿速云
PostgreSQL🔥 1033
9
PostgreSQL 逻辑复制的监控:六张视图 + 一组可执行 SQL,把 publisher/subscriber 的速率与健康度彻底看透
数据库🔥 1032
10
PostgreSQL 逻辑复制支持 DDL 之后:DDL 与 DML 的时序难题(重点:分区表)
数据库🔥 999
11
reorderbuffer.c 源码深度解析:PostgreSQL 逻辑复制的"事务重组引擎
数据库深入🔥 953
12
PostgreSQL 内核开发:读取一张表的 9 步标准流程与缓存全景
数据库🔥 938
13
从 `postgres` 二进制到生产级守护 —— PostgreSQL 最外层模块与启动全流程拆解
数据库🔥 936
14
支持逻辑复制同步 DDL 适配 SQL Server 方案
数据库深入🔥 934
15
PostgreSQL 逻辑复制的 ReorderBuffer 与事务机制:从一行 WAL 到一致性变更流的全链路绑定
数据库🔥 913
16
DDL同步架构(美化版)
数据库深入🔥 908
17
PostgreSQL Latch 机制详解:从一行 SetLatch 到 epoll 的内核之旅
数据库🔥 871
18
pgbench 源码全解:一个 C 文件如何撑起 PostgreSQL 官方压测工具
数据库🔥 860
19
PostgreSQL libpq 机制与缓冲区详解
数据库🔥 850
20
PostgreSQL 逻辑复制 spill 文件深度剖析:从 `xid-*.spill` 到 TPC-C 的增长方程
数据库🔥 845