三、程序员指南:数据平面开发套件

news2024/11/18 2:28:31

定时器库

定时器库为DPDK执行单元提供了定时器服务,以便异步执行回调函数。该库的特点包括:

  • 定时器可以是周期性的(多次触发)或单次的(一次性触发)。
  • 定时器可以从一个核加载并在另一个核上执行。这必须在调用rte_timer_reset()时指定。
  • 定时器提供高精度(取决于调用rte_timer_manage()的频率,用于检查本地核心的定时器到期情况)。
  • 如果应用程序不需要定时器,在编译时可以通过不调用rte_timer_manage()来禁用以提高性能。

定时器库使用rte_get_timer_cycles()函数,该函数使用高精度事件计时器(HPET)或CPU时间戳计数器(TSC)提供可靠的时间参考。

该库提供了一个接口来添加、删除和重新启动定时器。其API基于BSD的callout(),但存在一些不同之处。请参阅callout手册。

11.1 实现细节

定时器以每个逻辑核心(lcore)为基础进行跟踪,每个核心的所有待处理定时器按照到期时间的顺序在一个跳表(skiplist)数据结构中维护。所使用的跳表具有十个级别,表中的每个条目在每个级别上以概率 ¼^level 出现。这意味着所有条目都存在于级别 0,每 4 个条目中有 1 个存在于级别 1,每 16 个中有 1 个存在于级别 2,依此类推直到级别 9。这意味着对于每个核心,将定时器列表中的条目添加和删除可以在对数时间内完成,最多 4^10 个条目,即每个逻辑核心最多约 1,000,000 个定时器。

定时器结构包含一个名为status的特殊字段,它是定时器状态(stopped、pending、running、config)和所有者(lcore id)的联合。根据定时器状态,我们知道定时器是否存在于列表中:

  • STOPPED:无所有者,不在列表中
  • CONFIG:由一个核心拥有,不得由另一个核心修改,可能在列表中或不在列表中,取决于先前的状态
  • PENDING:由一个核心拥有,存在于列表中
  • RUNNING:由一个核心拥有,不得由另一个核心修改,存在于列表中

不允许在定时器处于CONFIG或RUNNING状态时重置或停止定时器。当修改定时器的状态时,应使用比较并交换(Compare And Swap,CAS)指令来保证状态(状态+所有者)的原子性修改。

在rte_timer_manage()函数中,跳表被用作一个常规列表,通过沿着包含所有定时器条目的级别 0 列表进行迭代,直到遇到尚未过期的条目为止。为了提高性能,在定时器列表中有条目但这些定时器尚未过期的情况下,第一个列表条目的过期时间保持在每个核心的定时器列表结构中。在 64 位平台上,可以检查该值而无需锁定整体结构。(由于过期时间以 64 位值的形式维护,因此在 32 位平台上无法对值进行检查,除非使用比较并交换(CAS)指令或使用锁定,因此在获取锁定后会按常规方式跳过此附加检查。)在 64 位和 32 位平台上,如果调用核心的定时器列表为空,则rte_timer_manage()调用将返回而不会锁定。

11.2 使用案例

定时器库用于周期性调用,例如垃圾收集器或某些状态机(ARP、桥接等)。

11.3 参考资料

  • callout手册:提供定时器机制,在给定时间执行函数的callout设施。
  • HPET:关于高精度事件定时器(HPET)的信息。

HASH库

DPDK提供了一个散列库,用于快速查找的哈希表创建。哈希表是一种针对通过唯一键标识的一组条目进行搜索进行优化的数据结构。为了提高性能,DPDK哈希要求所有键具有相同数量的字节,这在哈希创建时设置。

12.1 哈希API概述

哈希的主要配置参数包括:

  • 哈希表的总条目数
  • 键的字节大小

哈希还允许配置一些与低级实现相关的参数,例如:

  • 将键转换为存储桶索引的哈希函数
  • 每个存储桶的条目数

哈希导出的主要方法包括:

  • 使用键添加条目:将键作为输入提供。如果成功将新条目添加到指定键的哈希中,或者哈希中已经存在指定键的条目,则返回条目的位置。如果操作不成功,例如由于哈希中没有空闲条目,则返回负值;
  • 使用键删除条目:将键作为输入提供。如果在哈希中找到具有指定键的条目,则将该条目从哈希中删除,并返回在哈希中找到条目的位置。如果在哈希中不存在具有指定键的条目,则返回负值;
  • 使用键查找条目:将键作为输入提供。如果在哈希中找到具有指定键的条目(查找命中),则返回条目的位置,否则(查找未命中)返回负值。

当前的哈希实现仅处理键管理。与每个键关联的实际数据必须由用户使用一个单独的表进行管理,该表镜像了哈希的条目数和每个条目的位置,如以下各节中描述的流分类用例。

在L2/L3转发示例应用程序中的示例哈希表中,根据五元组查找确定将数据包转发到哪个端口。但是,此表也可以用于更复杂的功能,并提供可以对数据包和流执行的许多其他功能和操作。

12.2 实现细节

哈希表实现为一个条目数组,进一步分为存储桶,每个存储桶中有相同数量的连续数组条目。对于任何输入键,始终存在一个单个存储桶,该键可以存储在哈希中,因此仅需要检查该存储桶内的条目当查找键时。通过将要扫描的条目数量从哈希总条目数减少到哈希存储桶中的条目数来实现查找速度,而不是基本方法中线性扫描数组中的所有条目。哈希使用哈希函数(可配置)将输入键转换为4字节键签名。存储桶索引是键签名对哈希桶数量取模。一旦确定存储桶,哈希添加、删除和查找操作的范围将缩小到该存储桶中的条目。

为加快存储桶内的搜索逻辑,每个哈希条目存储了4字节键签名以及每个哈希条目的完整键。对于大键大小,将输入键与存储桶中的键进行比较所需的时间可能显着多于将输入键的4字节签名与存储桶中键的签名进行比较。因此,首先进行签名比较,只有在签名匹配时才进行完整键比较。仍然需要进行完整键比较,因为同一个存储桶中的两个输入键仍然可能具有相同的4字节哈希签名,尽管对于提供良好均匀分布的输入键集的哈希函数来说,这种情况相对较少。

12.3 使用案例:流分类

流分类用于将每个输入数据包映射到其所属的连接/流。这个操作是必要的,因为通常每个输入数据包的处理都是在其连接的上下文中进行的,因此同一流中的所有数据包都会应用相同的操作集合。

使用流分类的应用程序通常有一个流表来管理,其中每个单独的流在这个表中都有一个关联的条目。流表条目的大小是应用程序特定的,典型值为 4、16、32 或 64 字节。

每个使用流分类的应用程序通常都有一个机制来根据从输入数据包中读取的多个字段组成的流键来唯一标识一个流。一个例子是使用由 IP 和传输层数据包头的以下字段组成的 DiffServ 5-tuple:源 IP 地址、目的 IP 地址、协议、源端口、目的端口。

DPDK哈希提供了一种通用方法来实现应用程序特定的流分类机制。给定一个作为数组实现的流表,应用程序应该创建一个具有与流表相同条目数的哈希对象,并将哈希键的大小设置为所选流键中的字节数。

应用程序侧的流表操作如下所述:

  • 添加流:将流键添加到哈希中。如果返回的位置有效,则使用它来访问流表中的流条目,以添加新流或更新与现有流关联的信息。否则,流添加失败,例如由于没有空闲条目来存储新流。
  • 删除流:从哈希中删除流键。如果返回的位置有效,则使用它来访问流表中的流条目,以使与流相关联的信息无效。
  • 查找流:在哈希中查找流键。如果返回的位置有效(流查找命中),则使用返回的位置来访问流表中的流条目。否则(流查找未命中),当前数据包没有注册流。

12.4 参考资料

  • Donald E. Knuth,《计算机程序设计艺术》,第 3 卷:排序与搜索(第 2 版),1998 年,Addison-Wesley Professional

LPM库

DPDK的LPM库组件实现了用于32位键的最长前缀匹配(Longest Prefix Match,LPM)表搜索方法,通常用于在IP转发应用程序中查找最佳路由匹配。

13.1 LPM API概述

LPM组件实例的主要配置参数是支持的最大规则数。LPM前缀由一对参数(32位键,深度)表示,深度范围为1到32。LPM规则由LPM前缀和与前缀相关联的一些用户数据表示。前缀充当LPM规则的唯一标识符。在此实现中,用户数据长度为1字节,称为下一跳(next hop),与其主要用途相关联,在路由表条目中存储下一跳的ID。

LPM组件导出的主要方法包括:

  • 添加LPM规则:提供LPM规则作为输入。如果表中不存在具有相同前缀的规则,则将新规则添加到LPM表中。如果表中已经存在具有相同前缀的规则,则更新规则的下一跳。当没有可用的规则空间时返回错误。
  • 删除LPM规则:提供LPM规则的前缀作为输入。如果LPM表中存在具有指定前缀的规则,则将其移除。
  • 查找LPM键:提供32位键作为输入。该算法选择表示给定键的最佳匹配规则,并返回该规则的下一跳。如果LPM表中存在具有相同32位键的多个规则,则算法选择深度最深的规则作为最佳匹配规则,这意味着输入键和规则键之间具有最高数量的最重要位匹配。

13.2 实现细节

当前的实现使用的是DIR-24-8算法的变体,以提高LPM查找速度,以交换内存使用量。该算法允许查找操作通常以单个内存读取访问进行。在极少数情况下,当最佳匹配规则的深度大于24时,查找操作需要两个内存读取访问。因此,LPM查找操作的性能在很大程度上受特定内存位置是否存在于处理器缓存中的影响。

主要数据结构使用以下元素构建:

  • 一个包含2^24个条目的表。
  • 多个包含2^8个条目的表(RTE_LPM_TBL8_NUM_GROUPS)。

第一个表称为tbl24,使用IP地址的前24位进行索引,而第二个表(或多个表)称为tbl8,使用IP地址的最后8位进行索引。这意味着根据尝试将传入数据包的IP地址与存储在tbl24中的规则进行匹配的结果,我们可能需要在第二级中继续查找过程。由于tbl24的每个条目理论上都可能指向一个tbl8,理想情况下,我们将有224个tbl8,这与具有232个条目的单个表相同。但由于资源限制,这是不可行的。因此,该方法利用了超过24位的规则非常罕见的事实。通过将进程分成两个不同的表/级别,并限制tbl8的数量,我们可以大大减少内存消耗,同时保持非常好的查找速度(大多数情况下只需一次内存访问)。
在这里插入图片描述

用户

在 tbl24 中的条目包含以下字段:

  • 下一跳 / tbl8 的索引
  • 有效标志
  • 外部条目标志
  • 规则的深度(长度)

第一个字段可以包含一个数字,指示查找过程应该继续到哪个 tbl8,或者如果已经找到最长前缀匹配,则包含下一跳本身。这两个标志用于确定条目是否有效以及搜索过程是否已经完成。规则的深度或长度是存储在特定条目中的规则位数。

在 tbl8 中的条目包含以下字段:

  • 下一跳
  • 有效
  • 有效组
  • 深度

下一跳和深度包含与 tbl24 中相同的信息。这两个标志分别表示条目和表是否有效。

另一个主要数据结构是包含有关规则(IP 和下一跳)的主要信息的表。这是一个更高级别的表,用于不同的功能:

  • 在添加或删除之前,检查规则是否已经存在,而无需执行实际查找。
  • 在删除时,检查是否存在包含要删除的规则。这很重要,因为主数据结构将必须相应地进行更新。

13.2.1 添加

添加规则时,有不同的可能性。如果规则的深度正好是 24 位,则:

  • 使用规则(IP 地址)作为 tbl24 的索引。
  • 如果条目无效(即不包含规则),则将其下一跳设置为其值,将有效标志设置为 1(表示此条目正在使用),并将外部条目标志设置为 0(表示查找过程在此处结束,因为这是最长匹配的前缀)。

如果规则的深度正好是 32 位,则:

  • 使用规则的前 24 位作为 tbl24 的索引。
  • 如果条目无效(即不包含规则),则寻找一个空闲的 tbl8,将索引设置为此值,将有效标志设置为 1(表示此条目正在使用),并将外部条目标志设置为 1(表示查找过程必须继续,因为规则尚未完全探索)。

如果规则的深度是其他值,则必须执行前缀扩展。这意味着规则被复制到所有条目(只要它们未被使用),这也会导致匹配。
举个简单的例子,假设深度是 20 位。这意味着IP地址的前24位有 2^(24-20) = 16 种不同的组合可以导致匹配。因此,在这种情况下,我们将完全相同的条目复制到由这些组合之一索引的每个位置。
通过这样做,我们确保在查找过程中,如果存在与IP地址匹配的规则,则可以在一次或两次内存访问中找到它,具体取决于是否需要转到下一个表。前缀扩展是此算法的关键之一,通过增加冗余来显着提高速度。

13.2.2 查找

查找过程要简单得多且更快速。在这种情况下:

  • 使用 IP 地址的前 24 位作为 tbl24 的索引。如果条目未被使用,则意味着我们没有与此IP匹配的规则。如果有效且外部条目标志设置为 0,则返回下一跳。
  • 如果有效且外部条目标志设置为 1,则使用 tbl8 索引找出要检查的 tbl8,并使用 IP 地址的最后 8 位作为此表的索引。
    同样,如果条目未被使用,则表示没有与此 IP 地址匹配的规则。如果有效,则返回下一跳。

13.2.3 规则数量的限制

限制可以添加的规则数量的因素有不同。首先是最大规则数,这是通过 API 传递的参数。一旦达到这个数,除非移除一个或多个规则,否则无法再向路由表中添加规则。
第二个原因是算法固有的限制。如前所述,为了避免高内存消耗,在编译时限制了 tbl8 的数量(默认为 256)。如果耗尽了 tbl8,就无法再添加任何规则。对于特定的路由表需要多少个 tbl8 难以提前确定。
每当出现一个新规则的深度大于 24 位,并且该规则的前 24 位与先前添加的规则的前 24 位不同时,就会消耗一个 tbl8。如果它们相同,则新规则将与之前的规则共享相同的 tbl8,因为两个规则之间的唯一区别在于最后一个字节。
使用默认值 256,我们最多可以有 256 条超过 24 位并且在前三个字节上不同的规则。由于超过 24 位的路由不太可能,这在大多数设置中不应该是问题。即使存在问题,tbl8 的数量也是可以修改的。

13.2.4 应用案例:IPv4 转发

LPM 算法用于实现路由器执行 IPv4 转发所使用的无类域间路由选择(CIDR)策略。

13.2.5 参考资料

  • RFC1519 无类域间路由选择(CIDR):地址分配和聚合策略,http://www.ietf.org/rfc/rfc1519
  • Pankaj Gupta,《路由查找和数据包分类的算法》,斯坦福大学博士论文,2000 年 (http://klamath.stanford.edu/~pankaj/thesis/thesis_1sided.pdf)

LPM6 LIBRARY

LPM6(IPv6的最长前缀匹配)库组件实现了针对128位键的最长前缀匹配(LPM)表搜索方法,通常用于IPv6转发应用程序中查找最佳匹配路由。

14.1 LPM6 API概述

LPM6库的主要配置参数包括:

  • 最大规则数: 定义了保存规则的表的大小,因此也定义了可以添加的规则的最大数量。
  • tbl8的数量: tbl8是LPM6算法基于的trie树的节点。该参数与您可以拥有的规则数量有关,但由于规则的深度和IP地址强烈依赖于,因此无法准确预测需要的数量。一个tbl8消耗1KB的内存。建议使用65536个tbl8可以存储数千个IPv6规则,但具体数量取决于具体情况。

LPM前缀由一对参数(128位键、深度)表示,深度范围为1到128。LPM规则由LPM前缀和与前缀相关联的一些用户数据表示。前缀用作LPM规则的唯一标识符。在此实现中,用户数据长度为1字节,称为“下一跳”,对应于在路由表条目中存储下一跳的ID的主要用途。

LPM组件导出的主要方法包括:

  • 添加LPM规则: 提供LPM规则作为输入。如果表中没有具有相同前缀的规则,则将新规则添加到LPM表中。如果表中已存在具有相同前缀的规则,则更新规则的下一跳。当没有可用空间时会返回错误。
  • 删除LPM规则: 提供LPM规则的前缀作为输入。如果LPM表中存在指定前缀的规则,则将其删除。
  • 查找LPM键: 提供128位键作为输入。算法选择代表给定键的最佳匹配规则,并返回该规则的下一跳。如果LPM表中存在具有相同128位值的多个规则,则算法选择具有最高深度的规则作为最佳匹配规则,这意味着输入键和规则键之间匹配的最高有效位数。

14.1.1 实现细节

这是对用于IPv4的算法的修改(参见IPv4 LPM实现细节)。在此情况下,不再使用一个tbl24和第二个tbl8的两级,而是使用了14级。

该实现可以看作是多位trie树,每个级别的检查位或位数从级别到级别不同。具体来说,在根节点上检查了24位,在接下来的104位中以8位为一组进行检查。这意味着trie树最多有14级,具体取决于添加到表中的规则。

该算法允许进行查找操作的内存访问数量直接取决于规则的长度以及数据结构中具有相同键的更大深度的其他规则。这可以从1到14个内存访问不等,对于IPv6中最常用的长度,平均值为5。

主要数据结构使用以下元素构建:

  • 具有224个条目的表
  • 一组表,可以通过API由用户配置,具有28个条目

第一个表称为tbl24,使用要查找的IP地址的前24位作为索引,而其余的表称为tbl8s,使用IP地址的其余字节,以8位为一组进行索引。这意味着,根据尝试将传入数据包的IP地址与存储在tbl24或随后的tbl8s中的规则进行匹配的结果,可能需要在树的更深层次中继续查找过程。

类似于IPv4算法中提出的限制,要存储每个可能的IPv6规则,我们将需要一个具有2^128个条目的表。由于资源限制,这是不可行的。通过将过程拆分为不同的表/级别并限制tbl8s的数量,我们可以大大减少内存消耗,同时保持非常好的查找速度(每个级别一次内存访问)。

表中的条目包含以下字段:

  • 下一跳/ tbl8中的索引
  • 规则的深度(长度)
  • 有效标志
  • 有效组标志
  • 外部条目标志

第一个字段可以包含一个数字,指示应继续查找过程的tbl8,或者如果已经找到最长前缀匹配,则是下一跳本身。规则的深度或长度是存储在特定条目中的规则的位数。标志用于确定条目/表是否有效,以及搜索过程是否已经完成。

两种类型的表共享相同的结构。

另一个主要的数据结构是包含有关规则(IP、下一跳和深度)的主要信息的表。这是一个更高级别的表,用于不同的用途:

  • 在添加或删除之前检查规则是否已存在,而无需执行实际查找。
    在这里插入图片描述
    在删除时,要检查是否存在包含将要删除的规则的规则。这一点非常重要,因为主要的数据结构必须相应地进行更新。

14.1.2 添加

在添加规则时,有不同的可能性。如果规则的深度恰好为24位,则:
• 使用规则(IP地址)作为tbl24的索引。
• 如果条目无效(即尚未包含规则),则将其下一跳设置为其值,将有效标志设置为1(表示此条目正在使用),将外部条目标志设置为0(表示查找过程在此结束,因为这是与之匹配的最长前缀)。
如果规则的深度大于24位但是8的倍数,则:
• 使用规则的前24位作为tbl24的索引。
• 如果条目无效(即尚未包含规则),则查找空闲的tbl8,将tbl8的索引设置为此值,将有效标志设置为1(表示此条目正在使用),将外部条目标志设置为1(表示查找过程必须继续,因为规则尚未完全探索)。
• 使用规则的接下来的8位作为下一个tbl8的索引。
• 重复此过程,直到到达正确级别的tbl8(取决于深度),并用下一跳填充它,将下一个条目标志设置为0。
如果规则的深度是其他值,则必须执行前缀扩展。这意味着将规则复制到所有条目(只要它们未被使用),这也将导致匹配。
举个简单的例子,假设深度为20位。这意味着IP地址的前24位有2^(24-20) = 16种不同的组合会导致匹配。因此,在这种情况下,我们将完全相同的条目复制到由其中一个这些组合索引的每个位置。
通过这样做,我们确保在查找过程中,如果存在与IP地址匹配的规则,最多只需进行14次内存访问,取决于我们需要移动到下一个表的次数。前缀扩展是该算法的关键之一,因为它通过添加冗余性显著提高了速度。
前缀扩展可以在任何级别执行。例如,如果深度为34位,则将在第三级(基于第二个tbl8的级别)执行。

14.1.3 查找

查找过程要简单得多且更快。在这种情况下:
• 使用IP地址的前24位作为tbl24的索引。如果条目未被使用,则意味着我们没有与此IP匹配的规则。如果它有效且外部条目标志设置为0,则返回下一跳。
• 如果它有效且外部条目标志设置为1,则使用tbl8索引来查找要检查的tbl8,并使用IP地址的下一个8位作为此表的索引。同样,如果条目未被使用,则意味着我们没有与此IP地址匹配的规则。如果它有效,则检查外部条目标志以获得要检查的新tbl8。
• 重复此过程,直到我们找到无效条目(查找未命中)或外部条目标志设置为0的有效条目。在后一种情况下返回下一跳。

14.1.4 规则数量的限制

有许多因素限制了可以添加的规则数量。首先是最大规则数,这是通过 API 传递的一个参数。一旦达到此数量,除非删除一个或多个规则,否则将无法向路由表添加更多规则。
第二个限制是可用的 tbl8 数量。如果我们耗尽了 tbl8,就无法添加更多规则。如何确定特定路由表所需的 tbl8 数量是很难事先确定的。
在此算法中,单个规则可以消耗的最大 tbl8 数量为13,这是级别数减一,因为前三个字节在 tbl24 中已解析。但是:
• 通常,在IPv6中,路由不会超过48位,这意味着规则通常占用多达3个 tbl8。
正如IPv4算法的最长前缀匹配中所解释的那样,很可能有多个规则会共享一个或多个 tbl8,这取决于它们的前几个字节。例如,如果它们共享相同的前24位,第二级别的 tbl8 将被共享。在更深的级别中可能再次发生这种情况,因此,如果唯一区别是最后一个字节,则实际上两个48位长的规则可能使用相同的三个 tbl8。
在此版本的算法中,tbl8 的数量是向用户公开的一个参数,因为它影响内存消耗和可以添加到最长前缀匹配表中的规则数量。一个 tbl8 消耗1千字节的内存。

14.2 用例:IPv6 转发

最长前缀匹配算法用于实现路由器执行 IP 转发的无类域间路由选择(CIDR)策略。

PACKET DISTRIBUTOR LIBRARY

DPDK Packet Distributor library是设计用于动态负载均衡流量的库,同时支持单个数据包的操作。在使用此库时,使用的逻辑核心可分为两种角色:首先是分发器(distributor)核心,负责负载均衡或分发数据包;其次是一组工作核心(worker cores),负责接收分发器传来的数据包并对其进行操作。操作模型如下图所示。
在这里插入图片描述

15.1 分发器核心操作

分发器核心负责确保数据包在工作核心间公平共享的大部分处理工作。其操作如下:

  1. 数据包通过分发器核心线程调用“rte_distributor_process()”API传递到分发器组件。
  2. 工作核心与分发器核心共享单个缓存行,以便向工作核心传递消息和数据包。该过程API调用将轮询所有工作核心缓存行,以查看哪些工作核心正在请求数据包。
  3. 当工作核心请求数据包时,分发器从传入的数据包集中获取数据包并分发给工作核心。同时,它检查每个数据包中存储在mbuf的RSS哈希字段中的“标签”,并记录每个工作核心正在处理的标签。
  4. 如果输入集合中的下一个数据包具有工作核心已在处理的标签,则该数据包将排队等待由该工作核心处理,并在该工作核心下次请求工作时优先提供给它。这确保了不会并行处理具有相同标签的两个数据包,并且所有具有相同标签的数据包都按输入顺序处理。
  5. 一旦传递给处理API的所有输入数据包要么分发给工作核心,要么排队等待正在处理给定标签的工作核心,处理API将返回给调用者。

分发器核心还提供其他可用函数:

  • rte_distributor_returned_pkts()
  • rte_distributor_flush()
  • rte_distributor_clear_returns()

其中,最重要的API调用是“rte_distributor_returned_pkts()”,应仅在调用处理API的核心上调用。它将所有已由所有工作核心完成处理的数据包返回给调用者。在返回的数据包集合中,所有共享相同标签的数据包将按其原始顺序返回。

注意:

  • 如果工作核心在之后内部缓冲数据包以进行批量传输,则具有相同标签的数据包可能会变得无序。一旦工作核心请求新数据包,分发器就会假定其已完全处理完先前的数据包,因此可以安全地将具有相同标签的附加数据包分发给其他工作核心,这可能会更早地刷新其缓冲数据包并导致数据包无序。
  • 对于不共享公共数据包标签的数据包,不会做出数据包排序保证。

通过使用处理和返回数据包API,可以使用以下应用程序工作流程,同时保持标签识别的数据包流中的数据包顺序。

flush和clear_returns API调用,前述提到,可能比process和returned_pkts API的用途少,主要用于辅助库的单元测试。有关这些函数及其使用的详细描述,请参阅DPDK API参考文档。
在这里插入图片描述

15.2 Worker Operation

工作核心是对数据包执行实际操作的核心,这些数据包由数据包分发器分发。每个工作核心调用“rte_distributor_get_pkt()”API来请求新的数据包,当它完成处理前一个数据包时。[前一个数据包应通过将其作为此API调用的最后一个参数传递给数据包分发器组件来返回。]

由于根据流量负载的不同可能希望改变工作核心的数量,即在轻负载时节省功率,因此可以通过调用“rte_distributor_return_pkt()”来指示工作核心停止处理数据包,表示它已完成当前数据包,并且不想要新的数据包。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1226519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IntelliJ IDEA 2023 v2023.2.5

IntelliJ IDEA 2023是一款功能强大的集成开发环境(IDE),为开发人员提供了许多特色功能,以下是其特色介绍: 新增语言支持:IntelliJ IDEA 2023新增对多种编程语言的支持,包括Kotlin、TypeScript、…

介绍交换空间概念以及如何设置交换空间

文章目录 什么交换空间新增交换空间 什么交换空间 交换空间(Swap space)是计算机内存的一种补充,位于硬盘驱动器上。当物理内存不足时,系统会将不活跃的页面移到交换空间中。 交换空间可以帮助系统在以下情况下运行&#xff1a…

mysql 实现去重

个人网站 首发于公众号小肖学数据分析 1、试题描述 数据表user_test如下,请你查询所有投递用户user_id并且进行去重展示,查询结果和返回顺序如下 查询结果和返回顺序如下所示 解题思路: (1) 对user_id列直接去重: &#xff…

Kotlin学习(一)

Kotlin学习&#xff08;一&#xff09; 1.使用IDEA构建Kotlin项目 新建工程即可 我这里选择的Build System是IntelliJ&#xff0c;虽然我没用过但是这是Kotlin基础学习应该不会用到其他依赖 2.Hello World package com.simonfun main(args:Array<String>){println(&q…

Go 语言中切片的使用和理解

切片与数组类似&#xff0c;但更强大和灵活。与数组一样&#xff0c;切片也用于在单个变量中存储相同类型的多个值。然而&#xff0c;与数组不同的是&#xff0c;切片的长度可以根据需要增长和缩小。在 Go 中&#xff0c;有几种创建切片的方法&#xff1a; 使用[]datatype{val…

使用 C 语言快速排序将字符串按照 ASCII 码升序排列

示例代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h>static Comp(const void *a, const void *b) {char *pa (char *)a;char *pb (char *)b;return strcmp(a, b); }int main(void) {char strs[3][10] { "bd", &q…

C++ Qt 学习(十):Qt 其他技巧

1. 带参数启动外部进程 QProcess 用于启动外部进程int QProcess::execute(const QString &program, const QStringList &arguments);QObject *parent; ... QString program "./path/to/Qt/examples/widgets/analogclock"; QStringList arguments; argument…

卷积、卷积图像操作和卷积神经网络

好多内容直接看书确实很难坚持&#xff0c;就比如这个卷积&#xff0c;书上的一大堆公式和图表直接把人劝退&#xff0c;我觉得一般的学习流程应该是自顶向下&#xff0c;先整体后局部&#xff0c;先把握大概再推敲细节的&#xff0c;上来就事无巨细地展示对初学者来说很痛苦。…

泉盛UV-K5/K6全功能中文固件

https://github.com/wu58430/uv-k5-firmware-chinese/releases 主要功能&#xff1a; 中文菜单 许多来自 OneOfEleven 的模块&#xff1a; AM 修复&#xff0c;显著提高接收质量长按按钮执行 F 操作的功能复制快速扫描菜单中的频道名称编辑频道名称 频率显示选项扫描列表分配…

文本转语音

免费工具 音视频转译 通义听悟 | https://tingwu.aliyun.com/u/wg57n33kml5nkr3p 音色迁移 speechify | https://speechify.com/voice-cloning/ 视频生成 lalamu | http://lalamu.studio/demo/ 画质增强 topazlabs video AI | https://www.topazlabs.com 付费工具 rask | htt…

重生奇迹mu转职任务详解

重生奇迹mu神骑士怎么转 神骑士是一种转职类型&#xff0c;需要你的角色达到一定等级以及完成相应任务方可转职。以下是神骑士转职的具体步骤&#xff1a; 1.等级要求&#xff1a;首先&#xff0c;你的角色需要达到150级才能进行神骑士转职任务。 2.神骑士转职任务&#xff…

hyperledger fabric2.4测试网络添加组织数量

!!!修改内容比较繁琐,预期未来提供模板修改 修改初始配置文件,初始添加3个组织 organizations文件夹 /cryptogen文件夹下创建文件crypto-config-org3.yaml,内容如下: PeerOrgs:# ---------------------------------------------------------------------------# Org3# ----…

获取每个部门中当前员工薪水最高的相关信息

个人网站 首发于公众号小肖学数据分析 描述 有一个员工表dept_emp简况如下: 有一个薪水表salaries简况如下: 获取每个部门中当前员工薪水最高的相关信息&#xff0c;给出dept_no, emp_no以及其对应的salary&#xff0c;按照部门编号dept_no升序排列&#xff0c;以上例子输出…

ESP32 MicroPython 蜂鸣器及传感器的使用⑦

ESP32 MicroPython 蜂鸣器及传感器的使用⑦ 1、蜂鸣器奏乐2、实验目的3、实验内容5、实验结果6、小车传感器应用7、实验目的8、实验内容9、参考代码10、实验结果 1、蜂鸣器奏乐 我们小车底板配置有蜂鸣器&#xff0c;下面我们来学习如何去利用蜂鸣器演奏乐曲 2、实验目的 学…

ESP32 Arduino实战协议篇-搭建独立的 Web 服务器

在此项目中,您将创建一个带有 ESP32 的独立 Web 服务器,该服务器使用 Arduino IDE 编程环境控制输出(两个 LED)。Web 服务器是移动响应的,可以使用本地网络上的任何浏览器设备进行访问。我们将向您展示如何创建 Web 服务器以及代码如何逐步工作。 项目概况 在直接进入项目…

【机器学习12】集成学习

1 集成学习分类 1.1 Boosting 训练基分类器时采用串行的方式&#xff0c; 各个基分类器之间有依赖。每一层在训练的时候&#xff0c; 对前一层基分类器分错的样本&#xff0c; 给予更高的权重。 测试时&#xff0c; 根据各层分类器的结果的加权得到最终结果。 1.2 Bagging …

【Linux】21、软中断、网络小包、SYN FLOOD 攻击、sar tcpdump

文章目录 一、通俗理解&#xff1a;从“取外卖”看中断二、软中断2.1 网卡收发数据包2.2 查看软中断和内核线程2.3 案例2.3.1 案例&#xff1a;动态库 sleep 导致软中断2.3.2 Nginx 进程的不可中断状态是系统的一种保护机制&#xff0c;可以保证硬件的交互过程不被意外打断。所…

【数据结构算法(一)】递归篇(常见实例讲解)

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; ⭐本篇讲解实例&#xff1a; 斐波那契、兔子问题、猴子吃桃问题、跳台阶问题、汉诺塔、杨辉三角 ⭐用到的递归思想&#xff1a; 无记忆递归、记忆递归(重点掌握) 目录 一、斐波那契&#xff1a; ①无记忆多路递归&am…

NSSCTF第13页(2)

[HNCTF 2022 Week1]Challenge__rce 提示?hint 访问看到了源码 <?php error_reporting(0); if (isset($_GET[hint])) { highlight_file(__FILE__); } if (isset($_POST[rce])) { $rce $_POST[rce]; if (strlen($rce) < 120) { if (is_string($rce…

十八、Linux任务调度crond和at

1、crond任务调度 crond进行 定时任务的设置 概述 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序。 任务调度分类&#xff1a;1.系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等 个别用户工作&#xff1a;个别用户可希望执行某些程序…