当前位置: 首页 > news >正文

VPP接口INPUT节点运行数据

在设置virtio接口接收/发送队列函数的最后,更新接口的运行数据。

void virtio_vring_set_rx_queues (vlib_main_t *vm, virtio_if_t *vif)
{ ...vnet_hw_if_update_runtime_data (vnm, vif->hw_if_index);
}
void virtio_vring_set_tx_queues (vlib_main_t *vm, virtio_if_t *vif)
{...vnet_hw_if_update_runtime_data (vnm, vif->hw_if_index);

接口运行数据

更新硬件接口的运行数据,参数hw_if_index指定所要更新的硬件接口的索引值。

void
vnet_hw_if_update_runtime_data (vnet_main_t *vnm, u32 hw_if_index)
{                  vlib_main_t *vm = vlib_get_main ();vnet_hw_if_output_node_runtime_t *new_out_runtimes = 0;int something_changed_on_rx = 0;int something_changed_on_tx = 0;clib_bitmap_t *pending_int = 0;int last_int = -1;log_debug ("update node '%U' triggered by interface %v",format_vlib_node_name, vm, node_index, hi->name);

根据VPP系统的线程数量,分配d,a向量空间,并且将per_thread_node_state和per_thread_node_adaptive两个向量初始化为无效状态。其中,后边两个向量用于保存接口的input节点在每个worker线程中的新的运行模式:中断或者轮询,以及自适应adaptive(非独立模式,标志位)。

d,a两个向量分别保存每个线程中新的轮询模式队列,和自适应队列的值。

  vnet_hw_if_rxq_poll_vector_t *pv, **d = 0, **a = 0;vlib_node_state_t *per_thread_node_state = 0;u32 n_threads = vlib_get_n_threads ();u16 *per_thread_node_adaptive = 0;vec_validate (d, n_threads - 1);vec_validate (a, n_threads - 1);vec_validate_init_empty (per_thread_node_state, n_threads - 1, VLIB_NODE_STATE_DISABLED);vec_validate_init_empty (per_thread_node_adaptive, n_threads - 1, 0);

以下找到每个线程中input节点的新的运行状态。

遍历VPP接口模块中的接收队列池(hw_if_rx_queues),根据接收队列成员(hw_if_index)找到其对应的硬件接口,以下处理那些硬件接口与参数中指定硬件接口具有相同input_node_index的接收队列。

即在相同的input节点进行处理的硬件接口。

  vnet_interface_main_t *im = &vnm->interface_main;vnet_hw_if_rx_queue_t *rxq;vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);u32 node_index = hi->input_node_index;/* find out desired node state on each thread */pool_foreach (rxq, im->hw_if_rx_queues){u32 ti = rxq->thread_index;vnet_hw_interface_t *rxq_hi;ASSERT (rxq->mode != VNET_HW_IF_RX_MODE_UNKNOWN);ASSERT (rxq->mode != VNET_HW_IF_RX_MODE_DEFAULT);rxq_hi = vnet_get_hw_interface (vnm, rxq->hw_if_index);if (rxq_hi->input_node_index != node_index)continue;

根据这些接收队列的模式,初始化VPP处理线程节点的运行状态,轮询状态优先,即如果存在轮询模式的接收队列,input优先使用轮询状态:

  • 接收队列模式为轮询(RX_MODE_POLLING),线程input节点状态也设置为轮询(NODE_STATE_POLLING)。
  • 接收队列模式为中断/自适应(RX_MODE_INTERRUPT/ADAPTIVE),线程input节点状态设置为中断(NODE_STATE_INTERRUPT)。

轮询状态(NODE_STATE_POLLING)的input节点,在线程的主循环中每次都执行。中断状态的input节点,在接收到设备发送的中断请求时运行。

    if (rxq->mode == VNET_HW_IF_RX_MODE_POLLING) {per_thread_node_state[ti] = VLIB_NODE_STATE_POLLING;per_thread_node_adaptive[ti] = 0;}if (per_thread_node_state[ti] == VLIB_NODE_STATE_POLLING)continue;if (rxq->mode == VNET_HW_IF_RX_MODE_INTERRUPT || rxq->mode == VNET_HW_IF_RX_MODE_ADAPTIVE)per_thread_node_state[ti] = VLIB_NODE_STATE_INTERRUPT;if (rxq->mode == VNET_HW_IF_RX_MODE_ADAPTIVE)per_thread_node_adaptive[ti] = 1;}

以下再次遍历接收队列池,找到在相同input节点进行处理的硬件接口的接收队列。

  vnet_hw_if_rxq_poll_vector_t *pv,/* construct per-thread polling vectors */pool_foreach (rxq, im->hw_if_rx_queues){u32 ti = rxq->thread_index;vnet_hw_interface_t *rxq_hi;rxq_hi = vnet_get_hw_interface (vnm, rxq->hw_if_index);if (rxq_hi->input_node_index != node_index)continue;

对于中断或者adaptive模式的接收队列,记录在pool中的最大索引值。如果队列处理线程节点为adaptive模式,分配结构(vnet_hw_if_rxq_poll_vector_t),保存dev_instance和queue_id,分别为接口设备的实例索引和队列索引。

如果队列处理线程节点工作在轮询POLLING模式,也分配pv结构,保存接收队列信息。

    if (rxq->mode == VNET_HW_IF_RX_MODE_INTERRUPT ||rxq->mode == VNET_HW_IF_RX_MODE_ADAPTIVE)last_int = clib_max (last_int, rxq - im->hw_if_rx_queues);if (per_thread_node_adaptive[ti]) {vec_add2_aligned (a[ti], pv, 1, CLIB_CACHE_LINE_BYTES);pv->dev_instance = rxq->dev_instance;pv->queue_id = rxq->queue_id;}if (per_thread_node_state[ti] != VLIB_NODE_STATE_POLLING)continue;vec_add2_aligned (d[ti], pv, 1, CLIB_CACHE_LINE_BYTES);pv->dev_instance = rxq->dev_instance;pv->queue_id = rxq->queue_id;}

遍历VPP系统的线程,首先,对当前线程需要处理的所有pv结构,即所有设备和队列,进行排序。

  /* sort poll vectors and compare them with active ones to avoid unnecesary barrier */for (int i = 0; i < n_threads; i++){vlib_main_t *ovm = vlib_get_main_by_index (i);vlib_node_state_t old_state;vec_sort_with_function (d[i], poll_data_sort);

其次,获取硬件接口的input节点(node_index),在当前线程中的运行状态,如果与新的状态不同,设置修改标志。

    old_state = vlib_node_get_state (ovm, node_index);if (per_thread_node_state[i] != old_state) {something_changed_on_rx = 1;log_debug ("state changed for node %U on thread %u from %s to %s",format_vlib_node_name, vm, node_index, i,node_state_str[old_state],node_state_str[per_thread_node_state[i]]);}

否则,在修改标志为0的情况下,进一步进行检查。获取input节点在当前线程的运行数据rt,如果节点处理的中断模式队列数量,与以上得出的数量不同,设置修改标志。接下来通过memcmp比较中断模式队列的内容是否完全相同。

    /* check if something changed */if (something_changed_on_rx == 0) {vnet_hw_if_rx_node_runtime_t *rt;rt = vlib_node_get_runtime_data (ovm, node_index);if (vec_len (rt->rxq_vector_int) != vec_len (d[i]))something_changed_on_rx = 1;else if (memcmp (d[i], rt->rxq_vector_int, vec_len (d[i]) * sizeof (**d)))something_changed_on_rx = 1;

进一步比较接收中断的数量是否相等,以及轮询状态的向量是否完全相等。

      if (clib_interrupt_get_n_int (rt->rxq_interrupts) != last_int + 1)something_changed_on_rx = 1;if (something_changed_on_rx == 0 && per_thread_node_adaptive[i]) {if (vec_len (rt->rxq_vector_poll) != vec_len (a[i]))something_changed_on_rx = 1;else if (memcmp (a[i], rt->rxq_vector_poll, vec_len (a[i]) * sizeof (**a)))something_changed_on_rx = 1;}}}

发送数据修改判断

如果参数硬件接口的发送队列数量大于0,复制一份原有的线程节点runtimes数据,如下:

  if (vec_len (hi->tx_queue_indices) > 0){new_out_runtimes = vec_dup_aligned (hi->output_node_thread_runtimes, CLIB_CACHE_LINE_BYTES);vec_validate_aligned (new_out_runtimes, n_threads - 1, CLIB_CACHE_LINE_BYTES);

遍历每个线程中的发送节点runtime,

    for (u32 i = 0; i < vec_len (new_out_runtimes); i++){vnet_hw_if_output_node_runtime_t *rt;rt = vec_elt_at_index (new_out_runtimes, i);u32 n_queues = 0, total_queues = vec_len (hi->tx_queue_indices);rt->frame = 0;rt->lookup_table = 0;

遍历接口所有的发送队列。如果当前发送队列的处理线程向量中,不包含当前的遍历线程,不进行处理。否则,初始化frame结构,添加到线程的runtime结构成员frame向量中。

      for (u32 j = 0; j < total_queues; j++) {u32 queue_index = hi->tx_queue_indices[j];vnet_hw_if_tx_frame_t frame = { .shared_queue = 0,.hints = 7,  .queue_id = ~0 };vnet_hw_if_tx_queue_t *txq = vnet_hw_if_get_tx_queue (vnm, queue_index);if (!clib_bitmap_get (txq->threads, i))continue;log_debug ("tx queue data changed for interface %v, thread %u (queue_id %u)", hi->name, i, txq->queue_id);something_changed_on_tx = 1;frame.queue_id = txq->queue_id;frame.shared_queue = txq->shared_queue;vec_add1 (rt->frame, frame);n_queues++;}

n_queues表示当前线程需要处理的接口队列数量,如果此值发生变化,使用新值,并且标记改变(something_changed_on_tx)。

      // don't initialize rt->n_queues aboveif (rt->n_queues != n_queues) {something_changed_on_tx = 1;rt->n_queues = n_queues;}

这里要求线程处理的队列数量为2的幂,初始化lookup_table,填充队列ID值。lookup_table向量的长度可能大于队列数量,这时lookup_table中存在相同的队列ID。

      /* It is only used in case of multiple txq. */if (rt->n_queues > 0) {if (!is_pow2 (n_queues))n_queues = max_pow2 (n_queues);vec_validate_aligned (rt->lookup_table, n_queues - 1, CLIB_CACHE_LINE_BYTES);for (u32 k = 0; k < vec_len (rt->lookup_table); k++) {rt->lookup_table[k] = rt->frame[k % rt->n_queues].queue_id;log_debug ("tx queue lookup table changed for interface %v, (lookup table [%u]=%u)", hi->name, k, rt->lookup_table[k]);}}}

如果接口的队列索引向量(hi->tx_queue_indices)为空,即不存在发送队列,表明接口被删除。

  } else/* interface deleted */something_changed_on_tx = 1;

更新runtimes数据

如果接收或者发送队列改变,在处理之前锁住所有的VPP线程。

  if (something_changed_on_rx || something_changed_on_tx){int with_barrier;if (vlib_worker_thread_barrier_held ()) {with_barrier = 0;log_debug ("%s", "already running under the barrier");} elsewith_barrier = 1;if (with_barrier)vlib_worker_thread_barrier_sync (vm);

如果接收发生改变,遍历所有线程,更新线程中node节点对应的runtime数据,比如使用线程新的接收队列中断向量(d[i])替换原有的值(rxq_vector_int)。

    if (something_changed_on_rx) {for (int i = 0; i < n_threads; i++) {vlib_main_t *vm = vlib_get_main_by_index (i);vnet_hw_if_rx_node_runtime_t *rt;rt = vlib_node_get_runtime_data (vm, node_index);pv = rt->rxq_vector_int;rt->rxq_vector_int = d[i];d[i] = pv;

如果此线程需要处理adaptive模式的接收队列,使用新值(a[i])替换旧值(rxq_vector_poll)。

        if (per_thread_node_adaptive[i]) {pv = rt->rxq_vector_poll;rt->rxq_vector_poll = a[i];a[i] = pv;}

如果存在未处理的接收队列中断,将这些中断号保存到pending_int位图中,并记录下最大的中断号(last_int)。

        if (rt->rxq_interrupts) {void *in = rt->rxq_interrupts;int int_num = -1;while ((int_num = clib_interrupt_get_next (in, int_num)) != -1) {clib_interrupt_clear (in, int_num);pending_int = clib_bitmap_set (pending_int, int_num, 1);last_int = clib_max (last_int, int_num);}}

接下来设置线程中,节点(node_index)的新状态,以及标志位VLIB_NODE_FLAG_ADAPTIVE_MODE。根据最大中断号,重新设置rxq_interrupts向量的大小。

        vlib_node_set_state (vm, node_index, per_thread_node_state[i]);vlib_node_set_flag (vm, node_index, VLIB_NODE_FLAG_ADAPTIVE_MODE, per_thread_node_adaptive[i]);if (last_int >= 0)clib_interrupt_resize (&rt->rxq_interrupts, last_int + 1);elseclib_interrupt_free (&rt->rxq_interrupts);}}

如果发送发生改变,处理相对简单,不需要想接收那样遍历所有线程。如下将new_out_runtimes替换掉接口原有的output_node_thread_runtimes。最后,释放VPP的worker线程锁。

    if (something_changed_on_tx) {vnet_hw_if_output_node_runtime_t *t;t = hi->output_node_thread_runtimes;hi->output_node_thread_runtimes = new_out_runtimes;new_out_runtimes = t;}       if (with_barrier)vlib_worker_thread_barrier_release (vm);} else    log_debug ("skipping update of node '%U', no changes detected",format_vlib_node_name, vm, node_index);

如果存在之前未处理的中断,将其重新设置回新的runtime中。

  if (pending_int) {         int i;  clib_bitmap_foreach (i, pending_int) {       vnet_hw_if_rx_queue_set_int_pending (vnm, i);}clib_bitmap_free (pending_int);}     

相关文章:

VPP接口INPUT节点运行数据

在设置virtio接口接收/发送队列函数的最后&#xff0c;更新接口的运行数据。 void virtio_vring_set_rx_queues (vlib_main_t *vm, virtio_if_t *vif) { ...vnet_hw_if_update_runtime_data (vnm, vif->hw_if_index); } void virtio_vring_set_tx_queues (vlib_main_t *vm,…...

RabbitMQ学习(九):延迟队列

一、延迟队列概念延时队列中&#xff0c;队列内部是有序的&#xff0c;最重要的特性就体现在它的延时属性上&#xff0c;延时队列中的元素是希望 在指定时间到了以后或之前取出和处理。简单来说&#xff0c;延时队列就是用来存放需要在指定时间内被处理的 元素的队列。其实延迟…...

TCP并发服务器(多进程与多线程)

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 TCP并发服务器&#xff08;多进程与多线程&#xff09;1. 多进程并发服务器&#xff08;1&#xff09;…...

第1章 Memcached 教程

Memcached是一个自由开源的&#xff0c;高性能&#xff0c;分布式内存对象缓存系统。 Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fitzpatric为首开发的一款软件。现在已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提高Web应用扩展性的重要因素…...

【2022.12.9】Lammps+Python 在计算g6(r)时遇到的问题

目录写在前面绘制g6( r )执行步骤【updated】如何检查图像的正确性&#xff1a;不是编程问题&#xff0c;而是数学问题的一个小bug废稿2则&#xff1a;写在前面 全部log&#xff1a; 【2022.11.16】LammpsPythonMATLAB在绘制维诺图时遇到的问题 绘制g6( r )执行步骤【updated…...

MySQL使用C语言连接

文章目录MySQL使用C语言连接引入库下载库文件在项目中使用库使用库连接数据库下发SQL请求获取查询结果MySQL使用C语言连接 引入库 要使用C语言连接MySQL&#xff0c;需要使用MySQL官网提供的库。 下载库文件 下载库文件 首先&#xff0c;进入MySQL官网&#xff0c;选择DEVEL…...

JavaScript随手笔记---比较两个数组差异

&#x1f48c; 所属专栏&#xff1a;【JavaScript随手笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…...

【C++修炼之路】21.红黑树封装map和set

每一个不曾起舞的日子都是对生命的辜负 红黑树封装map和set前言一.改良红黑树的数据域结构1.1 改良后的结点1.2 改良后的类二. 封装的set和map2.1 set.h2.2 map.h三. 迭代器3.1 迭代器封装3.2 const迭代器四.完整代码实现4.1 RBTree.h4.2 set.h4.3 map.h4.4 Test.cpp前言 上一节…...

下载ojdbc14.jar的10.2.0.1.0版本的包

一、首先要有ojdbc14.jar包 没有的可以去下载一个&#xff0c;我的是从这里下载的ojdbc14.jar下载_ojdbc14.jar最新版下载[驱动包软件]-下载之家&#xff0c; 就是无奈关注了一个公众号&#xff0c;有的就不用下了。 二、找到maven的本地仓库的地址 我的地址在这里D:\apach…...

关于欧拉角你需要知道几个点

基础理解&#xff0c;参照&#xff1a;https://www.cnblogs.com/Estranged-Tech/p/16903025.html 欧拉角、万向节死锁&#xff08;锁死&#xff09;理解 一、欧拉角理解 举例讲解 欧拉角用三次独立的绕确定的轴旋转角度来表示姿态。如下图所示 经过三次旋转&#xff0c;旋…...

git ssh配置

ssh配置 执行以下命令进行配置 git config --global user.name “这里换上你的用户名” git config --global user.email “这里换上你的邮箱” 执行以下命令生成秘钥&#xff1a; ssh-keygen -t rsa -C “这里换上你的邮箱” 执行命令后需要进行3次或4次确认。直接全部回车就…...

Linux进程概念(三)

环境变量与进程地址空间环境变量什么是环境变量常见环境变量环境变量相关命令环境变量的全局属性PWDmain函数的三个参数进程地址空间什么是进程地址空间进程地址空间&#xff0c;页表&#xff0c;内存的关系为什么存在进程地址空间环境变量 什么是环境变量 我们所有写的程序都…...

新手福利——x64逆向基础

一、x64程序的内存和通用寄存器 随着游戏行业的发展&#xff0c;x32位的程序已经很难满足一些新兴游戏的需求了&#xff0c;因为32位内存的最大值为0xFFFFFFFF&#xff0c;这个值看似足够&#xff0c;但是当游戏对资源需求非常大&#xff0c;那么真正可以分配的内存就显得捉襟…...

虚幻c++中的细节之枚举类型(enum)

文章目录前言一、原生c的枚举类型关键字classint8 - 枚举的基础类型&#xff08;underlying type&#xff09;二、枚举类型的灵活运用位运算枚举循环遍历三、虚幻风格的枚举类型UENUMUMETATEnumAsByte总结前言 虚幻引擎中的代码部分实现了一套反射机制&#xff0c;为c代码带了…...

判断某个字符串在另一个字符串中的个数

/** * 用于判断字符串中字符的个数 * * param str1 原字符串 * param str2 需要判断的字符 * return 返回有几个 */ private int getCount(String str1, String str2) { //获取两个字符串的长度 int oneLength str1.length(); int toLength str2.length(); //定义两个整数&am…...

测试人员如何运用好OKR

在软件测试工作中是不是还不知道OKR是什么?又或者每次都很害怕写OKR?或者总觉得很迷茫&#xff0c;不知道目标是什么? OKR 与 KPI 的区别 去年公司从KPI换OKR之后&#xff0c;我也有一段抓瞎的过程&#xff0c;然后自己找了两本书看&#xff0c;一本是《OKR工作法》&#xf…...

CentOS7 Hive2.3.9 安装部署(mysql 8.0)

一、CentOS7安装MySQL数据库 查询载mariadb rpm -qa | grep mariadb卸载mariadb rpm -e --nodeps [查询出来的内容]安装wget为下载mysql准备 yum -y install wget在tools目录下执行以下命令&#xff0c;下载MySQL的repo源&#xff1a; wget -P /tools/ https://dev.mysql.…...

【PTA Advanced】1142 Maximal Clique(C++)

目录 题目 Input Specification: Output Specification: Sample Input: Sample Output: 思路 代码 题目 A clique is a subset of vertices of an undirected graph such that every two distinct vertices in the clique are adjacent. A maximal clique is a clique …...

1. MySQL在金融互联网行业的企业级安装部署

这里写目录标题 1. 版本介绍示例2.安装MySQL规范(建议二进制)2.1 安装方式2.2 安装用户2.3 目录规范3.二进制安装3.1 操作系统配置3.2 MySQL 5.7.33 安装部署2.3 MySQL8.0.27安装2.4 源码安装(了解 )3.多实例部署及注意事项3.1 多实例概念3.2 多实例安装3.3 多实例第二种方式…...

【C++修炼之路】19.AVL树

每一个不曾起舞的日子都是对生命的辜负 AVL树前言&#xff1a;一.AVL树的概念二.AVL树的结构2.1 AVL树节点的定义2.2 AVL树的结构2.3 AVL树的插入2.4 AVL树的验证2.5 AVL树的删除(了解)三.AVL树的旋转&#xff08;重要&#xff09;3.1 左单旋3.2 右单旋3.3 左右双旋3.4 右左双旋…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...