dpdk实现arp协议,icmp协议-----架构优化

仓库:wait/1010-dpdk


之前的代码都是写在main中的while里面,接收数据,解析协议帧,打包协议帧,发送数据,定时器发送arp请求,都是在main loop中。

这种写法不符合实际的开发规范,现在对整体的代码架构进行优化,对接收的数据异步解耦。


收发数据 与 协议帧的解析打包 实现分离。异步解耦。

创建两个队列,一个in ring 一个out ring。

接收到的数据都放在in ring中,发送数据时就从out ring中获取数据。

main loop只实现俩三个功能:

  1. 接收数据,放入in ring
  2. 发送数据,从out ring中取数据
  3. 定时器发送arp请求

再开辟一个线程用来pkt_process专门处理协议帧的解析和打包,打包好的协议帧放入out ring中。

实现收发与解析相互分离,只通过两个队列实现交互。


两个队列的设计使用的是dpdk提供的环形队列。

队列中的元素是rte_mbuf。

使用单例模式,创建一个全局变量,用来管理in ring和out ring。

复制代码
struct inout_ring
{
    struct rte_ring *in;
    struct rte_ring *out;
};

extern struct inout_ring *rInst;

先进行初始化

复制代码
    struct inout_ring *ringInit(void)
{
    if(rInst == NULL)
    {
        rInst = rte_malloc("in_out_ring",sizeof(struct inout_ring),0);
        memset(rInst,0,sizeof(struct inout_ring));
    }
    return rInst;
}

    struct inout_ring *ring = ringInit();
    if(ring == NULL){
        rte_exit(EXIT_FAILURE,"ring init fail\n");
    }
    if(ring->in == NULL){
        ring->in = rte_ring_create("in_ring",RING_SIZE,rte_socket_id(),RING_F_SP_ENQ | RING_F_SC_DEQ);
    }
    if(ring->out == NULL){
        ring->out = rte_ring_create("out_ring",RING_SIZE,rte_socket_id(),RING_F_SP_ENQ | RING_F_SC_DEQ);
    }

通过ringInit确保只有一个全局变量rInst,然后对rInst中的in ring 和 out ring申请内存。

rte_ring_create("out_ring",RING_SIZE,rte_socket_id(),RING_F_SP_ENQ | RING_F_SC_DEQ);

out_ring: 名字

RING_SIZE: ring的容量,最多能够存放多少rte_mbuf 指针

rte_socket_id(): 获取当前的NUMA结点

RING_F_SP_ENQ: 单生产者入队

RING_F_SC_DEQ: 单消费者出队

NUMA:CPU临近的内存块,CPU访问其的速度快于访问其他内存的速度。一个 NUMA 节点可能包含多个 CPU 核。

S:single 单个

P:producer 生产者 入队,写入队列

C:consumer 消费者 出队,从队列中获取数据

ENQ:enqueue 入队

DEQ:dequeue 出队

对于入队出队操作,dpdk提供了很多种接口。

复制代码
// 多生产者入队
rte_ring_mp_enqueue_burst();
// 单生产者入队
rte_ring_sp_enqueue_burst();
// 都可以
rte_ring_enqueue_burst();
// 多消费者 出队
rte_ring_mc_dequeue_burst();
// 单消费者 出队
rte_ring_sc_dequeue_burst();
// 都可以
rte_ring_dequeue_burst();

对于我们的架构,有两个线程,mian线程和,pkt process线程。

mian线程:in ring入队。out ring 出队。

pkt process线程:in ring出队。out ring入队。

对于in ring和out ring的入队出队操作都只有一个线程,所以使用单生产者入队,单消费者出队即可。

对于in ring和out ring的释放。

in ring的释放在pkt process线程中释放。

out ring在main线程中释放。

通常对线程是把任务丢给这个线程去做,做完后需要返回。但是dpdk的多线程不需要返回,线程之间通过队列进行通信,而不是return。一个dpdk的线程对应一个 lcore ,logical core,DPDK的逻辑核。通常一个逻辑核对应一个cpu核心。

每个线程对对应一个lcore,这个lcore长期存在的这个任务函数。

复制代码
/****************************** in ring******************************/ 
        struct rte_mbuf *rx[BURST_SIZE];
        unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId,0,rx,BURST_SIZE);
        
        if(num_recvd > BURST_SIZE)
        {
            rte_exit(EXIT_FAILURE,"rx burst get fail\n");
        }else if( num_recvd > 0){
            printf("in ring \n");
            // tx,rx 是一个数组,都指向第一个元素(void **)rx 与 (void **)&rx 效果完全一致
            // 而pkt_process_w中的指针,所以必须加&
            rte_ring_sp_enqueue_burst(ring->in,(void **)&rx,num_recvd,NULL);
        }
/****************************** out ring ******************************/   
        
        struct rte_mbuf *tx[BURST_SIZE];
        unsigned num_tx = rte_ring_sc_dequeue_burst(ring->out,(void **)&tx,BURST_SIZE,NULL);
        if( num_tx > 0)
        {
            printf("out ring \n");
            rte_eth_tx_burst(gDpdkPortId,0,tx,num_tx);

            unsigned i = 0;
            for(i = 0;i<num_tx;i++){
                rte_pktmbuf_free(tx[i]);
            }
        }

文章参考<零声教育>的C/C++linux服务期高级架构系统教程学习:0voice · GitHub

相关推荐
G018_star sky♬1 小时前
为什么java新建一个对象,需要obj o = new obj()这种写法
java·开发语言
星恒讯工业路由器2 小时前
工业网络设备选型:导轨式、机架式与车载式的部署差异
开发语言·网络·数据库·php·工业路由器·导轨式·车载式
2601_962885722 小时前
如何用 Python 把多只股票对齐成一张宽表?
开发语言·python
mldong2 小时前
为了 token 自由,我把 jeeflow 工作流引擎用 MoonBit 写了一遍
后端·架构
Hello.Reader3 小时前
一个开源项目,让 AI 做逆向不再靠猜reverse-skill 架构解析与本地实战
人工智能·架构·开源
蹊黎4 小时前
C++中vector类的部分简单实现
开发语言·c++·visual studio
半个落月10 小时前
LangChain Agent 中间件实战(上):生命周期、状态、短路与工具包
javascript·langchain
半个落月10 小时前
LangChain Agent 中间件实战(下):DeepAgents文件权限、长期记忆与对话摘要
javascript·langchain
程序猿追11 小时前
kotlin-retry 接入鸿蒙后,怎样确认它真的会停止重试
开发语言·kotlin·harmonyos