1. 摘要
本文档旨在指导新的ODP应用程序开发人员。 有关ODP的更多详细信息,请参见 ODP 主页。
Overview of a system running ODP applications
ODP是一份API规范,为高性能网络应用程序的实现提供平台独立性、自动硬件加速和CPU扩展。 本文档介绍如何充分利用API的优势来编写应用程序。
2. 简介
3. ODP API 规范
ODP由三个各自独立又相关联的组件组成。首先,ODP是一个抽象的API规范,用于描述数据面应用程序的功能模型。 该规范涵盖了许多常见数据面应用程序的编程需求,如接收、操作、传输数据包,但是并不要求这些功能具体如何实现。 这是非常有意义的,正因为ODP API没有一个优选实施例,它们允许在支持ODP实现的各种平台上运行ODP,并进行各种创新开发。 为了实现这一目标,ODP API使用了抽象数据类型描述,其定义留给ODP的实现者。 例如,ODP数据包由类型为 odp_packet_t
的抽象句柄引用,与数据包相关的API会引用此类型的参数。 这就是为什么 odp_packet_t
不是ODP API的一部分,这是实现者的责任。
ODP API 特性
- 开源,开放贡献,BSD-3许可
- 供应商与平台中立
- 以应用为中心,涵盖数据面应用的功能需求
- 通过指定ODP的功能行为确保可移植性
- 由应用程序作者和平台实施者共同公开界定
- 架构可以有效在各种平台上实现
- 由Linaro集团赞助、管理和维护
参考实现
为了轻松开始在新平台上实现ODP,ODP提供了许多实现方案,作为参考。 其中两个主要的实现是 odp-linux
和 odp-dpdk
。
odp-linux
odp-linux 实现是仅依赖于Linux系统API的纯SW实现。作为ODP的功能模型,它能方便的将ODP引导到任何支持Linux内核的平台上。
odp-dpdk
odp-dpdk 实现是使用DPDK作为SW加速器的纯SW实现。特别地,odp-dpdk为使用NIC的系统提供卓越的IO性能,这允许ODP应用程序充分利用 DPDK支持的各种NIC设备驱动程序。
https://odp.readthedocs.io/en/latest/user_guide/index.html
4. ODP 应用程序和报文流
ODP应用程序的顶层视图如下所示:
数据包到达并从由PktIO抽象表示的网络接口接收(RX)。 然后,它们直接转到由ODP线程轮询的队列,或者可以通过分类器Classification和排序,进入到代表各个流的队列。 接下来就可以通过调度器Scheduler将这些队列分派到应用程序线程。
线程,术语描述为可以调用各种ODP API来处理数据包内容。 对于输出处理,报文可以通过直接排队到PKTIO输出队列,或者可以在TX之前将其交给TM进行QoS处理。 注意,输出接口可以在loopback模式下工作,在这种情况下,发送给他们的数据包被重新回环到输入端进行“第二次”处理。 例如,传入的IPSec报文在解密之前无法正确分类,因此必须回环回来进行第二次处理。一旦解密,其实际内容可见,则可以将其分配到对应flow上。
需要注意的是,上述图表中唯一需要操作的是黄色部分。 这里显示的其他内容都由ODP提供,可供任何ODP应用程序使用。 这代表了数据面应用程序的“机械”,并且被构造为允许写入ODP API的应用程序可以在提供ODP实现的每个平台上进行移植和优化,而无需额外的工作。
ODP API 三个主要组件介绍
线程thread是ODP中的基本编程单元。ODP应用程序被组织成执行设计工作的线程集合。 ODP线程可能或者可能不会与其他线程共享内存,这取决于具体实现。 线程有两种类型:控制线程和工作线程,他们由抽象类型 odp_thread_type_t 表示。控制线程是组织工作线程工作的监督线程。 而工作线程则负责执行应用程序的主要逻辑,它采用的是RTC模型。 特别的是,工作线程一般运行于专用的处理核心上,特别是在多核心的处理环境中。 但是,如果需要,给定的实现可以在单核上运行(通常在较小和较低性能目标环境上)。
除了线程类型,线程还具有关联属性,例如,线程掩码和调度程序组,确定他们可以在哪里运行,以及他们可以处理的工作类型。// LteFcpEo, EcpriRtEo
事件event是线程执行工作的过程。事件可以表示新的工作,如需要处理的数据包到达,或者他们可以表示异步执行的请求完成。 事件还可以表示通知时间,或应用程序感兴趣的各个组件状态的更改。 事件有一个描述代表它的事件类型。 线程可以创建新事件,或消耗由他们处理的事件,或者可以对事件执行进一步处理,然后将事件传递给另一个组件以进行其他处理。 对事件的引用是通过抽象类 odp_event_t 句柄实现的。 提供了专用函数将他们转换成由事件表示的适当类型的特定句柄。
队列queue是保存事件的消息传递通道。 事件可以通过入队操作添加到队列中,或者通过出队操作从队列中删除。 队列的端点将根据使用方式而有所不同。 队列有两种主要类型:轮询和调度,这将在引入事件模型时更详细的讨论。 队列也可能具有关联的上下文,这表示所有使用它的事件的持久状态。 这些状态是允许线程对事件进行有状态处理以及无状态处理。
队列类型
除了在多核心环境中为ODP应用提供自动可扩展性的调度功能之外,调度程序的另一个主要功能是提供事件同步服务,大大简化并行处理环境中的应用程序编程。 队列的SYNC模式决定调度程序如何处理来自同一队列的多个事件的同步处理。 ODP支持三种类型的队列调度器同步区:并行,原子和有序。
5 队列
5.1 并行队列
指定 ODP_SCHED_SYNC_NONE
模式的SCHED队列在处理事件的方式上是不受限制的。
Parallel Queue Scheduling
在并行队列中保存的所有事件都有资格同时进行调度,并且它们之间的所有必需的同步都是应用程序负责的。 源自并行队列的事件也因此具有最高的吞吐率,但是它们也可能涉及大量的应用程序工作。 在上图中,四个线程正在调用 odp_schedule()
来获取要处理的事件。 调度程序已经将来自第一个队列的三个事件并行分配给三个线程。 第四个线程正在处理来自第三个队列的单个事件。 第二个队列可能是空的,优先级较低,或者不是在与调度程序服务的任何线程匹配的调度程序组中。
5.2 原子队列
原子队列简化事件同步,因为每一次只有一个线程可以处理给定原子队列中的事件。 由于锁定是由调度程序隐式完成的,因此原子队列调度的事件可以被锁定。 请注意,如果使用 odp_schedule_multi()
,调用者可能会从同一个原子队列接收一个或多个事件。 在这种情况下,这些多个事件都共享相同的原子调度上下文。
Atomic Queue Scheduling
在这个例子中,无论在一个原子队列中可能有多少个事件,每次只有一个调用线程可以接收它的调度事件。 这里两个线程处理来自两个不同原子队列的事件。 请注意,不同的原子队列之间不存在来自同一原子队列的事件之间的同步。 与原子队列相关联的队列上下文将持续到下次调用调度程序或直到应用程序通过调用 odp_schedule_release_atomic()
显式释放它。 请注意,虽然原子队列简化了编程,但原子队列的串行性质可能会削弱扩展性。
5.3 有序队列
有序队列同时提供了并行队列的可扩展性和原子队列的易同步性。
Ordered Queue Scheduling
当从有序队列调度事件时,调度程序从队列中并行分配多个事件到不同的线程, 并且调度程序还可以确保输出队列上的这些事件的相对顺序与其起始有序队列的序列相同。
与原子队列一样,与有序队列相关联的排序保证是指源自同一队列的事件,而不是源自不同队列的事件。 因此,在该图中,三个线程分别来自第一有序队列的处理事件5,3和4。 不管这些线程如何完成处理,这些事件将以它们的输出队列的原始相对顺序显示。