线程之间报文调度,首先需要初始化一个报文队列,如下调用函数vlib_frame_queue_main_init,返回值为新创建队列的索引值。首个参数为队列报文的接收节点索引,这里为:nat44-ed-in2out,第二个参数指定队列的大小。

int
nat44_plugin_enable (nat44_config_t c)
{if (!sm->frame_queue_nelts){sm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT;}if (sm->num_workers > 1){vlib_main_t *vm = vlib_get_main ();vlib_node_t *node;if (sm->fq_in2out_index == ~0){node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out");sm->fq_in2out_index =vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);}

函数vlib_buffer_enqueue_to_thread负责将报文送到指定的线程队列中。如果drop_on_congestion为真,当队列满时,将丢弃报文。函数返回值为成功进入队列的报文数量。

static_always_inline u32
vlib_buffer_enqueue_to_thread (vlib_main_t *vm, vlib_node_runtime_t *node,u32 frame_queue_index, u32 *buffer_indices,u16 *thread_indices, u32 n_packets,int drop_on_congestion)
{   vlib_buffer_enqueue_to_thread_fn_t *fn;fn = vlib_buffer_func_main.buffer_enqueue_to_thread_fn;return (fn) (vm, node, frame_queue_index, buffer_indices, thread_indices,n_packets, drop_on_congestion);
}

报文队列初始化函数如下,如果不指定队列的长度,默认长度为64(FRAME_QUEUE_MAX_NELTS)。初始化新分配的vlib_frame_queue_main_t结构。

u32
vlib_frame_queue_main_init (u32 node_index, u32 frame_queue_nelts)
{vlib_thread_main_t *tm = vlib_get_thread_main ();vlib_frame_queue_main_t *fqm;vlib_frame_queue_t *fq;vlib_node_t *node;if (frame_queue_nelts == 0)frame_queue_nelts = FRAME_QUEUE_MAX_NELTS;num_threads = 1 /* main thread */  + tm->n_threads;ASSERT (frame_queue_nelts >= 8 + num_threads);vec_add2 (tm->frame_queue_mains, fqm, 1);node = vlib_get_node (vm, fqm->node_index);ASSERT (node);if (node->aux_offset){ fqm->frame_queue_dequeue_fn =CLIB_MARCH_FN_VOID_POINTER (vlib_frame_queue_dequeue_with_aux_fn);}else{fqm->frame_queue_dequeue_fn =CLIB_MARCH_FN_VOID_POINTER (vlib_frame_queue_dequeue_fn);}fqm->node_index = node_index;fqm->frame_queue_nelts = frame_queue_nelts;

初始化和worker线程数量相同的队列结构(vlib_frame_queue_t),每个队列的大小为(frame_queue_nelts)。

  vec_validate (fqm->vlib_frame_queues, tm->n_vlib_mains - 1);vec_set_len (fqm->vlib_frame_queues, 0);for (i = 0; i < tm->n_vlib_mains; i++){fq = vlib_frame_queue_alloc (frame_queue_nelts);vec_add1 (fqm->vlib_frame_queues, fq);}return (fqm - tm->frame_queue_mains);
}

首先,根据报文队列索引,得到队列主结构vlib_frame_queue_main_t。如果报文数量大于节点node可接收到报文数量(VLIB_FRAME_SIZE),每次向队列中发送VLIB_FRAME_SIZE大小的报文数量。

u32 __clib_section (".vlib_buffer_enqueue_to_thread_fn")
CLIB_MULTIARCH_FN (vlib_buffer_enqueue_to_thread_fn)
(vlib_main_t *vm, vlib_node_runtime_t *node, u32 frame_queue_index,u32 *buffer_indices, u16 *thread_indices, u32 n_packets,int drop_on_congestion)
{vlib_thread_main_t *tm = vlib_get_thread_main ();vlib_frame_queue_main_t *fqm;u32 n_enq = 0;fqm = vec_elt_at_index (tm->frame_queue_mains, frame_queue_index);while (n_packets >= VLIB_FRAME_SIZE){n_enq += vlib_buffer_enqueue_to_thread_inline (vm, node, fqm, buffer_indices, thread_indices, VLIB_FRAME_SIZE,drop_on_congestion, 0 /* with_aux */, NULL);buffer_indices += VLIB_FRAME_SIZE;thread_indices += VLIB_FRAME_SIZE;n_packets -= VLIB_FRAME_SIZE;}if (n_packets == 0)return n_enq;n_enq += vlib_buffer_enqueue_to_thread_inline (vm, node, fqm, buffer_indices, thread_indices, n_packets,drop_on_congestion, 0 /* with_aux */, NULL);return n_enq;

首先,由第一个报文对应的线程索引(thread_indeces[0])开始,函数clib_mask_compare_u16找出参数线程数组thread_indices中有多少和第一个thread_index相同的值,以掩码的形式保存到mask中。表明对应位置的报文需要送到相同的线程处理。

其次,函数vlib_get_frame_queue_elt根据线程索引值,获得线程所对应的可用的结构vlib_frame_queue_elt_t,如果没有可用队列元素,并且drop_on_congestion为真,返回0,否则,等待可用队列元素。

根据第一步获得的线程掩码mask,将参数buffer_indices中对应的缓存索引值,设置到vlib_frame_queue_elt_t结构的buffer_index数组中。函数clib_compress_u32返回值为设置的缓存索引的数量n_comp。

static_always_inline u32
vlib_buffer_enqueue_to_thread_inline (vlib_main_t *vm,vlib_node_runtime_t *node,vlib_frame_queue_main_t *fqm,u32 *buffer_indices, u16 *thread_indices,u32 n_packets, int drop_on_congestion,int with_aux, u32 *aux_data)
{u32 drop_list[VLIB_FRAME_SIZE], n_drop = 0;vlib_frame_bitmap_t mask, used_elts = {};vlib_frame_queue_elt_t *hf = 0;u16 thread_index;u32 n_comp, off = 0, n_left = n_packets;thread_index = thread_indices[0];more:clib_mask_compare_u16 (thread_index, thread_indices, mask, n_packets);hf = vlib_get_frame_queue_elt (fqm, thread_index, drop_on_congestion);n_comp = clib_compress_u32 (hf ? hf->buffer_index : drop_list + n_drop,buffer_indices, mask, n_packets);if (with_aux)clib_compress_u32 (hf ? hf->aux_data : drop_list + n_drop, aux_data, mask,n_packets);

如果hf有值,将添加的缓存数量n_comp赋值给n_vectors。并且,将线程索引对应的vlib_main_t结构成员check_frame_queues设置1,表明队列有报文,需处理。否则,如果hf为空,表明n_comp数量的报文被丢弃。

  if (hf){if (node->flags & VLIB_NODE_FLAG_TRACE)hf->maybe_trace = 1;hf->n_vectors = n_comp;__atomic_store_n (&hf->valid, 1, __ATOMIC_RELEASE);vlib_get_main_by_index (thread_index)->check_frame_queues = 1;}elsen_drop += n_comp;

n_left表明缓存数组中剩余的数量。将处理过的掩码mask或到used_elts中,如果used_elts数组的当前(off初始为0)值为全F(64位都是1),表明都处理完成,将off递加。

计算下一个未处理的线程索引thread_index。

  n_left -= n_comp;if (n_left){vlib_frame_bitmap_or (used_elts, mask);while (PREDICT_FALSE (used_elts[off] == ~0)){off++;ASSERT (off < ARRAY_LEN (used_elts));}thread_index =thread_indices[off * 64 + count_trailing_zeros (~used_elts[off])];goto more;}if (drop_on_congestion && n_drop)vlib_buffer_free (vm, drop_list, n_drop);return n_packets - n_drop;

VPP线程之间报文调度相关推荐

  1. python线程的注意点(线程之间执行是无序的、主线程会等待所有的子线程执行结束再结束(守护主线程)、线程之间共享全局变量、线程之间共享全局变量数据出现错误问题(线程等待(join)、互斥锁))

    1. 线程的注意点介绍 线程之间执行是无序的 主线程会等待所有的子线程执行结束再结束 线程之间共享全局变量 线程之间共享全局变量数据出现错误问题 2. 线程之间执行是无序的 import thread ...

  2. rtems线程管理与调度(一)

    rtemsahi一个以线程为基本调度单位的实施操作系统,调度算法是基于优先级的抢占式线程调度,支持256个线程优先级,0代表最高优先级,主要用于内部线程,255是最低线程,是空闲线程的优先级,用户线程 ...

  3. 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...

    线程(from threading import Thread):CPU调度的最小单位 线程的两种创建方式:方式一: 1 from threading import Thread 2 def f1(i ...

  4. 程序、进程、线程之间的区别

    1.   .net的公共语言运行时(CLR)能够区别两种不同类型的线程:前台线程和后台线程.前台线程与后台线程的区别: 应用程序必须运行完所有的前台线程才可以退出.而对于后台线程,应用程序则可以不考虑 ...

  5. 管程,进程及线程之间的区别

    1,首先我们先了解进程.线程.管程各自的概念: 进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动.它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基 ...

  6. 线程的状态转换、sleep()、wait()、yeild()、终止线程的方法、线程之间的协作(join()、wait() notify() notifyAll()、await() signal() )

    1.线程的状态转换 1.1 新建(New) 创建后尚未启动 1.2 可运行(Runnable) 可能正在运行,也可能正在等待 CPU 时间片. 包含了操作系统线程状态中的 Running 和 Read ...

  7. Python多任务(8.进程与线程之间的区别以及多进程和多线程的区别 )

    1. 进程.线程的对比 进程,能够完成多任务,比如:  一台电脑上可以运行登录多个QQ 线程,能够完成多任务,比如: 一个QQ中可以和有多个聊天窗口 进程里面包含线程,线程不能够独立执行,必须依存在进 ...

  8. Java多线程系列(二):线程的五大状态,以及线程之间的通信与协作

    在Java面试的时候,经常会问到Java并发编程相关的多线程.线程池.线程锁.线程通信等面试必考点,比如: Java并发编程系列:Java线程池的使用方式,核心运行原理.以及注意事项 Java并发编程 ...

  9. 线程知识点(一)—— 程序、进程、线程之间的区别与联系、Java的线程状态和生命周期

    1 程序.进程.线程之间的区别与联系 三者之间的形象化理解: * 程序:代码实现了功能,就是程序,是静态的: * 进程:执行中的程序就是进程,是动态的: * 线程:进程内的一个执行单元,也是进程内的可 ...

最新文章

  1. TreaponseHeader
  2. mysql排他锁和共享锁视频_分享MySQL 中的共享锁和排他锁的用法
  3. android导航条高度修改,Android中修改TabLayout底部导航条Indicator长短的方法
  4. Machine Learning - Andrew Ng on Coursera (Week 3)
  5. 【无标题】RestHighLevelClient工具类
  6. Boost:bind绑定作为一个组合的测试程序
  7. nokia x7 android 9.0,诺基亚X7升级Android 9.0系统
  8. 数据结构与算法专题——第十题 输入法跳不过的坎-伸展树
  9. mysql 执行计划详解,Mysql中的explain执行计划详解(1)
  10. Element-UI-的布局和容器---Element-UI工作笔记003
  11. 海思uboot启动流程详细分析(二)
  12. 二叉树的层序遍历 二叉树
  13. android textview显示表情,Android开发-TextView中显示QQ表情类的图片和超链接
  14. 关于美食html网页设计完整版,10个以美食为主题的网页设计案例
  15. 虎头少保,天下第一手孙禄堂【转】
  16. hd620显卡驱动 linux,英特尔为Windows 10 推出新显卡驱动 26.20.100.7870
  17. 迭代法动态生成谢尔宾斯基三角形
  18. 基于VS + Qt编程的UG/NX二次开发
  19. A卡福利 : AMD Fluid Motion Video补帧教程,让你的视频从24帧补到60帧(144)
  20. RPM和YUM软件仓库的区别

热门文章

  1. 微信公众号文章爬取方法整理
  2. 微信公众号文章采集 爬取微信文章 采集公众号的阅读数和点赞数?
  3. 开奶茶店,哪里学奶茶的制作配方?
  4. CSR867x一拖多加密工具8670 8675
  5. xstream 对象 -》xml
  6. 拼图游戏(8 puzzle)
  7. JQuery快速入门之插件
  8. echart柱状图堆叠总计显示
  9. 用户使用移动支付的风险与防范策略
  10. 如何在供应链金融中防范风险?