dpdk 多进程共享内存描述信息的机制

在 dpdk legacy memory 模型浅析 这篇文章中,我描述了 linux 平台 dpdk 早期版本的内存模型并分析了此模型下对 hugepage 的管理代码,在此模型中,dpdk primary 进程负责 layout hugepage 到 VA 中。为了支持多进程共享大页内存,dpdk primary 进程将大页内存初始化过程中得到的每个 memseg 与 hugepage file 信息通过 mmap 记录到特定文件中,secondary 进程通过 mmap 相同文件来访问这些信息并映射大页到相同的 VA 地址以共享 primary 进程映射的大页。

当 dpdk 进程完成了内存初始化后,在一个进程中使用 dpdk 内存分配接口分配的内存可以直接在其它 dpdk 进程中使用,可其它 dpdk 进程如何知道某个进程分配的某块区域的地址呢?显然需要引入新的描述信息来保存这一信息,且这些信息也需要在多进程之间共享。

dpdk 中抽象了 rte_config 结构来保存共享内存区域的描述信息。dpdk 多进程共享内存示意图如下:

中间的的区域为 dpdk 多进程需要共享的内容,大页内存共享 + 内存分配描述信息共享共同实现了 dpdk 多进程共享内存机制,在本文中,我将基于 dpdk-17.11 源码分析下 dpdk 中内存共享描述信息的原理。

rte_config 结构

rte_config 结构定义如下:

struct rte_config {uint32_t master_lcore;       /**< Id of the master lcore */uint32_t lcore_count;        /**< Number of available logical cores. */uint32_t service_lcore_count;/**< Number of available service cores. */enum rte_lcore_role_t lcore_role[RTE_MAX_LCORE]; /**< State of cores. *//** Primary or secondary configuration */enum rte_proc_type_t process_type;/** PA or VA mapping mode */enum rte_iova_mode iova_mode;/*** Pointer to memory configuration, which may be shared across multiple* DPDK instances*/struct rte_mem_config *mem_config;

它可以划分为两部分:

  1. 每个进程的私有数据
  2. 所有进程共享的共享内存描述数据

共享内存描述数据为 rte_mem_config 结构,此结构定义如下:

struct rte_mem_config {volatile uint32_t magic;   /**< Magic number - Sanity check. *//* memory topology */uint32_t nchannel;    /**< Number of channels (0 if unknown). */uint32_t nrank;       /**< Number of ranks (0 if unknown). *//*** current lock nest order*  - qlock->mlock (ring/hash/lpm)*  - mplock->qlock->mlock (mempool)* Notice:*  *ALWAYS* obtain qlock first if having to obtain both qlock and mlock*/rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */uint32_t memzone_cnt; /**< Number of allocated memzones *//* memory segments and zones */struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects *//* Heaps of Malloc per socket */struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];/* address of mem_config in primary process. used to map shared config into* exact same address the primary process maps it.*/uint64_t mem_cfg_addr;
} __attribute__((__packed__));

核心字段为 memseg、memzone、tailq_head、malloc_heaps,这些字段保存了整个共享的描述信息,实际运行中 dpdk 多进程之间内存的分配、查找、释放就是通过控制这些结构实现的,下面分别对这些结构的功能进行描述。

rte_memseg 结构

此结构描述了单块物理内存连续区域的信息,其定义如下:

struct rte_memseg {RTE_STD_C11union {phys_addr_t phys_addr;  /**< deprecated - Start physical address. */rte_iova_t iova;        /**< Start IO address. */};RTE_STD_C11union {void *addr;         /**< Start virtual address. */uint64_t addr_64;   /**< Makes sure addr is always 64 bits */};size_t len;               /**< Length of the segment. */uint64_t hugepage_sz;       /**< The pagesize of underlying memory */int32_t socket_id;          /**< NUMA socket ID. */uint32_t nchannel;          /**< Number of channels. */uint32_t nrank;             /**< Number of ranks. */
} __rte_packed;

它由 primary 进程初始化,保存大页的映射信息。secondary 进程通过访问此结构得到大页内存的 layout 信息,然后按照此信息 mmap 大页到 primary 进程中相同的地址以实现整个大页内存区域的共享。

rte_memzone 结构

此结构描述一块在 memseg 上分配的带标识符的内存区域,标识符为字符串,其定义如下:

struct rte_memzone {#define RTE_MEMZONE_NAMESIZE 32       /**< Maximum length of memory zone name.*/char name[RTE_MEMZONE_NAMESIZE];  /**< Name of the memory zone. */RTE_STD_C11union {phys_addr_t phys_addr;        /**< deprecated - Start physical address. */rte_iova_t iova;              /**< Start IO address. */};RTE_STD_C11union {void *addr;                   /**< Start virtual address. */uint64_t addr_64;             /**< Makes sure addr is always 64-bits */};size_t len;                       /**< Length of the memzone. */uint64_t hugepage_sz;             /**< The page size of underlying memory */int32_t socket_id;                /**< NUMA socket ID. */uint32_t flags;                   /**< Characteristics of this memzone. */uint32_t memseg_id;               /**< Memseg it belongs. */
} __attribute__((__packed__));

name 唯一区分每块内存区域,在其它进程中可以以字符串为参数遍历 rte_mem_config 结构的 memzone 数组,匹配每个 memzone 结构的 name 来获取到某块内存区域的 rte_memzone 结构进而得到内存其实地址实现共享,这就是 rte_memzone_lookup 的实现原理。

rte_tailq_head 结构

rte_tailq_head 是 dpdk 中双向链表的表头,dpdk 中为多个功能不同的内存结构注册不同的 tailq 链表用于多进程间共享。此结构定义如下:

struct rte_tailq_head {struct rte_tailq_entry_head tailq_head; /**< NOTE: must be first element */char name[RTE_TAILQ_NAMESIZE];
};

rte_mem_config 中保存所有的 rte_tailq_head 信息,每一个 rte_tailq_head 的 name 唯一标识一个 tailq。 dpdk 通过共享 rte_tailq_head 头并在大页上分配 rte_tailq_head 链表中链入的每一个 rte_tailq_entry 来实现多进程间基于 tailq 获取共享内存信息功能。

malloc_heap 结构

此结构维护大页内存分配信息,每个 numa 节点上有一个独立的结构以支持在特定 numa 节点上分配内存。其定义如下:

struct malloc_heap {rte_spinlock_t lock;LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];unsigned alloc_count;size_t total_size;
}

核心数据为 free_head,free_head 按照不同的内存大小范围分为不同的 free_list,free_list 链表的成员单位为 malloc_elem。free_head 内容分配方式如下:

 * Example element size ranges for a heap with five free lists:*   heap->free_head[0] - (0   , 2^8]*   heap->free_head[1] - (2^8 , 2^10]*   heap->free_head[2] - (2^10 ,2^12]*   heap->free_head[3] - (2^12, 2^14]*   heap->free_head[4] - (2^14, MAX_SIZE]

在内存释放时会根据大小放入对应的 free_head 中,在内存申请时也会根据大小计算需要查找的 free_head 来分配。malloc_elem 也在大页内存上分配,是 dpdk 内存分配的核心数据结构。

多进程与多线程自旋、读写锁

涉及共享数据一般都离不开锁的使用,rte_mem_config 中有如下读写锁定义:

 rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */

malloc_heap 中有如下自旋锁定义:

rte_spinlock_t lock;

这些锁用于多线程互斥保护,而它们又在多进程之间共享,也用于多进程之间的共享数据保护。dpdk memzone、tailq、mempool 一般用于分配固定格式的数据,一次分配、多次访问,符合读写锁的使用场景,而 malloc_heap 结构会频繁的分配、释放,故而使用自旋锁。

这些锁都基于 dpdk 原生实现,工作在用户态,有很好的性能,然而却不支持异常回收。在一个进程中使用没有问题,在多进程中使用时,如果某个进程在临界区内异常退出,就会产生死锁。在 程序启动顺序引发的血案之 dpdk 进程死锁 这篇文章中,我描述了这个问题,并提供了相应的解决方案。

总结

dpdk 多进程模型以共享大页内存为基础,实际实现中仅仅共享大页内存还不够,还需要共享内存分配的一些描述文件,这就是 rte_config 等结构的由来。使用这些结构,dpdk 能够灵活的在某个进程中找到在另外一个进程中分配的内存来使用,表面看上去好像不符合每个进程虚拟地址独立的特点,其实不过是因为这些虚拟地址指向的物理地址完全一致而已。

在实际使用场景中,常常需要在多线程与多进程模型之间进行选择,使用多线程机制不用创建许多进程,然而一个线程异常很大概率会影响整个进程的执行,而采用多进程模型,某个进程异常,其它进程仍旧能够正常工作。

基于可靠性考虑多进程模型是更好的选择。 dpdk 内存架构原生支持多进程模型,支持一个 primary 进程与多个 secondary,甚至于可以使用 --file-prefix 来创建多个 primary 进程,在开发时不用做额外修改即可使用。

dpdk 多进程共享内存描述信息的机制相关推荐

  1. python 共享内存_37. Python 多进程锁 多进程共享内存

    Lock组件 当我们用多进程来读写文件的时候,如果一个进程是写文件,一个进程是读文件, 如果两个文件同时进行,肯定是不行的,必须是文件写结束后,才可以进行读操作. 或者是多个进程在共享一些资源的时候, ...

  2. python多进程共享内存_python 进程间通信 共享内存

    python多进程通信实例分析 python多进程通信实例分析操作系统会为每一个创建的进程分配一个独立的地址空间,不同进程的地址空间是完全隔离的,因此如果不加其他的措施,他们完全感觉不到彼此的存在.那 ...

  3. 南京邮电大学操作系统实验五:Windows平台多进程共享内存通信

    实验内容 1.理解Windows同步互斥机制中的等待函数.事件内核对象.信标内核对象.互斥对象内核对象.动态链接库.DLL整体运行情况.创建DLL模块和相关函数部分. DLL程序入口点函数为DllMa ...

  4. python多进程共享内存

    1.问题: 群中有同学贴了如下一段代码,问为何 list 最后打印的是空值? from multiprocessing import Process, Manager import os manage ...

  5. 多进程编程(四):共享内存

    定义: 共享内存(Shared Memory)就是允许两个或多个进程访问同一个内存空间,是在多进程通信的最高效的方式. 操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们 ...

  6. python多进程之间共享内存

    一.为什么要用到共享内存 进程之间交换数据我们可以通过建立本地RPC,但往往比较慢,因为要花时间去执行数据传递. 此时,如果有一个实时性要求比较高的跨进程功能,共享内存就是一个不错的选择. 1.什么是 ...

  7. Linux的IPC机制(一):共享内存

    0. 共享内存 比喻 本质 多个进程访问同一个逻辑内存 直接访问内存,不用read()/write()非常方便 1. POSIX 共享内存 资料:unpv22e-ch13 查看: man shm_ov ...

  8. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  9. java共享内存_Java共享内存

    1   共享内存对应应用开发的意义 对熟知UNIX系统应用开发的程序员来说,IPC(InterProcess Communication)机制是非常熟悉的,IPC基本包括共享内存.信号灯操作.消息队列 ...

最新文章

  1. 新到DELL M1000e 刀片服务器
  2. 关于简单动态规划(Dynamic Programming)的总结
  3. iOS 通过Jenkins 自动构建ipa
  4. 应用DOM操作文档的一个实用例子
  5. numpy求逆矩阵_线性代数精华2——逆矩阵的推导过程
  6. 【英语学习】【WOTD】ecstatic 释义/词源/示例
  7. iptable防火墙流程图
  8. cisco初级随堂笔记1
  9. 快捷切换hosts的小工具:SwitchHosts!
  10. three.js示例之旋转立方体
  11. 标准c语言教程gary,C语言标准教程
  12. bzoj 3039 玉蟾宫 单调栈
  13. 行业了解——挂耳咖啡
  14. android修改短信内容,Android手机
  15. CSR867x一拖多加密工具8670 8675
  16. 我的世界服务器修改个人游戏模式吗,我的世界1period;12指令更改模式 | 手游网游页游攻略大全...
  17. og协议-有利于SNS网站分享
  18. #Paper Reading# Implicit Neural Representations with Periodic Activation Functions
  19. mdict.cn的安卓安装包不能找到mdx文件问题解决方法
  20. Java后端实现安卓/IOS移动端消息推送(百度云推送)

热门文章

  1. VMware上Ubuntu实现和windows复制粘贴
  2. 动态规划的应用(二):cutting stock 问题
  3. 华为鸿蒙目标一年跨过生死线
  4. bat 批处理 小工具
  5. Kafka High Level API vs. Low Level API
  6. 软件测试可以分为哪几个类型?
  7. YOLO v4 糅合方法记录
  8. 何帅:“在线”思考,王坚的云计算心理学
  9. 「基因组学」使用CAFE进行基因家族扩张收缩分析
  10. 四大降维算法的比较和一些理解(PCA、LDA、LLE、LEP)