4. VPP源码分析(graph node(2))

简介: 2.2.2. VLIB_NODE_TYPE_PROCESS结点 每个process结点是由jump机制构成的一个协程,协程主要用于等待、处理事件。使用longjmp/setjmp的轻量级多任务协程,由应用进程自行进行调度,不受操作系统调度机制的影响,上下文切换只损耗调用longjmp/setjmp的时间。

2.2.2. VLIB_NODE_TYPE_PROCESS结点

每个process结点是由jump机制构成的一个协程,协程主要用于等待、处理事件。
使用longjmp/setjmp的轻量级多任务协程,由应用进程自行进行调度,不受操作系统调度机制的影响,上下文切换只损耗调用longjmp/setjmp的时间。
协程中运行的函数类似于线程函数,区别在于协程函数Can be suspended, wait for events, be resumed… (based on setjump/longjump).

  • Wait for an event
    always_inline f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
  • Send an event
    always_inline void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)

2.2.2.1. vlib_process_t结构体

1
2
协程间的切换最重要的是要保护CPU寄存器和私有栈。
这里clib_longjmp_t中保存上下文切换时CPU寄存器的信息,而相应的栈信息保存在process结构体中stack后续的内存空间中。

2.2.2.2. process node的初始化

在vlib_main_or_worker_loop开始包处理循环之前,main线程会对所有的process结点都调度一次,执行相应的node函数,类似于初始化操作。
3
在dispatch_process函数中讲调用vlib_process_startup函数,处理所有process结点的初始化工作。
vlib_process_startup先记录每个process结点正常返回时的jmp信息(return_longjmp),之后跳到vlib_process_bootstrap函数中执行process结点函数。
4

2.2.2.3. process node函数

以下为某个典型的process结点函数:
5
该函数为while (1)的形式,说明该node函数将一直执行直到应用进程退出为止。
而在开始的时候这里先调用了vlib_process_wait_for_event函数,检查是否由事件需要处理。

该函数先去检查non_empty_event_type_bitmap是否置位,如有说明有需要处理的事件则立刻返回进行处理,否则说明该process node任然需要等待事件,暂不需要分配CPU时间,可以进入suspend状态。
所以,这里先记录下当前的位置(记为resume_longjmp),然后再跳回return_longjmp所记录的位置,完成一次结点调度过程。
这样,初始化时调用的dispatch_process可以先初始化所有的process node,而各process node暂时处于suspend状态,等待某个条件的到来进入resume状态并执行业务逻辑,执行完后退出释放CPU资源。
6

2.2.3. VLIB_NODE_TYPE_INTERNAL结点

internal结点主要用于处理数据包业务逻辑,结点通过函数流程上与input结点大致相同。
static uword sample_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)

2.2.3.1. 从本node frame中取包

vlib_frame_t *frame    -> 由node函数入口传入 
U32 *from              -> 当前frame中vector的起始数据
from = vlib_frame_vector_args(frame); 

调用vlib_frame_vector_args函数返回本node vlib_frame_t中数据包的起始位置,对包进行处理使用from[0],from[1]…

2.2.3.2. to_next指针的获得

vlib_main_t *vm  = vlib_get_main ();
u32 next_index   = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
vlib_node_runtime_t *node;
u32 n_left_to_next;
u32 *to_next;
vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);

调用vlib_get_next_frame函数,找到下一node对应的vlib_frame_t中合适的存放数据包的起始位置并保存到to_next
如果要发送数据包,则可以修改to_next[0],to_next[1]……中的数据包索引值

2.2.3.3. 验证处理结果

vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
next_index   -> 默认的下一结点的index
next0        -> 实际的下一个结点的index 

当next0 != next_index时,说明该包被正确处理,该宏将do nothing
否则,说明本来该包应去next_index但是经过包处理需要去往其他结点
使得next0 != next_index,该宏会将该包索引pi0发往到next0实际的下一个结点

2.2.3.4. 登记调度

vlib_put_next_frame(vm, node, next_index, n_left_to_next);
所有流程都正确处理完毕后,下一结点的frame上已经有本结点处理过后的数据索引
执行该函数,将相关信息登记到vlib_pending_frame_t中,准备开始调度处理

2.2.3.5. 具体实例

1

目录
相关文章
|
6月前
|
JavaScript
Turndown 源码分析:五、节点相关`root-node.js`和`node.js`
Turndown 源码分析:五、节点相关`root-node.js`和`node.js`
72 0
|
JSON 缓存 JavaScript
Node.js躬行记(20)——KOA源码分析(下)
Node.js躬行记(20)——KOA源码分析(下)
|
缓存 JSON JavaScript
Node.js躬行记(19)——KOA源码分析(上)
Node.js躬行记(19)——KOA源码分析(上)
|
网络性能优化 索引 调度
5. VPP源码分析(graph node(3))
2.3. vlib_frame_t 从arguments地址开始的内存空间是vector空间,作为结点接收数据包索引的缓冲区。 2.3.1. vlib_frame_t内存空间 每个线程都会从堆中分配(mmap)一块内存clib_per_cpu_mheapsvm->heap_base = clib_.
6855 0
|
网络性能优化 调度 索引
3. VPP源码分析(graph node(1))
2.1. 与结点相关的结构体 2.1.1. 全局结构体 vlib_main_t:每个线程一份,记录着线程使用到的全局数据信息.比如: /* Node graph main structure.
9730 1
|
网络协议 Unix
源码分析Node的Cluster模块
### 从源码分析Node的Cluster模块 前段时间,公司的洋彬哥老哥遇到一个问题,大概就是本机有个node的http服务器,但是每次请求这个服务器的端口返回的数据都报错,一看返回的数据根本不是http的报文格式,然后经过一番排查发现是另外一个服务器同时监听了http服务器的这个端口。这个时候洋彬老哥就很奇怪,为啥我这个端口明明使用了,却还是可以启动呢?这个时候我根据以前看libuv源码
4713 0
|
关系型数据库 API NoSQL
|
2月前
|
JavaScript
NodeJs的安装
文章介绍了Node.js的安装步骤和如何创建第一个Node.js应用。包括从官网下载安装包、安装过程、验证安装是否成功,以及使用Node.js监听端口构建简单服务器的示例代码。
NodeJs的安装