封装dpdk接口成静(动)态库(同源同宿、多通道队列收包),然后在C++应用中使用方法
来源:互联网 发布:腰部赘肉 知乎 编辑:程序博客网 时间:2024/05/19 04:03
dpdk的接口全部都是C语言实现的,它的makefile模版也很简单,只需改变其宏就可以在生成可执行文件、静态库、动态库之间切换。本篇博文主要给出如果使用dpdk封装成静态库后,在C++应用程序中编译使用的方法。
一、封装dpdk抓包接口
源代码实现的功能:
1、可通过配置文件进行配置多个队列收取数据包,并且保证数据包的同源同宿(或者负载均衡,二者取其一,具体方法须修改网卡驱动程序);
2、实时显示收到总包数、Gbps、pps。
jz_dpdk_api.h
/********************************************************************Copyright (C), 2001-2017, XXX Co., Ltd.File Name:jz_dpdk_api.hVersion: Initial DraftAuthor:Created:2017.06.19Last Modified:2017.06.19Description:Api of capture packets by dpdk,with max speed 10Gbps.*********************************************************************/#ifndef JZ_DPDK_H#define JZ_DPDK_H#include <stdio.h>#include <stdint.h>#include <rte_eal.h>#include <rte_ethdev.h>#ifdef __cplusplus extern "C" { #endif /** rx_number:从配置文件中读取的线程数,用于调用者绑定cpu以及开启多线程*/extern int rx_number;/** 描述: 初始化函数,对端口、队列、大页内存等配置。* 参数: 无。* 返回值: 初始化成功返回0,失败返回-1。*/int jz_dpdk_init(int argc,char **argv);/** 描述: 接收包。* 参数: uint8_t : 端口号 uint16_t: 队列号 struct rte_mbuf * :存储接收到包的数组指针,一次性返回多个数据包。* uint16_t : 一次接收包的个数,需要根据包长以及配置的单个页大小来设定。* 如:页大小为2M,设定接收包大概是1500字节,那么设置32.* 返回值:返回收到包的个数,如果为-1,则说明收包失败。*/int jz_dpdk_recv_pkts(uint8_t port_id,uint16_t queue_id,struct rte_mbuf *pkts[],const uint16_t nb_pkts);/** 描述: 释放dpdk收包内存,注意:此接口必须在收包成功后调用。* 参数:struct rte_mbuf * :存储接收到包的数组指针。* uint16_t :收到包的个数,与接口 jz_dpdk_recv_pkt 配合使用。* 返回值: 无。*/void jz_dpdk_free(struct rte_mbuf *pkts[], uint16_t nb_pkts);/** 描述: 返回当前收包状态,如pps,总包数。* 参数: 无。* 返回值: 无。*/void jz_dpdk_current_stat(void);#ifdef __cplusplus }; #endif #endif
jz_dpdk_api.c
#include "jz_dpdk_api.h"#include "ini/iniparser.h"/**************************宏********************************/#define NUM_MBUFS_ 8191#define MBUF_CACHE_SIZE_ 512#define RX_RING_SIZE_ 128#define TX_RING_SIZE_ 512/**************************全局变量**************************/int rx_number = 0;uint64_t total_pkts[512] = {0};uint64_t last_total_pkts[512] = {0};uint64_t total_pkts_bytes[512] = {0};uint64_t last_total_pkts_bytes[512] = {0};uint64_t last_time = 0;static const struct rte_eth_conf port_conf_default = {.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN, .mq_mode = ETH_MQ_RX_RSS }, .rx_adv_conf = { .rss_conf = { .rss_key = NULL, .rss_hf = ETH_RSS_IP, } }};/**************************函数声明*************************/static int config_init(const char *filename);static int port_init(int port,struct rte_mempool *mbuf_pool);static void recv_pkt_init(void);/**************************接口实现*************************/int jz_dpdk_init(int argc,char **argv){ const char *filename = "conf.ini"; // 0.配置文件读取 rx_number = config_init(filename); if( rx_number < 1) { printf("错误:读取配置文件[conf.ini] 失败!\n"); return -1; } // 1. DPDK EAL初始化 if( rte_eal_init(argc,argv) < 0 ) { printf("错误:EAL初始化失败!\n"); return -1; } else { printf("EAL初始化成功。\n"); } // 2. 检测至少有一个端口(即网口) if( rte_eth_dev_count() != 1) { printf("错误:只能有一个端口(网口)。\n"); return -1; } // 3. 内存池的创建 struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("JZ_MBUF_POOL", NUM_MBUFS_ * rte_eth_dev_count(), MBUF_CACHE_SIZE_, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); if( NULL == mbuf_pool ) { printf("错误:创建内存池失败。\n"); return -1; } else { printf("创建内存池成功。\n"); } // 4. 对端口进行初始化 int port = 0; for (; port < rte_eth_dev_count(); port++) { if( port_init(port, mbuf_pool) < 0) { printf("错误:端口初始化失败。\n"); return -1; } else { printf("端口初始化成功。\n"); } } recv_pkt_init(); printf("\n恭喜:jz_dpdk 初始化成功!\n"); return 0;}int jz_dpdk_recv_pkts(uint8_t port_id,uint16_t queue_id,struct rte_mbuf *pkts[],const uint16_t nb_pkts){ uint16_t nb_rx = rte_eth_rx_burst(port_id, queue_id, pkts, nb_pkts); if( nb_rx > 0) { total_pkts[queue_id] += nb_rx; uint16_t i; for ( i = 0; i < nb_rx; i++) { total_pkts_bytes[queue_id] += pkts[i]->pkt_len + 24; } } return nb_rx;}void jz_dpdk_free(struct rte_mbuf *pkts[], uint16_t nb_pkts){uint16_t i; for ( i = 0; i < nb_pkts; i++) { rte_pktmbuf_free(pkts[i]); } }void jz_dpdk_current_stat(void){ uint64_t now = time(NULL); uint64_t total_pps = 0; uint64_t total_pkts_all = 0; float total_gbps = 0.0; uint16_t i = 0; printf("---------------------------------------------------------\n"); for(; i < rx_number; i++) { uint64_t pps = (total_pkts[i] - last_total_pkts[i]) / (now - last_time); float gbps = (float)((total_pkts_bytes[i] - last_total_pkts_bytes[i]) * 8) / ((now - last_time) * 1000000000); last_total_pkts_bytes[i] = total_pkts_bytes[i]; last_total_pkts[i] = total_pkts[i]; total_pps += pps; total_gbps += gbps; total_pkts_all += total_pkts[i]; printf("Rx[%d] rate: [current %lu pps/%0.3f Gbps][total: %lu pkts]\n",i,pps,gbps,total_pkts[i]); } printf("Rx[All] rate: [current %lu pps/%0.3f Gbps][total: %lu pkts]\n",total_pps,total_gbps,total_pkts_all); last_time = now;}int config_init(const char *filename){ int thread_number = 0; dictionary *ini = iniparser_load(filename); if( NULL == ini) { return thread_number; } thread_number = iniparser_getint(ini, "config:thread_number",-1); printf("成功配置了 %d 个线程用于接收数据包.\n",thread_number); return thread_number;}int port_init(int port,struct rte_mempool *mbuf_pool){ struct rte_eth_conf port_conf = port_conf_default; const uint16_t rx_rings = rx_number, tx_rings = rx_number; int retval = 0; uint16_t q = 0; struct rte_eth_link link; retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);if (retval != 0){ printf("ERROR: %d , %s ,%d ",retval,__FUNCTION__,__LINE__); return retval; } /* 初始化队列. */ 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); if (retval < 0) { printf("ERROR: %d , %s ,%d ",retval,__FUNCTION__,__LINE__); return retval; } } 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); if (retval < 0) { printf("ERROR: %d , %s ,%d ",retval,__FUNCTION__,__LINE__); return retval; } } /* 启动端口 */retval = rte_eth_dev_start(port);if (retval < 0){ printf("ERROR: %d , %s ,%d ",retval,__FUNCTION__,__LINE__); return retval; } /* 开启混杂模式 */rte_eth_promiscuous_enable(port); /* 查看端口状态 */rte_eth_link_get(port, &link);if (link.link_status) {printf("Port %d Link Up - speed %u Mbps - %s\n",port, (uint32_t) link.link_speed, (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex\n"));}else {printf("Port %d Link Down\n",port);} return 0;}void recv_pkt_init(void){ uint8_t port; for (port = 0; port < rte_eth_dev_count(); port++) { if (rte_eth_dev_socket_id(port) > 0 && rte_eth_dev_socket_id(port) != (int)rte_socket_id()) { printf("WARNING, port %u is on remote NUMA node to " "polling thread.\n\tPerformance will " "not be optimal.\n", port); } } printf("Core %u forwarding packets. [Ctrl+C to quit]\n",rte_lcore_id());}
二、通过dpdk的模版makefile生成 libdpdk_demo.a
ifeq ($(RTE_SDK),)$(error "Please define RTE_SDK environment variable")endif# Default target, can be overriden by command line or environmentRTE_TARGET ?= x86_64-native-linuxapp-gccinclude $(RTE_SDK)/mk/rte.vars.mk# binary name#APP = mainLIB = libdpdk_demo.so#SHARED = libdpdk_demo.so# all source are stored in SRCS-ySRCS-y := jz_dpdk_api.c ini/dictionary.c ini/iniparser.c # main.cCFLAGS += -O0 -gCFLAGS += $(WERROR_FLAGS)#include $(RTE_SDK)/mk/rte.extapp.mkinclude $(RTE_SDK)/mk/rte.extlib.mk#include $(RTE_SDK)/mk/rte.extshared.mk
上面这个makefile,完全就是dpdk给出的模版。很强大很简单,不需要做太多的配置。二、将上面编译生成的库引用到C++项目中:
main.cpp
#include <iostream>#include "jz_dpdk_api.h"int main(int argc,char **argv){jz_dpdk_init(argc,argv);std::cout << "hello,world." << std::endl;return 0;}
Makefile:
CC := g++TARGET := mainCFLAGS := -O0 -g -Wall -march=native # 这里直接粗暴的将dpdk所有的库都链接进来,以达到对所有网卡都支持DPDK_LIBS:= dpdk_demo rte_ethdev rte_mbuf rte_mempool rte_eal rte_acl rte_kni rte_pmd_af_packet rte_pmd_kni rte_pmd_vhost \rte_bitratestats rte_kvargs rte_pmd_ark rte_pmd_lio rte_pmd_virtio \rte_cfgfile rte_latencystats rte_pmd_avp rte_pmd_nfp rte_pmd_vmxnet3_uio \rte_cmdline rte_lpm rte_pmd_bnxt rte_pmd_null rte_port \rte_cryptodev rte_pmd_bond rte_pmd_null_crypto rte_power \rte_distributor rte_pmd_crypto_scheduler rte_pmd_octeontx_ssovf rte_reorder \rte_mempool_ring rte_pmd_cxgbe rte_pmd_qede rte_ring \rte_efd rte_mempool_stack rte_pmd_e1000 rte_pmd_ring rte_sched \rte_meter rte_pmd_ena rte_pmd_sfc_efx rte_table \rte_eventdev rte_metrics rte_pmd_enic rte_pmd_skeleton_event rte_timer \rte_hash rte_net rte_pmd_fm10k rte_pmd_sw_event rte_vhost \rte_ip_frag rte_pdump rte_pmd_i40e rte_pmd_tap \rte_jobstats rte_pipeline rte_pmd_ixgbe rte_pmd_thunderx_nicvfLIBS:= pthread LIBPATH=/usr/local/libINCLUDE=/usr/local/include/dpdk SRC := $(wildcard *.cpp)OBJ := $(patsubst %cpp,%o,$(SRC))all:$(TARGET)%.o:%.cpp$(CC) $(CFLAGS) -c $< -I$(INCLUDE) $(addprefix -L,$(LIBPATH)) $(addprefix -l,$(LIBS)) $(TARGET):$(OBJ)$(CC) $(CFLAGS) -o $@ $^ $(addprefix -L,$(LIBPATH)) -Wl,--whole-archive $(addprefix -l,$(DPDK_LIBS)) -Wl,--no-whole-archive $(addprefix -l,$(LIBS)) -ldl -lrt.PHONY:cleanclean:-rm -f $(TARGET) $(OBJ)
其中最重要的2处:
1、需要-march=native ,不然会出现指令错误。
2、需要 -Wl,--whole-archive $(addprefix -l,$(DPDK_LIBS)) -Wl,--no-whole-archive,不然程序运行会找不到网卡。
阅读全文
0 0
- 封装dpdk接口成静(动)态库(同源同宿、多通道队列收包),然后在C++应用中使用方法
- DPDK(17):网卡多队列技术与RSS功能介绍、DPDK多队列
- DPDK中Log的使用方法
- DPDK(一):DPDK安装
- Berkeley db使用方法简介(c接口)
- Berkeley db使用方法简介(c接口)
- Berkeley db使用方法简介(c接口)
- Berkeley db使用方法简介(c接口)
- Berkeley db使用方法简介(c接口)
- c语言数据结构应用-数组队列(无锁队列)在多线程中的使用
- 在虚拟机中部署dpdk
- 在Docker中运行DPDK
- C/C 动/静 态库
- DPDK-KERNEL NIC INTERFACE(内核NIC接口)
- DPDK 收发包处理流程(二)(网卡初始化)
- DPDK 收发包处理流程(二)(网卡初始化)
- DPDK收发包处理流程-----(一)网卡初始化
- DPDK收发包处理流程-----(一)网卡初始化
- Ubuntu 启动桌面失败
- 使用GitLab版本管理工具教程
- VPS安装开启TCP BBR提速工具
- 修改fitnesse源码->自定义页面脚本中各种Table(ScriptTable,DecisionTable)的类实例变量
- java中static关键字的用法
- 封装dpdk接口成静(动)态库(同源同宿、多通道队列收包),然后在C++应用中使用方法
- 使用storm处理消息队列中的日志信息遇见的错误
- Java注释@interface的用法
- win 10 安装robomongo(studio 3T)
- c语言之常量指针与指针常量
- 微信分享调用JS -- c#篇
- 容器技术如何改变游戏服务器托管行业
- ffmpeg实现音频resample(重采样)
- 关于返回函数一章节中遇到的问题