DPDK内存篇(三):17.11及早期版本

导读

这是关于数据面开发套件(DPDK)内存管理功能系列文章的第三篇。本系列的第一篇文章描述了DPDK基础的基本概念。第二篇文章深入介绍了输入输出虚拟地址(IOVA)和适用于所有DPDK版本的内核驱动程序。本文概述了DPDK版本中可用的内存管理工具,包括17.11版本。

DPDK17.11是(目前)仍然支持的最早的长期支持(LTS)版本。因此,描述DPDK 17.11提供的内存管理功能不仅为DPDK内存管理功能的发展提供了宝贵的历史视角,而且对许多使用这些较旧但仍受支持的版本的DPDK消费者有所帮助。

从内存管理的角度来看,18.11之前的LTS版本表现完全相同,因此,除非另有说明,否则关于17.11版本的所有内容也适用于任何早期DPDK版本。本文中涉及的一些概念已在本系列的第1篇和第2篇文章中介绍过,因此也请阅读这些文章。

作者简介

Anatoly Burakov:英特尔软件工程师,目前在维护DPDK中的VFIO和内存子系统。

为DPDK提供标准大页内存

为了使应用程序访问大页内存,Linux*使用了一个特殊的hugetlbfs文件系统。因此,为了利用标准大页,DPDK 17.11必须使用hugetlbfs文件系统挂载点。因为大页可以有不同的大小,hugetlbfs 挂载点可以提供对不同大小页面的访问,具体取决于它的配置方式。如果未明确指定特定hugetlbfs挂载点的大页大小,则它使用默认的大页大小,该大小通过内核命令行设置(或者是如果在启动时未指定默认的大页大小,则设置为某个默认值)。

在许多当前发行版中,默认情况下通过systemd可以使用hugetlbfs挂载点,在少数情况下(例如开发或调试),只有一个默认挂载点就足够了。用户也可以选择通过编辑/etc/fstab创建自己的hugetlbfs挂载点。如何设置hugetlbfs挂载点,设置默认大页大小以及保留标准大页超出了本系列文章的范围,但是有关如何执行这些操作的相关指南可以在DPDK文档以及Linux发行版文档中找到。

使用DPDK管理大页内存

一旦系统被设置为为应用程序使用保留大页,DPDK就可以使用它。默认情况下,如果没有指定与内存相关的环境抽象层(EAL)命令行参数,DPDK应用程序将占用任何它可以从系统中获得的标准大页。这是一个很好的默认设置,因为它可以令DPDK很容易地开始使用,

而且使用所有可用的大页内存可能对开发环境和提升场景有利。但这可能不适用于多个应用程序可能需要大页的生产环境。要解决此问题,可以使用-m EAL命令行开关指定DPDK应用程序被允许使用多少大页内存:

./app -m 64

此EAL参数将DPDK的内存使用限制为64MB(向上舍入到最小可用页面大小)。对于具有非统一内存访问(NUMA)支持的系统,提供此参数会尝试将所请求的内存量分散在所有可用的NUMA节点上。例如,给定双插槽系统,上述命令将在NUMA节点0和1上各保留32 MB。

但是,内存并不总是平均分配到所有NUMA节点上;实际上,分配给每个NUMA节点的内存量是按照coremask中特定NUMA节点上的CPU核心数量成比例计算的。默认coremask包括所有可用内核,每个NUMA节点上的内核数通常是相等的,因此在默认情况下内存将平均分配。如果coremask在NUMA节点0上有六个核心而在NUMA节点1上有两个核心,那么内存的分配方式会有所不同:NUMA节点0上有48MB和NUMA节点1上有16MB。类似地,如果coremask中的NUMA节点0上没有核,则上述命令将所有64 MB 分配在NUMA节点1上。

如果每个NUMA节点分配需要更精细的控制,怎么办?DPDK有另一个EAL命令行标志专用于这个用例— socket-mem标志:

./app —socket-mem 0,64

使用此标志,可以指定任何每NUMA节点内存要求。如果未指定特定NUMA节点的内存量,则假定该值为0; 例如,在双插槽系统上,提供—socket-mem64将在NUMA节点0上保留64 MB,而在NUMA节点1上不保留任何内容。

要预留多少内存

使用DPDK库17.11版本或更早版本的任何应用程序必须事先知道其内存要求。这是因为,对于这些版本的DPDK,在初始化之后不可能再申请额外的大页内存,或者将其释放回系统。因此,DPDK应用程序可能使用的任何内存都必须在应用程序初始化时预留,并在应用程序的整个生命周期都由DPDK保留。

在决定要保留的内存量时,留出一些余量通常是个好主意。在各种内部分配中,某些DPDK内存将在不同的内部分配中被“浪费”,数量会因您的配置而异(DPDK将使用的设备数量,启用功能等)。

此外,DPDK17.11中的大多数API都需要大量的IOVA连续内存。这是因为在DPDK 17.11中,虚拟内存布局总是与物理内存布局相匹配。换句话说,如果内存区域是VA连续的,它也会是IOVA连续的。这是DPDK17.11内存管理中众所周知的问题之一:实际上很少有应用程序需要IOVA连续内存,但是没有它也无法使用VA连续内存,由于缺少足够的IOVA连续内存,分配大量内存可能会失败。

图1. IOVA模式的比较

上述限制当然仅适用于作为物理地址(PA)模式的IOVA,因为在该模式下,DPDK的虚拟地址(VA)空间遵循PA空间的布局。在PA模式的IOVA中,可用IOVA连续内存量取决于DPDK控制之外的许多因素,尽管DPDK将尝试保留尽可能多的IOVA连续内存,具体取决于可用内存量和系统配置,可能没有足够的IOVA连续内存来满足所有分配。

在VA模式的IOVA中,这不是问题,因为在这种情况下,IOVA空间布局将与VA空间的布局相匹配(而不是相反),并且所有物理内存都被重新映射为IOVA连续到硬件。

DPDK中的大页内存使用

一旦DPDK启动,有许多方法可以在用户应用程序中使用DPDK提供的内存。在DPDK中,大多数情况下,可用内存将固定到NUMA节点。然后,用户可以选择在特定NUMA节点上分配内存(通过在分配时指定NUMA节点ID作为API参数),或者让DPDK自己决定使用特殊值作为NUMA节点ID来分配内存。

为了分配DPDK提供的数据结构,例如哈希表,Mempool,环形缓存等,API会自动使用适当的内存分配方法。例如,对一个rte_ring数据结构分配的调用,API会调用适当的内存分配过程,因此在这种情况下不需要显式调用任何内存分配API。

对于一般的内存申请,DPDK提供了自己的一组API,这些API反映了glibc malloc()函数。还有这些API的NUMA感知版本。C程序员应该对这些API非常熟悉。使用DPDK内存管理的所有好处(例如显式NUMA节点局部性,更少的TLB查询失败,对齐等),将适用于使用这些API分配的任何内存。

除此之外,DPDK还提供了rte_memzone系列API,允许分配满足大小,对齐和边界以及NUMA节点和页面大小要求的原始内存区域。内存区域也必须具有独一无二的名称,因为可以通过它们的名称进行查找。这些API主要由数据结构,驱动程序等在内部使用,通常不在应用程序代码中使用。

表1.分配API的决策图

DPDK数据结构,相应的数据结构分配API(如果存在),否则为rte_malloc

在DPDK 17.11及更早版本中,所有DPDK内存分配,无论它们如何执行,都是VA和IOVA连续的。所有内存分配API也是线程安全的,并且因为内存也在所有主进程和辅助进程之间共享,在进程之间传递指针,以及在不同进程中分配或释放内存,是非常安全的。

其他与内存相关的功能

除上述内容外,EAL还提供了一些专门性功能,在一般情况下无用,但在某些情况下可以派上用场。

(1) 在没有标准大页支持的情况下运行DPDK

EAL提供的功能之一是一个非庞大的模式,可通过指定—no-huge EAL命令行标志来实现。这使得DPDK不需要(或使用)大页内存初始化,而是使用常规内存。这通常仅用于调试目的(例如运行某些单元测试),因为除非使用VA模式的IOVA,否则数据包IO将无法工作。—socket-mem开关在此模式下无效,但是-m开关仍指定DPDK在启动时应保留多少内存。

(2)设置自定义大页文件系统挂载点

EAL提供的另一个功能是使用—huge-dir命令行参数设置自定义hugetlbfs挂载点。如果用户具有相同页面大小的hugetlbfs的多个挂载点,则结果可能是不可预测的,因为这不是DPDK操作的预期环境。指定DPDK的一个特定挂载点以解决该问题,尽管一次只有一个这样的挂载点可以使用。

(3)从文件系统中删除大页

最后,最有用的选项是—huge-unlink。默认情况下,只要DPDK初始化,它就会在hugetlbfs挂载点中创建并存储所有大页文件,并且不会在退出时删除它们。因此,当DPDK应用程序退出时,那些大页在内核方面仍然使用(即使DPDK已经关闭),并且为了将它们释放回系统,需要从文件系统中删除这些文件。这种行为是有意的,因为它允许在主DPDK进程退出后,辅助进程也能初始化并附加到内存(并继续正常工作)。

—huge-unlink选项使得在初始化时,hugetlbfs挂载点中的大页文件被创建,映射,然后立即被删。DPDK将继续保留分配的大页,但在hugetlbfs文件系统中不再有文件遗留,因此在DPDK应用程序退出后没有任何东西可以清理。不言而喻,这个选项牺牲了辅助进程支持(因为没有可以在初始化时发现的共享内存),但也有许多DPDK用例乐于使用该选项。

结论

本文概述了DPDK 17.11及更早版本中可用的内存管理功能,并提供了可使特定用例充分利用DPDK的各种选项。

这是DPDK内存管理系列文章的第三篇文章。第一篇文章概述了DPDK内存管理子系统基础中的关键原则。第二篇文章深入介绍了DPDK如何处理物理地址。下一篇也即最后一篇文章将概述DPDK 18.11或更高版本中可用的新内存管理功能。

文章转载自DPDK与SPDK开源社区


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/24517.html
分享到:
相关文章
条评论

登录后才可以评论

SDNLAB君 发表于20-10-23
1