封装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