五、程序员指南:数据平面开发套件,2024最新网易前端面试题目

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

令牌桶速率计算公式

令牌桶速率(以每秒字节数计)可以使用以下公式计算:
bucket_rate = (tb_credits_per_period / tb_period) * r
其中,

  • ( r ) = 端口线速率(以每秒字节数计)。
  • (\text{tb_credits_per_period}) = 令牌桶每周期产生的信用数量。
  • (\text{tb_period}) = 令牌桶周期(以秒为单位)。

表 21.9:令牌桶操作

#Token bucket operationDescription
1Initializationtb_credits = 0; 或者 tb_credits = tb_size / 2;
2Credit update信用更新选项: • 每次为端口发送数据包时,更新该端口的所有子端口和管道的信用。不可行。 • 每次发送数据包时,更新管道和子端口的信用。非常准确,但不需要(需要大量计算)。 • 每次选择管道(即由研磨器之一选择)时,更新管道及其子端口的信用。当前实现使用选项3。根据出队状态机章节,在实际使用管道和子端口信用之前,每次由出队过程选择管道时都会更新管道和子端口信用。 实现在准确性和速度之间进行权衡,仅在至少经过一个完整的 tb_period 自上次更新以来才更新桶信用。 • 通过选择 tb_credits_per_period = 1 的 tb_period 值可以实现完全的准确性。 • 当不需要完全的准确性时,通过将 tb_credits 设置为较大的值可以实现更好的性能。 更新操作: • n_periods =(时间 - tb_time)/ tb_period; • tb_credits += n_periods * tb_credits_per_period; • tb_credits = min(tb_credits,tb_size); • tb_time += n_periods * tb_period;
严格优先级调度和上限强制

严格优先级调度实现
同一管道内的流量类别的严格优先级调度由管道出队状态机实现,它按升序选择队列。因此,处理队列0到3(与最高优先级TC 0相关联的队列)先于处理队列4到7(TC 1,优先级低于TC 0),而这些队列又先于处理队列8到11(TC 2),依此类推先于处理队列12到15(TC 3,优先级最低的TC)。

上限强制
管道和子端口级别的流量类别没有进行流量整形,因此在这个情境下没有维护令牌桶。子端口和管道级别的流量类别的上限由定期重新填充子端口/管道流量类别信用计数器来执行。每当为该子端口/管道调度数据包时,就会消耗其中的信用,如表10和表11所描述。

表21.10:子端口/管道流量类别上限强制持久化数据结构

#Subport or pipe fieldUnitDescription
1tc_timeBytes当前子端口/管道的4个 TC 的下一次更新时间(上限重新填充)。请参阅内部时间参考部分,了解时间为何以字节单位维护的解释。
2tc_periodBytes当前子端口/管道的4个 TC 之间的连续更新时间。预期这个值会比令牌桶 tb_period 的典型值大很多倍。
3tc_credits_per_periodBytes在每个强制执行周期 tc_period 中允许当前 TC 消耗的信用数量的上限。
4tc_creditsBytes当前流量类别可在当前强制执行周期剩余时间内消耗的信用数量的当前上限。

表 21.11:子端口/管道流量类别上限强制执行操作

#Traffic Class OperationDescription
1Initializationtc_credits = tc_credits_per_period; tc_time = tc_period;
2Credit update更新操作: 如果(时间 >= tc_time){ tc_credits = tc_credits_per_period; tc_time = time + tc_period;}
3Credit consumption (on packet scheduling)作为数据包调度的结果,TC 限制随着所需的信用数量减少。只有在当前 TC 限制中有足够的信用来发送完整数据包(数据包字节和数据包的帧开销)时,才能发送该数据包。调度操作: pkt_credits = pk_len + frame_overhead; 如果(tc_credits >= pkt_credits){tc_credits -= pkt_credits;}

加权循环法 (WRR)
WRR 设计解决方案从简单到复杂的演变如表 12 所示。
表 21.12:加权循环法 (WRR)

#All Queues Active?Equal Weights for All Queues?All Packets Equal?Strategy
1YesYesYes字节级循环轮询(Byte level round robin) Next queue: queue #i,i = (i + 1) % n
2YesYesNo分组级循环轮询(Packet level round robin) 每从队列 #i 消耗一个字节需要消耗队列 #i 的一个令牌。T(i) = 之前从队列 #i 消耗的令牌总数。每次从队列 #i 消耗一个数据包时,T(i) 更新为:T(i) += pkt_len。 Next queue:具有最小 T 的队列。
3YesNoNo分组级加权轮询(Packet level weighted round robin) 通过为每个队列引入不同的每字节成本,将此情况简化为前一情况。具有较低权重的队列每字节的成本较高。这样,仍然有意义地比较不同队列之间的消耗,以选择下一个队列。w(i) = 队列 #i 的权重 t(i) = 队列 #i 的每字节令牌,定义为队列 #i 的倒数权重。例如,如果 w[0…3] = [1:2:4:8],则 t[0…3] = [8:4:2:1];如果 w[0…3] = [1:4:15:20],则 t[0…3] = [60:15:4:3]。每从队列 #i 消耗一个字节需要为队列 #i 消耗 t(i) 个令牌。T(i) = 之前从队列 #i 消耗的令牌总数。每次从队列 #i 消耗一个数据包时,T(i) 更新为:T(i) += pkt_len * t(i)。Next queue:具有最小 T 的队列。
4NoNoNo可变队列状态的分组级加权轮询(Packet level weighted round robin with variable queue status) 通过将不活动队列的消耗设置为一个较高的数字,将此情况简化为前一情况,以使不活动队列永远不会被最小 T 逻辑选中。为防止连续累加导致 T 溢出,每次数据包消耗后,对所有队列截断 T(i)。例如,T[0…3] = [1000, 1100, 1200, 1300] 截断为 T[0…3] = [0, 100, 200, 300],通过从 T(i) 中减去最小的 T,i = 0…n。这需要在输入队列集中至少有一个活动队列,由出队状态机永远不会选择不活动流量类别来保证。mask(i) = 队列 #i 的饱和掩码,定义为:mask(i) = (队列 #i 是否活动)? 0 : 0xFFFFFFFF; w(i) = 队列 #i 的权重 t(i) = 队列 #i 的每字节令牌,定义为队列 #i 的倒数权重。T(i) = 之前从队列 #i 消耗的令牌总数。
子端口流量类别超额订阅

问题陈述
对于子端口流量类别X,超额订阅是在配置时发生的事件。这种情况发生在子端口成员管道级别为流量类别X分配的带宽比父子端口级别为相同流量类别分配的带宽更多时。
对于特定子端口和流量类别的超额订阅的存在,纯粹是由于管道和子端口级别的配置而不是由于运行时流量负载的动态演变(就像拥塞一样)。

当流量类别X的整体需求较低时
对于当前子端口的流量类别X的整体需求较低时,超额订阅条件的存在并不代表问题,因为对于所有成员管道,流量类别X的需求得到了完全满足。然而,当所有子端口成员管道的流量类别X的总需求超过了在子端口级别配置的限制时,这就无法再实现了。

解决方案空间
解决这个问题的一些可能方法被总结如下,其中第三种方法被选定用于实现。

表21.13:子端口流量类别超额订阅

No.ApproachDescription
1Don’t care首来先服务。这种方法在子端口成员管道之间不公平,因为首先服务的管道将根据它们需要的 TC X 的带宽使用尽可能多的带宽,而稍后服务的管道由于子端口级别的 TC X 带宽稀缺而接收到较差的服务。
2Scale down all pipes子端口内的所有管道的 TC X 的带宽限制按相同比例缩减。这种方法在子端口成员管道之间不公平,因为低端管道(即配置带宽较低的管道)可能会遭受严重的服务降级,可能导致其服务不可用(如果这些管道的可用带宽降至可用服务的最低要求以下),而高端管道的服务降级可能根本不可察觉。
3Cap the high demand pipes每个子端口成员管道在子端口级别的 TC X 可用带宽上收到相等份额。任何未被低需求管道使用的带宽会以相等份额重新分配给高需求管道。这样,高需求管道被截断,而低需求管道不受影响。
子端口流量类别超额订阅实现概述

典型情况下
通常情况下,子端口流量类别(TC)超额订阅功能仅对最低优先级流量类别(TC 3)启用,该流量类别通常用于尽力而为的流量,并且管理平面可以防止此条件发生于其他(优先级更高的)流量类别。

前提假设
为了简化实现,还假设子端口 TC 3 的上限设置为子端口速率的 100%,管道 TC 3 的上限对于所有子端口成员管道也设置为各自管道速率的 100%。

实现概述
算法计算一个水位线(watermark),它基于子端口成员管道当前需求而周期性更新,其目的是限制每个管道允许发送给 TC 3 的流量量。水位线在每个流量类别上限执行期间的开始时在子端口级别计算,并且相同的值在整个当前执行期间内被所有子端口成员管道使用。下图说明了水位线如何在每个期间开始时从子端口级别传播到所有子端口成员管道。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在当前执行期间的开始(与前一执行期间结束同时),水位线的值根据前一期间开始时分配给 TC 3 的带宽量进行调整,而该量在前一期间结束时未被子端口成员管道使用。

如果存在未使用的子端口 TC 3 带宽,当前期间水位线的值将增加,以鼓励子端口成员管道消耗更多带宽。否则,水位线的值将减少,以强制要求 TC 3 的带宽消耗在子端口成员管道之间平等。

水位线值的增加或减少以小幅度增量进行,因此可能需要多个执行周期才能达到平衡状态。这种状态可能随时发生变化,原因是子端口成员管道对 TC 3 的需求发生变化,例如需求增加时(需要降低水位线)或需求减少时(需要增加水位线)。

需求低时,水位线设置得较高,以防止其阻碍子端口成员管道消耗更多带宽。水位线的最高值是从子端口成员管道配置的最高速率中选择的。

表21.14:水位线从子端口级别传播到每个流量类别上限执行期间的成员管道开始时

No.Subport Traffic Class OperationDescription
1Initialization子端口级别:subport_period_id = 0管道级别:pipe_period_id = 0
2Credit update子端口级别:如果(时间 >= subport_tc_time){subport_wm = water_mark_update(); subport_tc_time = time + subport_tc_period; subport_period_id++;} 管道级别:如果(pipe_period_id != subport_period_id){pipe_ov_credits = subport_wm * pipe_weight; pipe_period_id = subport_period_id;}
3Credit consumption (on packet scheduling)管道级别:pkt_credits = pk_len + frame_overhead; 如果(pipe_ov_credits >= pkt_credits){pipe_ov_credits -= pkt_credits;}

表 21.15:水印计算

No.Subport Traffic Class OperationDescription
1Initialization子端口级别:wm = WM_MAX
2Credit update子端口级别(water_mark_update):tc0_cons = subport_tc0_credits_per_period - subport_tc0_credits; tc1_cons = subport_tc1_credits_per_period - subport_tc1_credits; tc2_cons = subport_tc2_credits_per_period - subport_tc2_credits; tc3_cons = subport_tc3_credits_per_period - subport_tc3_credits; tc3_cons_max = subport_tc3_credits_per_period - (tc0_cons + tc1_cons + tc2_cons); 如果(tc3_consumption > (tc3_consumption_max - MTU)){ wm -= wm >> 7; if(wm < WM_MIN) wm = WM_MIN; } else { wm += (wm >> 7) + 1; if(wm > WM_MAX) wm = WM_MAX; }
21.2.5 性能最差情景

大量活跃队列但信用不足
调度器需要检查大量队列以选择一个包和信用时,其性能会降低。调度器维护活跃队列的位图,跳过非活跃队列,但为了检测特定管道是否有足够的信用,需要使用管道出队状态机深入探查管道,这会消耗周期,而不管调度结果如何(无包生成或至少生成一个包)。这种情况强调了对调度器性能的速度控制的重要性:如果管道没有足够的信用,其数据包应尽快被丢弃(在到达分层调度器之前),从而将管道队列渲染为非活跃状态,允许出队端跳过该管道而无需花费用于调查管道信用的周期,因为这将导致“信用不足”状态。

单个队列达到100%线速率
端口调度器的性能针对大量队列进行了优化。如果队列数较少,则相同活跃流量水平下,端口调度器的性能预计会比小型消息传递队列的性能差。

21.3 丢弃器

DPDK丢弃器的目的
DPDK丢弃器的目的是在分组调度器到达时丢弃分组,以避免拥塞。丢弃器支持随机早期检测(RED)、加权随机早期检测(WRED)和尾部丢弃算法。图21.7说明了丢弃器如何与调度器集成。目前DPDK不支持拥塞管理,因此丢弃器提供了唯一的拥塞避免方法。

在这里插入图片描述
图 21.7:DPDK Dropper 的高级框图

丢弃器使用的拥塞避免算法

丢弃器使用文献中记录的随机早期检测(RED)拥塞避免算法。RED算法的目的是监视数据包队列,确定队列的当前拥塞水平,并决定是否应该将到达的数据包入队或丢弃。RED算法使用指数加权移动平均(EWMA)滤波器来计算平均队列大小,从而指示队列的当前拥塞水平。

对于每个入队操作,RED算法将平均队列大小与最小和最大阈值进行比较。根据平均队列大小是低于、高于还是介于这些阈值之间的情况,RED算法计算到达数据包应该被丢弃的概率,并基于这一概率进行随机决策。

丢弃器还通过允许调度器在运行时为同一个数据包队列选择不同的RED配置来支持加权随机早期检测(WRED)。在严重拥塞情况下,丢弃器会使用尾部丢弃。当数据包队列达到最大容量并且无法存储更多数据包时,就会发生尾部丢弃。在这种情况下,所有到达的数据包都会被丢弃。

丢弃器的数据流程如图21.8所示。首先执行RED/WRED算法,其次执行尾部丢弃。

丢弃器支持的使用案例有:

  • 初始化配置数据
  • 初始化运行时数据
  • 入队(决定是将到达的数据包入队还是丢弃)
  • 标记为空(记录数据包队列变为空的时间)

配置用例在"配置"中解释,入队操作在"入队操作"中解释,标记为空操作在"队列为空操作"中解释。

21.3.1 配置

RED配置包含表21.16中给出的参数。

表21.16:RED配置参数

ParameterMinimumMaximumTypical
Minimum Threshold010221/4 x queue size
Maximum Threshold110231/2 x queue size
Inverse Mark Probability125510
EWMA Filter Weight1129

这些参数的含义在后续章节中将会详细解释。这些参数按照其指定给丢弃器模块API的格式,对应于思科*在其RED实现中所使用的格式。最小和最大阈值参数以数据包数量的形式提供给丢弃器模块。标记概率参数以逆值指定,例如,标记概率参数值为10对应于标记概率为1/10(即,10个数据包中将丢弃1个)。EWMA滤波器权重参数以逆对数值指定,例如,滤波器权重参数值为9对应于滤波器权重为1/29。

21.3.2 入队操作

在图21.9中所示的示例中,q(实际队列大小)是输入值,avg(平均队列大小)和count(自上次丢弃以来的数据包数量)是运行时值,decision是输出值,其余数值为配置参数。

在这里插入图片描述
图 21.8:通过滴管的流量

在这里插入图片描述
图 21.9:通过 Dropper 的数据流示例

EWMA滤波器微块

EWMA滤波器微块的目的是对队列大小值进行滤波,以平滑“突发”流量所导致的瞬态变化。输出值为平均队列大小,从而更稳定地反映了队列当前的拥塞水平。

EWMA滤波器具有一个配置参数,即滤波器权重,它决定了平均队列大小输出对实际队列大小变化的快慢响应。滤波器权重值越高,平均队列大小对实际队列大小的变化响应就越快。

队列非空时的平均队列大小计算

EWMA滤波器的定义如下方程所示:
在这里插入图片描述

其中:

  • avg = 平均队列大小
  • wq = 滤波器权重
  • q = 实际队列大小
  • R = 固定整数,表示除法的位移量

队列为空时的平均队列大小计算

当队列为空时,EWMA滤波器不会读取时间戳,而是假定入队操作会相当定期地发生。当队列变为空时,需要进行特殊处理,因为队列可能短时间或长时间为空。队列变为空时,平均队列大小应该逐渐衰减到零,而不是突然降为零或停留在上次计算的值上。当在空队列上入队一个数据包时,平均队列大小使用以下公式计算:
avg = (m * avg) / (2^R)

其中:

  • m = 在队列为空时可能发生的入队操作数

在dropper模块中,m被定义为:
在这里插入图片描述

其中:

  • time = 当前时间
  • qtime = 队列变为空的时间
  • s = 在该队列上连续入队操作之间的典型时间

时间参考以字节为单位,其中每个字节表示物理接口在传输介质上发送一个字节所需的时间(参见"内部时间参考"部分)。参数s在dropper模块中被定义为一个常数,其值为:s=2^22。这对应于在具有64K个叶节点的层次结构中,每个叶节点传输一个64字节数据包到传输介质上所需的时间,并代表了最坏情况。对于规模较小的调度器层次结构,可能需要减小参数s,在red头文件源文件(rte_red.h)中定义为:
#define RTE_RED_S

由于时间参考是以字节为单位的,因此端口速度被包含在表达式time-qtime中。dropper无需配置实际端口速度,它会自动适应低速和高速链路。

实现

采用数值方法来计算方程2中出现的(1-wq)^m因子。这个方法基于以下恒等式:
在这里插入图片描述

这使我们能够表达如下:
在这里插入图片描述

在dropper模块中,使用查找表来为dropper模块支持的每个wq值计算log2(1-wq)。然后,可以通过将表值乘以m并进行位移操作来获得(1-wq)m因子。为了避免乘法中的溢出,值m和查找表值都限制在16位。查找表的总大小为56字节。一旦使用这种方法获得了(1-wq)m因子,就可以从方程2中计算出平均队列大小。

替代方法

考虑了其他计算在队列为空时(方程2)计算平均队列大小所需的因子(1-wq)^m的方法。这些方法包括:

  • 浮点数计算
  • 使用小查找表(512B)和最多16次乘法的定点数计算(这是FreeBSD* ALTQ RED实现中使用的方法)
  • 使用小查找表(512B)和16个SSE乘法的定点数计算(FreeBSD* ALTQ RED实现的SSE优化版本)
  • 大查找表(76 KB)

最终选择的方法(在上面的“实现”部分中描述)在运行时性能和内存需求方面优于所有这些方法,并且在精度上也达到了与浮点数计算相当的水平。表17列出了这些替代方法相对于dropper使用的方法的性能。可以看到,浮点数实现性能最差。

表21.17:替代方法的相对性能

MethodRelative Performance
Current dropper method (see Dropper)100%
Fixed-point method with small (512B) look-up table148%
SSE method with small (512B) look-up table114%
Large (76KB) look-up table118%
Floating-point595%
注意:

在这种情况下,由于性能是以在特定条件下执行操作所花费的时间来表示的,任何超过100%的相对性能值都比参考方法运行得更慢。

丢弃决策模块

丢弃决策模块:

  • 比较平均队列大小与最小和最大阈值
  • 计算丢包概率
  • 随机决定是否对到达的数据包进行入队或丢弃

丢包概率的计算分为两个阶段。首先根据平均队列大小、最小和最大阈值以及标记概率计算初始丢包概率。然后,从初始丢包概率计算实际丢包概率。实际丢包概率考虑了计数的运行时值,因此随着自上次丢包以来到达队列的数据包越来越多,实际丢包概率也会增加。

初始数据包丢包概率

初始丢包概率使用以下方程计算:
在这里插入图片描述

其中:

  • maxp = 标记概率
  • avg = 平均队列大小
  • minth = 最小阈值
  • maxth = 最大阈值

方程3中使用平均队列大小计算数据包丢失概率的过程如图21.10所示。如果平均队列大小低于最小阈值,则将到达的数据包入队。如果平均队列大小达到或超过最大阈值,则将到达的数据包丢弃。如果平均队列大小介于最小和最大阈值之间,则计算丢包概率以确定是否应将数据包入队或丢弃。

实际丢包概率

如果平均队列大小介于最小和最大阈值之间,则实际丢包概率由以下方程计算:
在这里插入图片描述

其中:

  • Pb = 初始丢包概率(来自方程3)
  • count = 自上次丢包以来到达的数据包数量

方程4中的常数2是与参考文档给出的丢包概率公式唯一的偏差,参考文档中使用了值1。需要注意的是,从计算得到的pa可能为负值或大于1。如果是这种情况,则应该使用值1。

图21.11显示了初始和实际丢包概率。实际丢包概率分别使用了参考文档中给出的公式(蓝色曲线)和dropper模块中实现的公式(红色曲线)。与用户指定的标记概率配置参数相比,参考文档中的公式导致了明显更高的丢包率。与参考文档的偏差只是一个设计决策,其他RED实现(例如FreeBSD* ALTQ RED)也做出了类似的选择。
在这里插入图片描述
图 21.10:给定 RED 配置的数据包丢弃概率
在这里插入图片描述
图 21.11:使用 a 计算的初始掉落概率 (pb)、实际掉落概率 (pa)
因子 1(蓝色曲线)和因子 2(红色曲线)

21.3.3 队列为空操作

记录数据包队列变为空的时间,并将其保存到RED运行时数据中,以便EWMA过滤器块在下一次入队操作时计算平均队列大小。通过API通知dropper模块队列已经变为空是调用应用程序的责任。

21.3.4 源文件位置

DPDK dropper的源文件位于:

  • DPDK/lib/librte_sched/rte_red.h
  • DPDK/lib/librte_sched/rte_red.c
21.3.5 与DPDK QoS调度器的集成

DPDK QoS调度器中的RED功能默认情况下是禁用的。要启用它,请使用DPDK配置参数:
CONFIG_RTE_SCHED_RED=y
必须将此参数设置为y。该参数位于DPDK/config目录中的构建配置文件中,例如,DPDK/config/common_linuxapp。在初始化过程中,RED配置参数在传递给调度器的rte_sched_port_params结构中的rte_red_params结构中进行指定。RED参数分别针对四个流量类别和三种数据包颜色(绿色、黄色和红色)进行指定,允许调度器实现加权随机早期检测(WRED)。

21.3.6 与DPDK QoS调度器示例应用程序的集成

DPDK QoS调度器应用程序在启动时读取一个配置文件。配置文件包括一个包含RED参数的部分。这些参数的格式在“配置”中有描述。下面是一个示例RED配置。在此示例中,队列大小为64个数据包。
注意:为了正确运行,应该在相同流量类别(tc)中的每种数据包颜色(绿色、黄色、红色)中使用相同的EWMA过滤器权重参数(wred weight)。

; RED params per traffic class and color (Green / Yellow / Red)
[red]
tc 0 wred min = 28 22 16
tc 0 wred max = 32 32 32
tc 0 wred inv prob = 10 10 10
tc 0 wred weight = 9 9 9
tc 1 wred min = 28 22 16
tc 1 wred max = 32 32 32
tc 1 wred inv prob = 10 10 10
tc 1 wred weight = 9 9 9
tc 2 wred min = 28 22 16
tc 2 wred max = 32 32 32
tc 2 wred inv prob = 10 10 10
tc 2 wred weight = 9 9 9
tc 3 wred min = 28 22 16
tc 3 wred max = 32 32 32
tc 3 wred inv prob = 10 10 10
tc 3 wred weight = 9 9 9

通过此配置文件,适用于绿色、黄色和红色数据包的 RED 配置
流量类别 0 的情况如表 18 所示。
表 21.18:与 RED 配置相对应的 RED 配置文件

Parameter NameGreenYellowRed
Minimum Threshold282216
Maximum Threshold323232
Mark Probability101010
EWMA Filter Weight999
21.3.7 应用程序编程接口(API)

Enqueue API
enqueue API 的语法如下:

int rte\_red\_enqueue(const struct rte\_red\_config \*red_cfg,
struct rte\_red \*red,
const unsigned q,
const uint64\_t time)

传递给 enqueue API 的参数包括配置数据、运行时数据、数据包队列的当前大小(以数据包为单位)以及表示当前时间的值。时间参考以字节为单位,其中一个字节表示物理接口在传输介质上发送一个字节所需的时间持续。(请参阅内部时间参考部分)。为了性能考虑,丢弃器重用调度器的时间戳。
Empty API
empty API 的语法如下:

void rte\_red\_mark\_queue\_empty(struct rte\_red \*red, const uint64\_t time)

传递给 empty API 的参数包括运行时数据和以字节表示的当前时间。

21.4 交通计量

交通计量组件实现了由 IETF RFC 2697 和 2698 定义的单速率三色标记器(srTCM)和双速率三色标记器(trTCM)算法。这些算法根据预先为每个流量流定义的允许量来测量传入数据包流。因此,每个传入的数据包根据其所属流的监测消耗被标记为绿色、黄色或红色。

21.4.1 功能概述

srTCM 算法为每个流量流定义了两个令牌桶,这两个桶共享相同的令牌更新速率:
• 承诺(C)桶:以承诺信息速率(CIR)参数定义的速率提供令牌(以每秒 IP 数据包字节为单位)。C 桶的大小由承诺突发大小(CBS)参数定义(以字节为单位);
• 过剩(E)桶:以与 C 桶相同的速率提供令牌。E 桶的大小由过剩突发大小(EBS)参数定义(以字节为单位)。
trTCM 算法为每个流量流定义了两个令牌桶,这两个桶以独立的速率更新令牌:
• 承诺(C)桶:以承诺信息速率(CIR)参数定义的速率提供令牌(以每秒 IP 数据包字节为单位)。C 桶的大小由承诺突发大小(CBS)参数定义(以字节为单位);
• 峰值(P)桶:以峰值信息速率(PIR)参数定义的速率提供令牌(以每秒 IP 数据包字节为单位)。P 桶的大小由峰值突发大小(PBS)参数定义(以字节为单位)。
请参阅 RFC 2697(用于 srTCM)和 RFC 2698(用于 trTCM)以获取有关如何从桶中消耗令牌以及确定数据包颜色的详细信息。
色盲和色感知模式
对于这两种算法,色盲模式在功能上等同于输入颜色设置为绿色的色感知模式。对于色感知模式,具有红色输入颜色的数据包只能获得红色输出颜色,而具有黄色输入颜色的数据包只能获得黄色或红色输出颜色。
色盲模式仍然与色感知模式有明显区别的原因是,与色感知模式相比,色盲模式可以用更少的操作来实现。

21.4.2 实现概述

对于每个输入数据包,srTCM / trTCM 算法的步骤如下:
• 更新 C 和 E / P 令牌桶。这是通过读取当前时间(从 CPU 时间戳计数器获取),识别自上次桶更新以来的时间量,并根据预先配置的桶速率计算关联的令牌数量来完成的。桶中的令牌数量受到预先配置的桶大小的限制;
• 根据 IP 数据包的大小和 C 和 E / P 令牌桶中当前可用的令牌数量,确定当前数据包的输出颜色;对于仅色感知模式,还考虑数据包的输入颜色。当输出颜色不为红色时,从 C 或 E / P 桶中减去与 IP 数据包长度相等的令牌数量,取决于算法和数据包的输出颜色。

POWER MANAGEMENT

DPDK 的电源管理功能允许用户空间应用程序通过动态调整 CPU 频率或进入不同的 C 状态来节省电力。
• 根据 RX 队列的利用率动态调整 CPU 频率。
• 根据自适应算法进入不同的深度 C 状态,用于猜测短暂的挂起应用程序的时间,如果未收到数据包。
用于调整操作 CPU 频率的接口位于电源管理库中。C 状态控制根据不同的使用情况在应用程序中实现。

22.1 CPU 频率调节

Linux 内核为每个逻辑核心提供了一个 cpufreq 模块用于 CPU 频率调节。例如,对于 cpuX,/sys/devices/system/cpu/cpuX/cpufreq/ 包含以下与频率调节相关的 sys 文件:
• affected_cpus
• bios_limit
• cpuinfo_cur_freq
• cpuinfo_max_freq
• cpuinfo_min_freq
• cpuinfo_transition_latency
• related_cpus
• scaling_available_frequencies
• scaling_available_governors
• scaling_cur_freq
• scaling_driver
• scaling_governor
• scaling_max_freq
• scaling_min_freq
• scaling_setspeed
在 DPDK 中,scaling_governor 在用户空间中配置。然后,用户空间应用程序可以通过写入 scaling_setspeed 来提示内核根据用户空间应用程序定义的策略调整 CPU 频率。

22.2 通过 C 状态对核心负载进行节流

当指定的逻辑核心没有任务时,核心状态可以通过猜测性的睡眠来改变。在 DPDK 中,如果轮询后未收到任何数据包,则可以根据用户空间应用程序定义的策略触发猜测性睡眠。

22.3 电源库的 API 概述

电源库导出的主要方法是用于 CPU 频率调节的,包括以下内容:
• Freq up:提示内核增加特定逻辑核心的频率。
• Freq down:提示内核降低特定逻辑核心的频率。
• Freq max:提示内核将特定逻辑核心的频率调至最大。
• Freq min:提示内核将特定逻辑核心的频率调至最小。
• Get available freqs:从 sys 文件中读取特定逻辑核心的可用频率。
• Freq get:获取特定逻辑核心的当前频率。
• Freq set:提示内核设置特定逻辑核心的频率。

22.4 用户案例

电源管理机制用于在执行 L3 转发时节省功耗。

22.5 参考资料

• l3fwd-power:DPDK 中执行带有电源管理的 L3 转发的示例应用程序。
• DPDK 示例应用程序用户指南中的“带有电源管理的 L3 转发示例应用程序”章节。

PACKET CLASSIFICATION AND ACCESS CONTROL

DPDK 提供了一个访问控制库,它具有基于一组分类规则对输入数据包进行分类的能力。
ACL 库用于在具有多个类别的一组规则上执行 N 元搜索,并为每个类别找到最佳匹配(最高优先级)。库 API 提供了以下基本操作:
• 创建新的访问控制(AC)上下文。
• 将规则添加到上下文中。
• 对上下文中的所有规则,构建执行数据包分类所需的运行时结构。
• 执行输入数据包的分类。
• 销毁 AC 上下文及其运行时结构,并释放相关内存。

23.1 概述
23.1.1 规则定义

当前实现允许用户为每个 AC 上下文指定其自己的规则(字段集),以执行数据包分类。尽管规则字段布局上有一些限制:
• 规则定义中的第一个字段必须是一个字节长。
• 所有后续字段必须分组为连续 4 个字节的集合。
这主要是出于性能考虑 - 搜索函数将第一个输入字节作为流程设置的一部分处理,然后搜索函数的内部循环展开为一次处理四个输入字节。
为定义 AC 规则内的每个字段,使用以下结构:

struct rte\_acl\_field\_def {
uint8\_t type; /\*< type - ACL\_FIELD\_TYPE. \*/
uint8\_t size; /\*< size of field 1,2,4, or 8. \*/
uint8\_t field_index; /\*< index of field inside the rule. \*/
uint8\_t input_index; /\*< 0-N input index. \*/
uint32\_t offset; /\*< offset to start of field. \*/
};

• 类型:字段类型有三种选择之一:
– _MASK - 用于具有值和定义相关位数的掩码的字段,例如 IP 地址。
– _RANGE - 用于具有字段的下限和上限值的字段,例如端口。
– _BITMASK - 用于具有值和位掩码的协议标识符字段。
• 大小:大小参数定义字段的字节长度。允许的值为 1、2、4 或 8 字节。注意,由于输入字节的分组,1 或 2 字节字段必须定义为组成 4 个连续输入字节的连续字段。此外,最好将 8 字节或更多字节的字段定义为 4 字节字段,以便构建过程可以消除所有为通配符的字段。
• 字段索引:表示规则内字段位置的从零开始的值;对于 N 个字段,范围为 0 到 N-1。
• 输入索引:如上所述,除了第一个字段外,所有输入字段必须分组为 4 个连续字节。输入索引指定该字段属于哪个输入组。
• 偏移量:偏移字段定义字段的偏移量。这是从搜索的 buffer 参数开始的偏移量。
例如,要定义以下 IPv4 5-tuple 结构的分类:

struct ipv4\_5tuple {
uint8\_t proto;
uint32\_t ip_src;
uint32\_t ip_dst;
uint16\_t port_src;
uint16\_t port_dst;
};

可以使用以下字段定义数组:

#include <rte\_acl.h> // 假设相关的头文件

// 定义一个包含5个 rte\_acl\_field\_def 结构的数组,用于 ACL 匹配
struct rte\_acl\_field\_def ipv4_defs[5] = {
    /\* 第一个输入字段 - 总是一个字节长。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8\_t),
        .field_index = 0,
        .input_index = 0,
        .offset = offsetof(struct ipv4\_5tuple, proto),
    },
    /\* 下一个输入字段(IPv4 源地址)- 连续的4个字节。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 1,
        .input_index = 1,
        .offset = offsetof(struct ipv4\_5tuple, ip_src),
    },
    /\* 下一个输入字段(IPv4 目标地址)- 连续的4个字节。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 2,
        .input_index = 2,
        .offset = offsetof(struct ipv4\_5tuple, ip_dst),
    },
    /\*
 \* 接下来的2个字段(源和目标端口)共同占用4个连续字节。
 \* 它们共享相同的输入索引。
 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16\_t),
        .field_index = 3,
        .input_index = 3,
        .offset = offsetof(struct ipv4\_5tuple, port_src),
    },
    {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16\_t),
        .field_index = 4,
        .input_index = 3,
        .offset = offsetof(struct ipv4\_5tuple, port_dst),
    },
};


这种 IPv4 5 元组规则的典型示例如下:

source addr/mask destination addr/mask source ports dest ports protocol/mask
192.168.1.0/24 192.168.2.31/32 0:65535 1234:1234 17/0xff

协议 ID 17 (UDP)、源地址 192.168.1.[0-255]、目标地址的任何 IPv4 数据包
地址192.168.2.31,源端口[0-65535]和目标端口1234与上面匹配规则。
要定义 IPv6 2 元组的分类:<协议,IPv6 源地址> 基于以下内容
IPv6 标头结构:

struct struct ipv6_hdr {
uint32\_t vtc_flow; /\* IP version, traffic class & flow label. \*/
uint16\_t payload_len; /\* IP packet length - includes sizeof(ip\_header). \*/
uint8\_t proto; /\* Protocol, next header. \*/
uint8\_t hop_limits; /\* Hop limits. \*/
uint8\_t src_addr[16]; /\* IP address of source host. \*/
uint8\_t dst_addr[16]; /\* IP address of destination host(s). \*/
} \_\_attribute\_\_((__packed__));

可以使用以下字段定义数组:

#include <rte\_acl.h> // 可能需要包含相关的头文件

// 定义一个包含5个 rte\_acl\_field\_def 结构的数组,用于 IPv6 两元组匹配
struct rte\_acl\_field\_def ipv6_2tuple_defs[5] = {
    /\* 第一个输入字段 - 总是一个字节长,用于IPv6协议类型。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8\_t),
        .field_index = 0,
        .input_index = 0,
        .offset = offsetof(struct ipv6\_hdr, proto),
    },
    /\* 下一个输入字段(IPv6源地址的前4个字节)。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 1,
        .input_index = 1,
        .offset = offsetof(struct ipv6\_hdr, src_addr[0]),
    },
    /\* 接下来的输入字段(IPv6源地址的第5至第8字节)。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 2,
        .input_index = 2,
        .offset = offsetof(struct ipv6\_hdr, src_addr[4]),
    },
    /\* 后续的输入字段(IPv6源地址的第9至第12字节)。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 3,
        .input_index = 3,
        .offset = offsetof(struct ipv6\_hdr, src_addr[8]),
    },
    /\* 最后的输入字段(IPv6源地址的第13至第16字节)。 \*/
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32\_t),
        .field_index = 4,
        .input_index = 4,
        .offset = offsetof(struct ipv6\_hdr, src_addr[12]),
    },
};


这种 IPv6 2 元组规则的典型示例如下:

source addr/mask                         		protocol/mask
2001:db8:1234:0000:0000:0000:0000:0000/48 		6/0xff

任何 IPv6 数据包的协议 ID 为 6(TCP),且源地址位于范围 [2001:db8🔢0000:0000:0000:0000:0000 - 2001:db8🔢ffff:ffff:ffff:ffff:ffff] 内,都符合上述规则。
在创建一组规则时,对于每个规则,还必须提供额外的信息:
• 优先级:用于衡量规则优先级的权重(值越高越好)。如果输入元组匹配多个规则,则返回优先级较高的规则。请注意,如果输入元组匹配多个具有相同优先级的规则,则未定义将返回哪个规则作为匹配。建议为每个规则分配唯一的优先级。
• 类别掩码:每个规则使用位掩码值来选择规则的相关类别。当执行查找时,将为每个类别返回结果。这有效地通过使单个搜索能够返回多个结果来提供“并行查找”,例如,如果有四个不同的 ACL 规则集,一个用于访问控制,一个用于路由等。每个集合可以分配其自己的类别,并通过将它们合并为单个数据库,使单个查找返回每个集合的结果。
• 用户数据:一个用户定义的字段,可以是除零之外的任何值。对于每个类别,成功匹配将返回具有最高优先级的匹配规则的 userdata 字段。
注意:将新规则添加到 ACL 上下文时,所有字段必须是主机字节顺序(LSB)。当对输入元组执行搜索时,元组中的所有字段必须是网络字节顺序(MSB)。

23.1.2 RT 内存大小限制

构建阶段(rte_acl_build())为给定规则集创建用于进一步运行时遍历的内部结构。使用当前实现,它是一组多位 Trie(stride == 8)。根据规则集,这可能会消耗大量内存。为了节省一些空间,ACL 构建过程尝试将给定的规则集拆分为几个不相交的子集,并为每个子集构建单独的 Trie。根据规则集,这可能会减少 RT 内存需求,但可能会增加分类时间。在构建时,可以为给定 AC 上下文的内部 RT 结构指定最大内存限制。可以通过 rte_acl_config 结构的 max_size 字段来完成。将其设置为大于零的值,指示 rte_acl_build():
• 尝试最小化 RT 表中 Trie 的数量,但
• 确保 RT 表的大小不超过给定值。
将其设置为零会使 rte_acl_build() 使用默认行为:尝试最小化 RT 结构的大小,但不会施加任何硬限制。
这使用户能够自行决定性能/空间折衷的方案。例如:

#include <rte\_acl.h> // 可能需要包含相关的头文件

struct rte\_acl\_ctx \*acx; // 定义指向 ACL 上下文的指针
struct rte\_acl\_config cfg; // 定义 ACL 配置结构体
int ret; // 定义用于存储函数返回值的变量

/\*
 \* 假设 acx 指向已创建并填充了规则的 ACL 上下文,
 \* 并且 cfg 被正确填充。
 \*/

/\* 尝试构建 ACL 上下文,RT 结构小于 8MB。\*/
cfg.max_size = 0x800000; // 设置最大大小为 8MB
ret = rte\_acl\_build(acx, &cfg); // 尝试构建 ACL 上下文

/\*
 \* 如果给定上下文的 RT 结构超出了 8MB。
 \* 尝试构建时不暴露任何硬限制。
 \*/
if (ret == -ERANGE) {
    cfg.max_size = 0; // 设置最大大小为 0,不暴露硬限制
    ret = rte\_acl\_build(acx, &cfg); // 再次尝试构建 ACL 上下文
}


23.1.3 分类方法

在给定的 AC 上下文上成功完成 rte_acl_build() 后,可以用于执行分类 - 在输入数据上搜索具有最高优先级的规则。有几种分类算法的实现:
• RTE_ACL_CLASSIFY_SCALAR:通用实现,不需要任何特定的硬件支持。
• RTE_ACL_CLASSIFY_SSE:矢量实现,可以并行处理多达 8 个流。需要 SSE 4.1 支持。
• RTE_ACL_CLASSIFY_AVX2:矢量实现,可以并行处理多达 16 个流。需要 AVX2 支持。
纯粹是一个运行时的决策选择哪种方法,没有构建时的差异。所有实现都在相同的内部 RT 结构上操作,并使用类似的原理。主要区别在于矢量实现可以手动利用 IA SIMD 指令,并并行处理多个输入数据流。在启动时,ACL 库确定给定平台的最高可用分类方法,并将其设置为默认方法。尽管用户可以覆盖给定 ACL 上下文的默认分类器函数或使用非默认的分类方法执行特定搜索。在这种情况下,用户有责任确保给定平台支持所选的分类实现。

23.2 应用程序编程接口(API)使用

注意:有关访问控制 API 的更多详细信息,请参阅 DPDK API 参考。
以下示例更详细地演示了上述定义的带有多个类别的 IPv4 5-tuple 分类规则的示例。

23.2.1 多类别分类
#include <rte\_acl.h> // 可能需要包含相关的头文件

struct rte\_acl\_ctx \*acx; // 定义指向 ACL 上下文的指针
struct rte\_acl\_config cfg; // 定义 ACL 配置结构体
int ret; // 用于存储函数返回值的变量

/\* 定义一个包含多达5个字段的规则结构。\*/
RTE\_ACL\_RULE\_DEF(acl_ipv4_rule, RTE\_DIM(ipv4_defs));

/\* AC 上下文创建参数。\*/
struct rte\_acl\_param prm = {
    .name = "ACL\_example", // ACL 上下文的名称
    .socket_id = SOCKET_ID_ANY, // 指定的套接字 ID
    .rule_size = RTE\_ACL\_RULE\_SZ(RTE\_DIM(ipv4_defs)), // 每个规则的大小
    .max_rule_num = 8, // AC 上下文中规则的最大数量
};

/\* 定义 ACL 规则数组。\*/
struct acl\_ipv4\_rule acl_rules[] = {
    /\* 匹配前往 192.168.0.0/16 的所有数据包,适用于类别:0,1 \*/
    {
        .data = {.userdata = 1, .category_mask = 3, .priority = 1},
        /\* 目标 IPv4 \*/
        .field[2] = {.value.u32 = IPv4(192,168,0,0), .mask_range.u32 = 16,},
        /\* 源端口 \*/
        .field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
        /\* 目标端口 \*/
        .field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
    },
    // 其他规则...

    // 依次添加更多规则...
};

/\* 创建一个空的 AC 上下文 \*/
if ((acx = rte\_acl\_create(&prm)) == NULL) {
    /\* 处理上下文创建失败的情况。\*/
}

/\* 向上下文中添加规则 \*/
ret = rte\_acl\_add\_rules(acx, acl_rules, RTE\_DIM(acl_rules));
if (ret != 0) {
    /\* 处理添加 ACL 规则时的错误。\*/
}

/\* 准备 AC 构建配置。\*/
cfg.num_categories = 2; // 规定的类别数量
cfg.num_fields = RTE\_DIM(ipv4_defs); // 规则中字段的数量
memcpy(cfg.defs, ipv4_defs, sizeof(ipv4_defs)); // 复制字段定义

/\* 构建已添加规则的运行时结构,使用2个类别。 \*/
ret = rte\_acl\_build(acx, &cfg);
if (ret != 0) {
    /\* 处理为 ACL 上下文构建运行时结构时的错误。\*/
}


对于源 IP 地址:10.1.1.1 和目标 IP 地址:192.168.1.15 的元组,一次
执行以下几行:

uint32\_t results[4]; /\* make classify for 4 categories. \*/
rte\_acl\_classify(acx, data, results, 1, 4);

那么 results[] 数组包含:

results[4] = {2, 3, 0, 0};

• 对于类别 0,规则 1 和规则 2 都匹配,但规则 2 优先级更高,因此 results[0] 包含规则 2 的 userdata。
• 对于类别 1,规则 1 和规则 3 都匹配,但规则 3 优先级更高,因此 results[1] 包含规则 3 的 userdata。
• 对于类别 2 和 3,没有匹配,因此 results[2] 和 results[3] 包含零,表示这些类别没有匹配项。
对于源 IP 地址为 192.168.1.1 和目标 IP 地址为 192.168.2.11 的元组,
一旦执行以下行:

uint32\_t results[4]; /\* make classify by 4 categories. \*/
rte\_acl\_classify(acx, data, results, 1, 4);

the results[] array contains:

results[4] = {1, 1, 0, 0};

• 对于类别 0 和 1,只有规则 1 匹配。
• 对于类别 2 和 3,没有匹配项。
对于源 IP 地址为 10.1.1.1 和目标 IP 地址为 201.212.111.12 的元组,
一旦执行以下行:

uint32\_t results[4]; /\* make classify by 4 categories. \*/
rte\_acl\_classify(acx, data, results, 1, 4);

the results[] array contains:

results[4] = {0, 3, 0, 0};

• 对于类别 1,只有规则 3 匹配。
• 对于类别 0、2 和 3,没有匹配项。

24.1 设计目标

DPDK 数据包框架的主要设计目标包括:
• 提供标准方法来构建复杂的数据包处理流水线。为常用的流水线功能模块提供可重用和可扩展的模板;
• 允许在同一个流水线功能模块中切换纯软件和硬件加速的实现方式;
• 在灵活性和性能之间寻求最佳平衡。硬编码的流水线通常提供最佳性能,但不灵活,而开发灵活的框架通常不成问题,但性能通常较低;
• 提供一个在逻辑上类似于 Open Flow 的框架。

24.2 概述

数据包处理应用通常被构建为多个阶段的流水线,每个阶段的逻辑都围绕着查找表进行。对于每个传入的数据包,该表定义了要应用于数据包的一组操作,以及发送数据包到下一个阶段的逻辑。

DPDK 数据包框架通过定义流水线开发的标准方法以及提供可重用模板库,最大限度地减少了构建数据包处理流水线所需的开发工作量。

流水线通过将一组输入端口与一组输出端口通过一组表连接在树状拓扑中构建。作为当前数据包在当前表中查找操作的结果,表的一个条目(查找命中时)或默认表条目(查找未命中时)提供了应用于当前数据包的一组操作,以及数据包的下一跳,可以是另一个表、一个输出端口或数据包丢弃。

数据包处理流水线的示例如图 24.1 所示:
在这里插入图片描述图 24.1:连接输入端口 0 和 1 的数据包处理管道示例
具有输出端口 0、1 和 2 至表 0 和 1

24.3 端口库设计
24.3.1 端口类型

表 19 是可以使用数据包框架实现的端口的非详尽列表。
表 24.1:端口类型

Port类型描述
1SW ring:用于在应用程序线程之间进行消息传递的SW环形缓冲区。使用DPDK rte_ring 原语。预计是最常用的端口类型。
2HW ring:用于与NIC、交换机或加速器端口交互的缓冲区描述符队列。对于NIC端口,使用DPDK rte_eth_rx_queue 或 rte_eth_tx_queue 原语。
3IP重组:输入数据包可以是IP片段或完整的IP数据报。输出数据包是完整的IP数据报。
4IP分段:输入数据包是巨大的(长度大于MTU的IP数据报)或非巨大的数据包。输出数据包是非巨大的数据包。
5流量管理器:连接到特定NIC输出端口的流量管理器,根据预定义的SLA执行拥塞管理和分层调度。
6KNI:发送/接收数据包到/从Linux内核空间。
7Source:作为数据包生成器使用的输入端口。类似于Linux内核的 /dev/zero 字符设备。
8Sink:用于丢弃所有输入数据包的输出端口。类似于Linux内核的 /dev/null 字符设备。

24.3.2 端口接口

每个端口是单向的,即输入端口或输出端口。每个输入/输出端口都需要实现一个抽象接口,该接口定义了端口的初始化和运行时操作。端口的抽象接口描述如下。
表 24.2: 20 端口抽象接口

#Port OperationDescription
1Create创建低级端口对象(例如队列)。可以在内部分配内存。
2Free释放低级端口对象使用的资源(例如内存)。
3RX读取一组输入数据包。非阻塞操作。仅适用于输入端口。
4TX写入一组输入数据包。非阻塞操作。仅适用于输出端口。
5Flush刷新输出缓冲区。仅适用于输出端口。

24.4 表格库设计

24.4.1 表格类型
表 21 是一个非详尽列举的可使用 Packet Framework 实现的表格类型列表。
表 24.3: 表格类型

表格类型描述
Hash表 Hash table键查找是基于n元组的。通常,查找键被散列以产生一个签名,该签名用于识别下一个进行查找的条目存储桶。与每个输入数据包的查找键相关联的签名要么从数据包描述符(预先计算的签名)中读取,要么在表查找时计算。表查找、添加条目和删除条目操作,以及预先计算签名的任何其他流水线块都必须使用相同的散列算法来生成签名。通常用于实现流分类表、ARP缓存、隧道协议的路由表等。
最长前缀匹配(LPM)Longest Prefix Match键查找是IP地址。每个表条目都有一个关联的IP前缀(IP和深度)。表查找操作选择与查找键匹配的IP前缀;在多次匹配的情况下,具有最长前缀深度的条目获胜。通常用于实现IP路由表。
访问控制列表(ACL)Access Control List键查找是两个VLAN/MPLS标签、IP目标地址、IP源地址、L4协议、L4目标端口、L4源端口的7元组。每个表条目都有一个关联的ACL和优先级。ACL包含VLAN/MPLS标签的位掩码,IP目标地址的IP前缀,IP源地址的IP前缀,L4协议和位掩码,L4目标端口和位掩码,L4源端口和位掩码。表查找操作选择与查找键匹配的ACL;在多次匹配的情况下,具有最高优先级的条目获胜。通常用于实现防火墙等的规则数据库。
模式匹配搜索 Pattern matching search键查找是数据包有效负载。表是具有分配的每个模式的优先级的模式数据库。表查找操作选择与输入数据包匹配的模式;在多次匹配的情况下,具有最高优先级的匹配模式获胜。
数组 Array键查找是表条目索引本身。

24.4.2 表格接口
每个表格都需要实现一个抽象接口,该接口定义了表格的初始化和运行时操作。表格的抽象接口描述在表 29 中。
表 24.4: 表格抽象接口

操作描述
Create创建查找表的低级数据结构。可以在内部分配内存。
Free释放查找表使用的所有资源。
Add entry向查找表添加新条目。
Delete entry从查找表中删除特定条目。
Lookup查找一组输入数据包,并返回指定每个数据包查找操作结果的位掩码:设置位表示相应数据包的查找命中,清除位表示查找未命中。对于每个查找命中的数据包,查找操作还会返回指向被命中的表条目的指针,其中包含要应用于数据包的操作和任何相关元数据。对于每个查找未命中的数据包,要应用于数据包的操作和任何相关元数据由预先配置的用于查找未命中的默认表条目指定。

24.4.3 哈希表设计

哈希表概述

哈希表很重要,因为键查找操作被优化为速度:不需要通过表中的所有键进行线性搜索查找键,而是仅限于在单个表桶中存储的键。

关联数组

关联数组是一个可以指定为一组(键、值)对的函数,每个键最多出现一次。对于给定的关联数组,可能的操作有:

  1. 添加(键,值):当键当前未关联任何值时,创建(键,值)关联。当键已与值 value0 关联时,删除(键,value0)关联并创建(键,value)关联;
  2. 删除键:当键当前未关联任何值时,此操作不起作用。当键已与值关联时,删除(键,值)关联;
  3. 查找键:当键当前未关联任何值时,此操作返回空值(查找未命中)。当键与值关联时,此操作返回值。不更改(键,值)关联。
    用于比较输入键与关联数组中的键的匹配标准是精确匹配,因为键的大小(字节数)和键值(字节数组)必须与比较中的两个键完全匹配。
哈希函数

哈希函数将可变长度(键)的数据确定性地映射到固定大小的数据(哈希值或键签名)。通常,键的大小大于键签名的大小。哈希函数基本上将长键压缩为短签名。多个键可以共享相同的签名(碰撞)。
高质量的哈希函数具有均匀的分布。对于大量的键,将签名值空间划分为固定数量的相等间隔(桶)时,希望键签名均匀分布在这些间隔之间(均匀分布),而不是大多数签名只进入少数间隔,其余间隔基本上未使用(非均匀分布)。

哈希表

哈希表是使用哈希函数进行操作的关联数组。使用哈希函数的原因是通过最小化与输入键进行比较的表键数量来优化查找操作的性能。
哈希表不是将(键,值)对存储在单个列表中,而是维护多个列表(桶)。对于任何给定的键,存在一个单独的桶,其中该键可能存在,并且该桶基于键签名是唯一标识的。一旦计算了键签名并标识了哈希表桶,要么键位于此桶中,要么根本不在哈希表中,因此键搜索可以从当前表中所有键的完整集合缩小为仅位于识别的表桶中的键集合。
只要表键均匀分布在哈希表桶中,哈希表查找操作的性能就会大大提高,这可以通过使用具有均匀分布的哈希函数来实现。将键映射到其桶的规则可以简单地使用键签名(对表桶数量取模)作为表桶ID:
bucket_id = f_hash(key) % n_buckets;
通过选择桶的数量为2的幂,取模运算符可以被位与逻辑运算符取代:
bucket_id = f_hash(key) & (n_buckets - 1);
考虑到 n_bits 为 bucket_mask = n_buckets - 1 中设置的位数,这意味着所有最终位于相同哈希表桶中的键都具有其签名的较低 n_bits 相同。为了减少相同桶中键的数量(碰撞),需要增加哈希表桶的数量。
在数据包处理上下文中,哈希表操作涉及的操作序列在图 24.2 中描述:
在这里插入图片描述

图 24.2:数据包处理上下文中哈希表操作的步骤序列

哈希表用例
流分类

描述:对每个输入数据包至少执行一次流分类。此操作将每个传入数据包映射到流数据库中的已知流量流之一,流数据库通常包含数百万个流。
哈希表名称:流分类表
键数:数百万
键格式:唯一标识流量流/连接的数据包字段的 n 元组。例如:DiffServ 的 5 元组(源 IP 地址、目标 IP 地址、L4 协议、L4 协议源端口、L4 协议目标端口)。对于 IPv4 协议和 TCP、UDP 或 SCTP 等 L4 协议,DiffServ 5 元组的大小为 13 字节,而对于 IPv6 则为 37 字节。
键值(键数据):描述当前流量流数据包要应用的处理的操作和操作元数据。与每个流量流相关的数据大小可从 8 字节到千字节不等。

地址解析协议(ARP)

描述:一旦为 IP 数据包确定了路由(因此知道了输出接口和下一跳站点的 IP 地址),则需要下一跳站点的 MAC 地址,以便将该数据包发送到其目的地的旅程的下一段(由其目标 IP 地址标识)。下一跳站点的 MAC 地址成为出站以太网帧的目的地 MAC 地址。

总结

三套“算法宝典”

28天读完349页,这份阿里面试通关手册,助我闯进字节跳动

算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

描述:对每个输入数据包至少执行一次流分类。此操作将每个传入数据包映射到流数据库中的已知流量流之一,流数据库通常包含数百万个流。
哈希表名称:流分类表
键数:数百万
键格式:唯一标识流量流/连接的数据包字段的 n 元组。例如:DiffServ 的 5 元组(源 IP 地址、目标 IP 地址、L4 协议、L4 协议源端口、L4 协议目标端口)。对于 IPv4 协议和 TCP、UDP 或 SCTP 等 L4 协议,DiffServ 5 元组的大小为 13 字节,而对于 IPv6 则为 37 字节。
键值(键数据):描述当前流量流数据包要应用的处理的操作和操作元数据。与每个流量流相关的数据大小可从 8 字节到千字节不等。

地址解析协议(ARP)

描述:一旦为 IP 数据包确定了路由(因此知道了输出接口和下一跳站点的 IP 地址),则需要下一跳站点的 MAC 地址,以便将该数据包发送到其目的地的旅程的下一段(由其目标 IP 地址标识)。下一跳站点的 MAC 地址成为出站以太网帧的目的地 MAC 地址。

总结

三套“算法宝典”

28天读完349页,这份阿里面试通关手册,助我闯进字节跳动

算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-i4p9lqLj-1713227107509)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《前端程序员面试笔试宝典PDF》是一本帮助前端程序员备战面试和笔试的电子书籍。该书内容包含了面试、常见笔试题以及解析和答案。这本宝典对于想要进入前端开发行业或者进阶的人来说,是一本非常实用的参考资料。 首先,该宝典提供了大量的面试,从基础的HTML、CSS和JavaScript知识到高级的前端框架和工具,涵盖了前端开发技术的各个方面。这些面试旨在考察面试者对于前端技术的理解和应用能力,帮助面试者系统性地学习和掌握前端相关知识。 其次,宝典还包含了常见的笔试题及其解析和答案。笔试题的设计更注重考察面试者的编程能力和解决问题的能力。通过详细的解析和答案,读者可了解题的解题思路和方法,提高自己的解题效率和技巧。 此外,宝典还提供了面试过程中需要注意的事项和策略,如面试前的准备、面试时的表现技巧,以及常见的面试问题和回答技巧等。这些内容帮助读者在面试过程中更加自信和从容,提高自己的竞争力。 总之,《前端程序员面试笔试宝典PDF》是一本帮助前端程序员备战面试和笔试的实用电子书籍。它提供了丰富的面试和笔试题,配备详细的解析和答案,在帮助读者学习和巩固前端知识的同时,提高了面试和笔试的准备水平。无论是入门初学者还是有一定经验的前端开发者,都可以从该宝典中获得帮助,提升自己的职业素养。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值