zoukankan      html  css  js  c++  java
  • DPDK-----内存管理之mempool

    无论对于DPDK做怎样的增量开发,了解DPDK的内存管理模式有利于在满足自己产品功能同时最大化的提高性能;

    一:Mempool的基本单元概念(https://doc.dpdk.org/guides/prog_guide/mempool_lib.html

    Mempool是固定大小的对象分配器。 在DPDK中,它由名称唯一标识,并且使用mempool操作来存储空闲对象。Mempool的组织是通过三个部分实现的:

    • mempool对象节点:mempool的对象挂接在 static struct rte_tailq_elem rte_mempool_tailq 全局队列中,可以通过名字进行唯一标识符;此队列只是mempool的一个对象指示结构,并不是实际的内存区;
    • mempool实际内存区: struct rte_memzone 是实际分配的连续内存空间,存储所创建的mempool对象;
    • ring无锁队列:作为一个无锁环形队列 struct rte_ring ,存储着mempool对象的指针,提供了方便存取使用mempool的空间的办法。

    二:一般结构

    如图所示,mempool的对象通过与ring无锁队列建立关联方便存取;同时,为了减少多核访问造成的冲突,引入了local_cache对象缓冲区。该local_cache非硬件上的cache,而是为了减少多核访问ring造成的临界区访问,

    coreX app会优先访问该local_cache上的对象。入队的时候优先入local_cache中,出队的时候优先从local_cache中出队。

    三:mempool的创建和使用

    先注意一下 rte_mempool_create 的参数中的两个 mp_init  和 obj_init ,前者负责初始化mempool中配置的私有参数,如在数据包中加入的我们自己的私有结构;后者负责初始化每个mempool对象。我们然后按照mempool的3个关键部分展开说明。

    (1)mempool头结构的创建

       mempool头结构包含3个部分: struct rte_mempool , struct rte_mempool_cache 和mempool private。创建是在 rte_mempool_create_empty() 中完成的,看这个函数,先进行了对齐的检查

        RTE_BUILD_BUG_ON((sizeof(struct rte_mempool) &
                  RTE_CACHE_LINE_MASK) != 0);
        RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_cache) &
                  RTE_CACHE_LINE_MASK) != 0);

    然后从mempool队列中取出头节点,我们创建的mempool结构填充好,就挂接在这个节点上。接下来做一些检查工作和创建flag的设置。

     rte_mempool_calc_obj_size() 计算了每个obj的大小,这个obj又是由三个部分组成的,header_size、elt_size、trailer_size,即头,数据区,尾。在没有开启RTE_LIBRTE_MEMPOOL_DEBUG调试时,没有尾部分;头部分的结构为: struct rte_mempool_objhdr ,

    通过这个头部,mempool中的obj都是链接到队列中的,所以,提供了遍历obj的方式(尽管很少这么用)。函数返回最后计算对齐后的obj的大小,为后面分配空间提供依据。

    然后分配了一个mempool队列条目,为后面挂接在队列做准备。

        /* try to allocate tailq entry */
        te = rte_zmalloc("MEMPOOL_TAILQ_ENTRY", sizeof(*te), 0);
        if (te == NULL) {
            RTE_LOG(ERR, MEMPOOL, "Cannot allocate tailq entry!
    ");
            goto exit_unlock;
        }

    接下来,就是计算整个mempool头结构多大。

        mempool_size = MEMPOOL_HEADER_SIZE(mp, cache_size);
        mempool_size += private_data_size;
        mempool_size = RTE_ALIGN_CEIL(mempool_size, RTE_MEMPOOL_ALIGN);

    这里指的是计算mempool的头结构的大小。而不是内存池实际的大小。在这里可以清晰的看出这个mempool头结构是由三部分组成的。cache计算的是所有核上的cache之和。

    然后,使用 rte_memzone_reserve() 分配这个mempool头结构大小的空间,填充mempool结构体,并把mempool头结构中的cache地址分配给mempool。初始化这部分cache。

    最后就是挂接mempool结构。  TAILQ_INSERT_TAIL(mempool_list, te, next); (这里上了锁?)。

    (2)mempool实际空间的创建

    这部分的创建是在函数 rte_mempool_populate_default(struct rte_mempool *mp) 中完成的。

    首先计算为这些元素需要分配多大的空间, rte_mempool_ops_calc_mem_size() 

    接着 rte_memzone_reserve_aligned() 分配空间。把元素添加到mempool,实际上就是把申请的空间分给每个元素。

    (3)ring的创建

    先看到的是这么一段代码:

    static int
    mempool_ops_alloc_once(struct rte_mempool *mp)
    {
        int ret;
    
        /* create the internal ring if not already done */
        if ((mp->flags & MEMPOOL_F_POOL_CREATED) == 0) {
            ret = rte_mempool_ops_alloc(mp);
            if (ret != 0)
                return ret;
            mp->flags |= MEMPOOL_F_POOL_CREATED;
        }
        return 0;
    }

    这就是创建ring的过程咯,其中的函数rte_mempool_ops_alloc()就是实现。那么,对应的ops->alloc()在哪注册的呢?

        /*
         * Since we have 4 combinations of the SP/SC/MP/MC examine the flags to
         * set the correct index into the table of ops structs.
         */
        if ((flags & MEMPOOL_F_SP_PUT) && (flags & MEMPOOL_F_SC_GET))
            ret = rte_mempool_set_ops_byname(mp, "ring_sp_sc", NULL);
        else if (flags & MEMPOOL_F_SP_PUT)
            ret = rte_mempool_set_ops_byname(mp, "ring_sp_mc", NULL);
        else if (flags & MEMPOOL_F_SC_GET)
            ret = rte_mempool_set_ops_byname(mp, "ring_mp_sc", NULL);
        else
            ret = rte_mempool_set_ops_byname(mp, "ring_mp_mc", NULL);

    就是根据ring的类型,来注册对应的操作函数,如默认的就是ring_mp_mc,多生产者多消费者模型,其操作函数不难找到:

    static const struct rte_mempool_ops ops_mp_mc = {
        .name = "ring_mp_mc",
        .alloc = common_ring_alloc,
        .free = common_ring_free,
        .enqueue = common_ring_mp_enqueue,
        .dequeue = common_ring_mc_dequeue,
        .get_count = common_ring_get_count,
    };

    接下来,又分配了一个 struct rte_mempool_memhdr *memhdr; 结构的变量,就是这个变量管理着mempool的实际内存区,它记录着mempool实际地址区的物理地址,虚拟地址,长度等信息。

    再然后,就是把每个元素对应到mempool池中了: mempool_add_elem() 。在其中,把每个元素都挂在了elt_list中,可以遍历每个元素。最后 rte_mempool_ops_enqueue_bulk(struct rte_mempool *mp, void * const *obj_table, ,最终,把元素对应的地址入队,这样,mempool中的每个元素都放入了ring中。

    四:mempool的使用及实践

    mempool的常见使用是获取元素空间和释放空间。

    待补充

    部分内容转载自:https://www.cnblogs.com/yhp-smarthome/p/6687175.html

  • 相关阅读:
    小程序(四):模板
    小程序(三):授权登录
    小程序(二)
    小程序(一)
    从零开始学习微信小程序
    flex 弹性布局
    如何使用docker进行shadsocks环境开发配置
    eclipse 设置注释模板
    idea 2019.1.3最新注册码
    centos7安装rabbitmq简单方式
  • 原文地址:https://www.cnblogs.com/rex-2018-cloud/p/10039065.html
Copyright © 2011-2022 走看看