【DAPM杂谈之二】实践是检验真理的标准
本文主要分析DAPM的设计与实现
内核的版本是:linux-5.15.164,下载链接:Linux内核下载
主要讲解有关于DAPM相关的知识,会给出一些例程并分析内核如何去实现的
/*****************************************************************************************************************/
声明: 本博客内容均由芯心智库-CSDN博客原创,转载or引用请注明出处,谢谢!
创作不易,如果文章对你有帮助,麻烦点赞 收藏支持~感谢
/*****************************************************************************************************************/
目录
零、本文主要内容
一、虚拟寄存器的定义
1.1 实现寄存器的读写
2.1 基于kcontrol实现打印所有的寄存器
二、widget实现和route的定义
三、实验验证
3.1 验证环境
3.2 源码编译方法和加载ko
3.3 实验观察一下现象
四、总结
零、本文主要内容
前面讲了一下内核的官方文档,但是光是看文档并不能对DAPM有深入的了解,很多时候我们对原理很懂,但是在实践中总是遇到各种各样的问题.....本文就从应用的角度去看DAPM是如何调用的!
为什么要先从应用的角度去分析,而不从设计者的角度去分析内核中的调用流程呢?因为我觉得实践才是检验真理的唯一标准!先学会用好,再去深究为什么它可以让我用得这么丝滑。
要实践一下DAPM,首先需要一份具备声卡的代码,声卡的注册也是不小的一块内容,我要继续讲一下声卡要怎么写的话,怕是有点冗长了,你可以参考我的文章:
【ASOC全解析(一)】ASOC架构简介和欲解决的问题
我基于我之前的文章(有声卡,但是没有kcontrol、Widgets等等)进行二次开发演示DAPM的功能。
本文所有提交的代码可以见平台:https://github.com/xiaoWEN7/ASOC_DAPM/tree/master
一、虚拟寄存器的定义
1.1 实现寄存器的读写
因为我们的code并不是真实存在的codec,因此会有一个问题,就是我们的各种widget需要设定寄存器的时候,无法去设定,那么为了解决这个问题,我们需要原本的snd_soc_component_driver结构体下面定义一下:
.read = virtual_reg_read,
.write = virtual_reg_write,
这部分一般的codec驱动程序都是不定义的(通常使用regmap),但是为了更加容易去控制我们手动去指定寄存器的读写函数,参考代码调用:
/sound/soc/soc-component.c
static unsigned int soc_component_read_no_lock(struct snd_soc_component *component,unsigned int reg)
{int ret;unsigned int val = 0;if (component->regmap)ret = regmap_read(component->regmap, reg, &val);else if (component->driver->read) {ret = 0;val = component->driver->read(component, reg);}elseret = -EIO;if (ret < 0)return soc_component_ret(component, ret);return val;
}
static unsigned int soc_component_read_no_lock(struct snd_soc_component *component,unsigned int reg)
{int ret;unsigned int val = 0;if (component->regmap)ret = regmap_read(component->regmap, reg, &val);else if (component->driver->read) {ret = 0;val = component->driver->read(component, reg);}elseret = -EIO;if (ret < 0)return soc_component_ret(component, ret);return val;
}
我们定义component->driver->read的读写函数和虚拟一个寄存器reg_data:
/* 寄存器列表 */
enum reg {VCODEC_ADCL_REG,VCODEC_MIC1_REG,VCODEC_Output_mixer_REG,VCODEC_CTRL_NUM //寄存器总数
};static u32 reg_data[VCODEC_CTRL_NUM]; //虚拟的寄存器static unsigned int virtual_reg_read(struct snd_soc_component *component, unsigned int reg)
{pr_info("virtual_reg_read : reg = %d reg_data[reg]=%d \n",reg,reg_data[reg]);return reg_data[reg];
}static int virtual_reg_write(struct snd_soc_component *component, unsigned int reg, unsigned int value)
{pr_info("virtual_reg_write : reg = %d , value = %d\n",reg ,value);reg_data[reg] = value;return 0;
}static struct snd_soc_component_driver soc_my_codec_drv = {.probe = my_codec_probe,.remove = my_codec_remove,.read = virtual_reg_read,.write = virtual_reg_write,
};
2.1 基于kcontrol实现打印所有的寄存器
上面虽然实现了寄存器的读写,要一次一次的核对Kernel log,这其实并不方便,我在这里实现一个kcontrol用于显示所有的寄存器数值:
先在snd_soc_component_driver我们使用controls:
.controls = my_controls,
.num_controls = ARRAY_SIZE(my_controls),
然后实现my_controls:
static const char *const reg_date[] = { "VCODEC_ADCL_REG", "VCODEC_MIC1_REG", "VCODEC_Output_mixer_REG"};int current_reg = 0;// 定义一个枚举描述结构
static const struct soc_enum audio_reg_dump =SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(reg_date),
reg_date);// 获取当前音频输出模式的函数
static int get_audio_reg(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{int i;ucontrol->value.integer.value[0] = current_reg;pr_info("************audio_reg Dump************\n");for(i=0;i<VCODEC_CTRL_NUM;i++){pr_info("reg_data[%d] = %d \n",i,reg_data[i]);}pr_info("************audio_reg Dump************\n");return 0;
}// 设置音频输出模式的函数
static int set_audio_reg(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{current_reg = ucontrol->value.integer.value[0];pr_info("current_reg : %d \n",current_reg);return 0;
}static const struct snd_kcontrol_new my_controls[] = {SOC_ENUM_EXT("Get Audio reg", audio_reg_dump,get_audio_reg, set_audio_reg),
};
这边我还顺便实现了基本的读写功能,这个是为什么呢?主要是展示后面的DAPM的特征用的。这一点我们文章往后会分析一下。
二、widget实现和route的定义
widget和route一般是需要同时出现了:
widget:抽象硬件成为一个一个小部件
route:描述这些部件之间的连接关系
所以要同时对这两个进行定义:
.dapm_widgets = vcodec_dapm_widgets,.num_dapm_widgets = ARRAY_SIZE(vcodec_dapm_widgets),.dapm_routes = vcodec_dapm_routes,.num_dapm_routes = ARRAY_SIZE(vcodec_dapm_routes),
然后就是实现了,我这边假设一种硬件通路,可以让mic1这个硬件连接到ADC采集器并且ADC采集的数据直接输出到上层,硬件框架大概如下:
那么这个路由也是很明确的,应该这么写:
/* 定义音频路径(路由) */
static const struct snd_soc_dapm_route vcodec_dapm_routes[] = {/* MIC 输入路径 */{"ADCL Input", "MIC1 Boost Switch", "MIC1"},{"ADCL", NULL, "ADCL Input"},{"Output Switch", "Output_mixer", "ADCL"},/* 如果有其他输入输出路径,在此添加 */{"ADC Output", NULL, "Output Switch"},
};
MIC1可以通过一个开关去选择它到底要从ADCL Input采集,还是ADCR Input采集(我上面的路由没有抽象出ADCR的通路)。
然后如果走ADCL Input进行输入,那么会经过ADCL的控制器,这ADC控制器有很多设置,其中bit 2是使能开关,当然除了使能之外,还需要设置ADC的阻抗、通道选择、预分频器设置等等一堆设置,这些设置往往是真正需要工作的时候才开始设置,不然耗电还是挺高的。
最后经过ADCL的采集后,产生了数字信号,那么数字信号还可以被各种增益、削频等出来,也可以直接输出到上层。如果走Output Switch代表了ADC直接采集的数字信号会被输出到上层使用,如果走other swith就是其他的处理了。
综合上面,代码如下实现:
/* 定义事件处理函数 */
static int vcodec_capture_event(struct snd_soc_dapm_widget *w,struct snd_kcontrol *kcontrol, int event)
{switch (event) {case SND_SOC_DAPM_WILL_PMU:pr_info("Vcodec capture event: POST_PMU\n");break;case SND_SOC_DAPM_WILL_PMD:pr_info("Vcodec capture event: POST_PMD\n");break;default:break;}return 0;
}/* 定义控制项 */
static const struct snd_kcontrol_new mic1_input_mixer[] = {SOC_DAPM_SINGLE("MIC1 Boost Switch", VCODEC_MIC1_REG, 0, 1, 0),/* 如果有其他控件可以在这里继续添加 */
};static const struct snd_kcontrol_new mic1_Output_mixer[] = {SOC_DAPM_SINGLE("Output_mixer", VCODEC_Output_mixer_REG, 0, 1, 0),/* 如果有其他控件可以在这里继续添加 */
};/* 定义 DAPM Widget */
static const struct snd_soc_dapm_widget vcodec_dapm_widgets[] = {SND_SOC_DAPM_INPUT("MIC1"),SND_SOC_DAPM_MIXER("ADCL Input", SND_SOC_NOPM, 0, 0,mic1_input_mixer, ARRAY_SIZE(mic1_input_mixer)),/* 2表示偏移量,也就是设置这个寄存器的数值为 1<<2 = 4 */SND_SOC_DAPM_AIF_OUT_E("ADCL", "Capture", 0, VCODEC_ADCL_REG,2, 0, vcodec_capture_event,SND_SOC_DAPM_WILL_PMU | SND_SOC_DAPM_WILL_PMD),SND_SOC_DAPM_MIXER("Output Switch", SND_SOC_NOPM, 0, 0,mic1_Output_mixer, ARRAY_SIZE(mic1_Output_mixer)),SND_SOC_DAPM_OUTPUT("ADC Output"), /* 输出口 */
};/* 定义音频路径(路由) */
static const struct snd_soc_dapm_route vcodec_dapm_routes[] = {/* MIC 输入路径 */{"ADCL Input", "MIC1 Boost Switch", "MIC1"},{"ADCL", NULL, "ADCL Input"},{"Output Switch", "Output_mixer", "ADCL"},/* 如果有其他输入输出路径,在此添加 */{"ADC Output", NULL, "Output Switch"},
};
细心的你会发现,在DAPM中也定义了一些kcontrol,如下:
SOC_DAPM_SINGLE("MIC1 Boost Switch", VCODEC_MIC1_REG, 0, 1, 0),
SOC_DAPM_SINGLE("Output_mixer", VCODEC_Output_mixer_REG, 0, 1, 0),
DAPM的kcontrol是带有DAPM关键字的,而普通的kcontrol是不带DAPM关键字的,那么我们可以比较容易的推导出来,这个带了DAPM关键字的kcontrol要比普通的kcontrol要多做了一些事情,来实现DAPM的特征,这个具体带了什么功能,后续文章会继续讲解。
三、实验验证
3.1 验证环境
为了方便去验证DAPM,我这边直接在云服务器上面验证,我的云服务器的版本如下(你可以使用其他的云服务器,但是Linux Kernel需要是5.15左右):
# uname -a
Linux 5.15.0-113-generic #123-Ubuntu SMP Mon Jun 10 08:16:17 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
我搞的更多的是安卓方面的开发,这边搭建一个tinyasla的环境(如果你熟悉原生的Linux环境,比如aplay、amixer,也可以自行搭建自己熟悉的):
git clone https://github.com/tinyalsa/tinyalsa
然后按照官方文档搭建:
make
sudo make install
sudo ldconfig
在云服务器上面,一般是关闭了音频模块的,手动打开一下,如果你使用实际的Ubuntu设备,是带有真实声卡的,可以不用下面的操作:
modprobe snd_soc_core
3.2 源码编译方法和加载ko
源码的编译,直接make就会生成ko:
make
生成结果:
# ls
codec.c machine.c Makefile platform.c
# make
make -C /usr/src/linux-headers-5.15.0-113-generic M=`pwd` modules
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-113-generic'CC [M] /root/test_Asoc/new_ASOC/platform.oCC [M] /root/test_Asoc/new_ASOC/codec.oCC [M] /root/test_Asoc/new_ASOC/machine.oMODPOST /root/test_Asoc/new_ASOC/Module.symversCC [M] /root/test_Asoc/new_ASOC/codec.mod.oLD [M] /root/test_Asoc/new_ASOC/codec.koBTF [M] /root/test_Asoc/new_ASOC/codec.ko
Skipping BTF generation for /root/test_Asoc/new_ASOC/codec.ko due to unavailability of vmlinuxCC [M] /root/test_Asoc/new_ASOC/machine.mod.oLD [M] /root/test_Asoc/new_ASOC/machine.koBTF [M] /root/test_Asoc/new_ASOC/machine.ko
Skipping BTF generation for /root/test_Asoc/new_ASOC/machine.ko due to unavailability of vmlinuxCC [M] /root/test_Asoc/new_ASOC/platform.mod.oLD [M] /root/test_Asoc/new_ASOC/platform.koBTF [M] /root/test_Asoc/new_ASOC/platform.ko
Skipping BTF generation for /root/test_Asoc/new_ASOC/platform.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-113-generic'
# ls
codec.c codec.mod.c codec.o machine.ko .machine.mod.cmd .machine.o.cmd Module.symvers .platform.ko.cmd platform.mod.o
codec.ko .codec.mod.cmd .codec.o.cmd .machine.ko.cmd machine.mod.o Makefile .Module.symvers.cmd platform.mod .platform.mod.o.cmd
.codec.ko.cmd codec.mod.o .git machine.mod .machine.mod.o.cmd modules.order platform.c platform.mod.c platform.o
codec.mod .codec.mod.o.cmd machine.c machine.mod.c machine.o .modules.order.cmd platform.ko .platform.mod.cmd .platform.o.cmd
#
加载ko(注意machine.ko一定是最后加载的,因为是在machine.ko会注册声卡,dai link依赖前面两个ko):
# insmod codec.ko
# insmod platform.ko
# insmod machine.ko
# cat /proc/asound/pcm
00-00: MY-CODEC my_codec.0-0 : : playback 1 : capture 1
#
通过cat /proc/asound/pcm我们可以看到声卡已经加载成功了。
3.3 实验观察一下现象
根据我们的源码,我们会注册三个控件,一个是普通的kcontrol,两个是DAMP类的kcontrol,可以输入命令查看一下:
# tinymix contents
Number of controls: 3
ctl type num name device value
0 ENUM 1 Get Audio reg 0> VCODEC_ADCL_REG, VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
1 BOOL 1 ADCL Input MIC1 Boost Switch 0Off
2 BOOL 1 Output Switch Output_mixer 0Off
#
- 我们先试一下普通的kcontrol
# tinymix -D 0 get 0
> VCODEC_ADCL_REG, VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
# tinymix -D 0 set 0 1
# tinymix -D 0 get 0
VCODEC_ADCL_REG, > VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
#
看一下Kernel log:
[22086.167666] ************audio_reg Dump************
[22086.167670] reg_data[0] = 0
[22086.167672] reg_data[1] = 0
[22086.167672] reg_data[2] = 0
[22086.167673] ************audio_reg Dump************
[22097.375953] ************audio_reg Dump************
[22097.375956] reg_data[0] = 0
[22097.375958] reg_data[1] = 0
[22097.375958] reg_data[2] = 0
[22097.375959] ************audio_reg Dump************
[22097.375962] current_reg : 1
[22105.824032] ************audio_reg Dump************
[22105.824035] reg_data[0] = 0
[22105.824037] reg_data[1] = 0
[22105.824038] reg_data[2] = 0
[22105.824039] ************audio_reg Dump************
- 看一下DAPM的情况:
# tinymix -D 0 set "ADCL Input MIC1 Boost Switch" 1
# tinymix -D 0 get 0
VCODEC_ADCL_REG, > VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
#
看一下Kernel log:
[22455.184560] virtual_reg_read : reg = 1 reg_data[reg]=0
[22455.184566] virtual_reg_read : reg = 1 reg_data[reg]=0
[22455.184572] virtual_reg_read : reg = 1 reg_data[reg]=0
[22455.184573] virtual_reg_write : reg = 1 , value = 1
[22457.360196] ************audio_reg Dump************
[22457.360199] reg_data[0] = 0
[22457.360201] reg_data[1] = 1
[22457.360202] reg_data[2] = 0
[22457.360203] ************audio_reg Dump************
你会发现直接就设定成功了。
但是细心的你想一下,下面这个寄存器怎么设定呢?似乎也没有引用出相关的kcontrol给我们进行设定:

这个就是DAPM的神奇之处了,即只有通路完整了,相关的寄存器才会被设置,在我的例程里面,这个相关的寄存器就是上图的寄存器:VCODEC_ADCL_REG。
我们只需要在设定一下output switch,那么通路就完整:
# tinymix -D 0 set "Output Switch Output_mixer" 1
# tinymix -D 0 get 0
VCODEC_ADCL_REG, > VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
#
看一下Kernel log的寄存器打印打印情况和调用情况:
[22799.483102] virtual_reg_read : reg = 2 reg_data[reg]=0
[22799.483109] virtual_reg_read : reg = 2 reg_data[reg]=0
[22799.483144] Vcodec capture event: POST_PMU
[22799.483146] virtual_reg_read : reg = 2 reg_data[reg]=0
[22799.483147] virtual_reg_write : reg = 2 , value = 1
[22799.483148] virtual_reg_read : reg = 0 reg_data[reg]=0
[22799.483149] virtual_reg_write : reg = 0 , value = 4
[22801.277420] ************audio_reg Dump************
[22801.277424] reg_data[0] = 4
[22801.277425] reg_data[1] = 1
[22801.277426] reg_data[2] = 1
[22801.277427] ************audio_reg Dump************
- 如果此时把完整的通路变成不完整,那么对应的SND_SOC_DAPM_WILL_PMD事件和寄存器就会被自动调用与设定:
# tinymix -D 0 set "Output Switch Output_mixer" 0
# tinymix -D 0 get 0
VCODEC_ADCL_REG, > VCODEC_MIC1_REG, VCODEC_Output_mixer_REG,
#
Kernel log为:
[23118.456984] virtual_reg_read : reg = 2 reg_data[reg]=1
[23118.456990] virtual_reg_read : reg = 2 reg_data[reg]=1
[23118.457028] Vcodec capture event: POST_PMD
[23118.457030] virtual_reg_read : reg = 0 reg_data[reg]=4
[23118.457031] virtual_reg_write : reg = 0 , value = 0
[23118.457032] virtual_reg_read : reg = 2 reg_data[reg]=1
[23118.457033] virtual_reg_write : reg = 2 , value = 0
[23120.979284] ************audio_reg Dump************
[23120.979288] reg_data[0] = 0
[23120.979289] reg_data[1] = 1
[23120.979290] reg_data[2] = 0
[23120.979291] ************audio_reg Dump************
可以看到SND_SOC_DAPM_WILL_PMD事件被触发并且打印“Vcodec capture event: POST_PMD”,且不仅kcontrol对应的寄存器reg_data[2]被设定了,reg_data[0]也被自动设定了!!
在实际的代码中,这些事件的回调就可以用于去设定相关的寄存器了,写代码的工程师可以根据自己IC的特征,来对各种事件进行响应,比如上电需要设定哪些寄存器,下电需要设定哪些寄存器。
四、总结
通过上面这一堆假设的寄存器和通路,我们可以知道DAPM对比与kcontrol的一个特征:即通路完整后,会自动进行相关寄存器的上电与调用相关的事件去处理。
在实际的应用中,往往我们用这个特征进行硬件电路的抽象,将一些控制器的寄存器封装到DAPM中,让DAPM帮我们管理器件的上电和下电。
相关文章:
【DAPM杂谈之二】实践是检验真理的标准
本文主要分析DAPM的设计与实现 内核的版本是:linux-5.15.164,下载链接:Linux内核下载 主要讲解有关于DAPM相关的知识,会给出一些例程并分析内核如何去实现的 /**************************************************************…...
关于使用FastGPT 摸索的QA
近期在通过fastGPT,创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上,可以定义业务逻辑,满足输出对话之外的需求。 在最近3个月来的摸索和实践中,一些基于经验的小问题点(自己也常常…...
虚拟文件系统 VFS
目录 虚拟文件系统 VFS 文件系统挂载过程 虚拟文件系统 VFS 统一标准的系统调用接口: VFS定义了一组标准的文件操作API,如open(), read(), write(), close()等,使得用户空间的应用程序无需关心底层文件系统的具体类型。 下层文件系统必须实现…...
React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)
触发渲染过程——renderRoot renderRoot 是一个函数,用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务,直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork,以及渲染新状态或 DOM。 function ren…...
Xcode 正则表达式实现查找替换
在软件开发过程中,查找和替换文本是一项常见的任务。正则表达式(Regular Expressions)是一种强大的工具,可以帮助我们在复杂的文本中进行精确的匹配和替换。Xcode 作为一款流行的开发工具,提供了对正则表达式的支持。本…...
【opencv】第8章 图像轮廓与图像分割修复
8.1 查找并绘制轮廓 一个轮廓一般对应一系列的点,也就是图像中的一条曲线。其表示方法可能 根据不同的情况而有所不同。在OpenCV 中,可以用findContours()函数从二值图 像中查找轮廓 8.1.1 寻找轮廓: findContours() 函数 findContours) 函…...
excel VBA 基础教程
这里写目录标题 快捷键选择所有有内容的地方 调试VBA录制宏,打开VBA开发工具录制宏,相当于excel自动写代码(两个表格内容完全一致才可以) 查看宏代码保持含有宏程序的文件xlsm后缀(注意很容易有病毒)宏文件安全设置 使…...
2008-2019年各省城镇人口数据
2008-2019年各省城镇人口数据 1、时间:2008-2019年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区、年份、城镇人口 4、范围:31省 5、指标解释:城镇人口是指居住在城镇范围内的全部常住人口。 6、下…...
【机器学习】在不确定的光影中:机器学习与概率论的心灵共舞
文章目录 概率与统计基础:解锁机器学习的数据洞察之门前言一、概率论基础1.1 概率的基本概念与性质1.1.1 概率的定义1.1.2 样本空间与事件1.1.3 互斥事件与独立事件1.1.4 概率的计算方法 1.2 条件概率与独立性1.2.1 条件概率1.2.2 独立事件 1.3 随机变量1.3.1 随机变…...
vscode使用Marscode编程助手
下载 vscode 在插件里下载Marscode编程助手 插件完成 在这里点击安装,点击后这里出现AI编程插件。...
谷歌开放语音命令数据集,助力初学者踏入音频识别领域
在人工智能的浪潮中,语音识别技术正逐渐成为我们日常生活的一部分。从智能助手到语音控制设备,语音识别的应用场景越来越广泛。然而,对于初学者来说,进入这一领域往往面临诸多挑战,尤其是缺乏合适的开源数据集和简单的…...
Diffchecker图像比较工具介绍
Diffchecker图像比较工具介绍 网站地址: Diffchecker图像比较 主要功能: 图像差异比较: 该工具允许用户上传两张图片,系统会自动识别并高亮显示这两张图片之间的差异。简单易用: 用户只需将图片拖放到指定区域或点击浏…...
后端开发 Springboot整合Redis Spring Data Redis 模板
目录 redis 配置 RedisConfig 类 完整代码 代码讲解 1. 类定义和注解 2. 定义 RedisTemplate Bean 3. 配置 JSON 序列化 4. 配置 Redis 的 key 和 value 序列化方式 5. 完成配置并返回 RedisTemplate 总结 redis 服务接口实现类 类级别 注入 RedisTemplate 常用 Re…...
极狐GitLab 正式发布安全版本17.7.1、17.6.3、17.5.5
本分分享极狐GitLab 补丁版本 17.7.1, 17.6.3, 17.5.5 的详细内容。这几个版本包含重要的缺陷和安全修复代码,我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS,技术团队已经进行了升级,无需用户采取任何…...
策略模式详解与应用
策略模式(Strategy Pattern),是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换,而应用程序可以在运行时选择使用哪一个算法。策略模式使得算法的变化独立于使…...
Gateway怎么实现限流的
Gateway怎么实现限流的 在API网关(如Spring Cloud Gateway、Kong、Nginx等)中实现限流是为了控制服务请求的频率,从而避免系统过载,确保稳定性和可用性。限流可以通过多种策略实现,常见的方法包括基于请求次数、时间窗…...
OpenCV实现Kuwahara滤波
Kuwahara滤波是一种非线性的平滑滤波技术,其基本原理在于通过计算图像模板中邻域内的均值和方差,选择图像灰度值较为均匀的区域的均值来替代模板中心像素的灰度值。以下是Kuwahara滤波的详细原理说明: 一、基本思想 Kuwahara滤波的基本思想…...
【DevOps】Jenkins使用Pipeline构建java代码
使用Pipeline发布java项目 文章目录 使用Pipeline发布java项目资源列表基础环境一、准备gitlab1.1、部署gitlab1.2、创建chinanews项目1.3、提交代码1.4、查看上传的代码 二、准备Jenkins2.1、部署Jenkins2.2、安装maven2.3、修改Maven源2.4、准备chinanews 三、Jenkins配置工具…...
【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论和实操考试题
文章目录 选择题理论题 解释RIP协议中的“水平分割”机制,并说明其目的。 可以防止路由器错误地将从邻居学到的路由再发送回给该邻居,从而避免路由环路的发生。实操题 【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论…...
Entity 的材质(棋盘、条纹、网格)
Entity 的材质 普通物体的材质 import { nextTick, onMounted, ref } from vue import * as Cesium from cesium // console.log(Cesium, Cesium)const viewer ref<any>(null)onMounted(() > { ... })let material Cesium.Color.YELLOW.withAlpha(0.5)Cesium.Colo…...
shell脚本编写练习3
1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容,不存在则创建一个文件将创建时间写入。 #!/bin/bash # 定义文件路径变量 file_path"/tmp/size.log"# 使用if语句检查文件是否存在 if [ -e "$file_path" ] # 检查变量file_path…...
事务的隔离级别和MDL
文章目录 说明不同隔离级别可能发生的现象关键现象解释MDL(元数据锁,Metadata Lock)MDL 的作用MDL 的工作原理MDL 锁的常见场景如何避免 MDL 阻塞 说明 本文章由大模型对话整理而来,如果有错误之处,请在评论区留言指正…...
用户界面软件05
已知应用 几乎所有的流行的用户界面架构都使用这种模式。我在这里举三个例子: 1. Seeheim 用户界面架构的特点是有一个应用核心的领域层和一个用户界面层。后者 被分为两层,叫做表示层和对话控制层。因为这个架构和面向事务系统有渊源,没有…...
基于Springboot + vue实现的办公用品管理系统
🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…...
17_Redis管道技术
Redis管道(Pipeline)技术是一种在 Redis 客户端与服务器之间进行高效数据交互的技术。 1.Redis管道技术介绍 1.1 传统请求响应模式 在传统的请求-响应模式下,客户端每发送一个命令后会等待服务器返回结果,然后再发送下一个命令。这种方式在网络延迟较高的情况下会导致性…...
【环境搭建】Metersphere v2.x 容器部署教程踩坑总结
前言 Metersphere部署过程中遇到的问题有点多,原因是其容器的架构蛮复杂的,比较容易踩坑,所以记录一下。 介绍 MeterSphere 是开源持续测试平台,遵循 GPL v3 开源许可协议,涵盖测试管理、接口测试、UI 测试和性能测…...
Vue虚拟DOM:如何提高前端开发效率
前言 随着前端技术的不断发展,越来越多的框架和库涌现出来,其中Vue.js成为了最受欢迎的前端框架之一。Vue.js采用了响应式数据绑定和组件化的思想,让开发者可以更加高效地构建交互式的用户界面。而Vue.js的底层原理涉及到许多概念和技术&…...
【C】预处理详解
在上一篇文章中,简单讲解了一个C程序是如何从一句句C代码变为一个个二进制指令,并最终变成可执行程序成功运行。在预处理、编译、汇编、链接四个步骤中,预处理阶段做的事情特别多,接下来我们就来讲解一下在预处理阶段处理的一些预…...
CES Asia 2025:VR/AR/XR引领科技新潮流
在全球科技领域蓬勃发展的大背景下,CES Asia 2025(赛逸展)即将在京盛大开幕,VR/AR/XR技术作为前沿科技的代表,将在本次展会上大放异彩,展现出令人瞩目的发展趋势和巨大潜力,同时政策优势也将为其…...
Lua调用C#
目录 创建C#入口 Lua调用类 Lua调用枚举 Lua调用数组,列表,字典 Lua调用C#拓展方法 Lua调用C#Ref与Out知识 Lua调用C#函数重载 Lua调用C#委托与事件 Lua调用C#二维数组 Lua调用C#中nil与null的差距 Lua调用C#中让系类型与lua能够互相访问 Lua调用…...
EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施
靶场搭建 当我们考虑到攻击他人服务器属于违法行为时,我们需要思考如何更好地保护我们自己的服务器。为了测试和学习,我们可以搭建一个专门的靶场来模拟文件上传漏洞攻击。以下是我搭建靶场的环境和一些参考资料,供大家学习和参考࿰…...
springboot使用Easy Excel导出列表数据为Excel
springboot使用Easy Excel导出列表数据为Excel Easy Excel官网:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write 主要记录一下引入时候的pom,直接引入会依赖冲突 解决方法: <!-- 引入Easy Excel的依赖 -->&l…...
现代 CPU 的高性能架构与并发安全问题
现代 CPU 的设计(如多级缓存、指令重排)为了提升性能,引入了许多优化机制,但这些机制可能导致并发场景下的安全性问题。并发安全性主要体现在三个方面:原子性、有序性 和 可见性。这些问题在底层通过 CAS(C…...
【数模学习笔记】插值算法和拟合算法
声明:以下笔记中的图片以及内容 均整理自“数学建模学习交流”清风老师的课程资料,仅用作学习交流使用 文章目录 插值算法定义三个类型插值举例插值多项式分段插值三角插值 一般插值多项式原理拉格朗日插值法龙格现象分段线性插值 牛顿插值法 Hermite埃尔…...
JavaScript 数组及其常用方法
1. JavaScript 数组概述 数组是 JavaScript 中用于存储多个值的数据结构。它可以存储不同类型的元素,并提供强大的方法来操作和管理数据。数组的元素按索引(从 0 开始)进行访问。 2. 数组的创建方式 1) 使用数组字面量 let fruits [&quo…...
SQL HAVING 子句深入解析
SQL HAVING 子句深入解析 介绍 SQL(Structured Query Language)是一种用于管理关系数据库管理系统的标准编程语言。在SQL中,HAVING子句是与GROUP BY子句一起使用的,用于筛选分组后的数据。它根据聚合函数的结果对组进行条件过滤…...
vue3+ts的几个bug调试
由于编译问题,把几个type检查给关闭了,否则错误太多。 1)第一个检查出的问题,拼写错误数组的length,写成了lengh。 2)数组的对象引用。 torStatus Array(8).fill({ ...defaultStatus }) as TorStatus[]…...
git: hint:use --reapply-cherry-picks to include skipped commits
问: 当我在feture分支写完功能,切换到dev更新了远端dev代码,切回feture分支,git rebase dev分支后出现报错: warning skipped previously applied commit 709xxxx hint:use --reapply-cherry-picks to include skippe…...
Microsoft Sql Server 2019 数据类型
数据类型 bigint、int、smallint、tinyint 使用整数数据的精确数字数据类型。 若要节省数据库空间,请使用能够可靠包含所有可能值的最小数 据类型。 例如,对于一个人的年龄,tinyint 就足够了,因为没人活到 255 岁以上。 但对于建筑物的 年龄,tinyint 就不再适应,因为建…...
C++实现设计模式---代理模式 (Proxy)
代理模式 (Proxy) 代理模式 是一种结构型设计模式,它为其他对象提供一个代理以控制对该对象的访问。代理模式常用于延迟加载、访问控制、智能引用等场景。 意图 提供对某对象的控制。控制对目标对象的访问,通常用于在不改变目标对象的情况下࿰…...
微信小程序用的SSL证书有什么要求吗?
微信小程序主要建立在手机端使用,然而手机又涉及到各种系统及版本,所以对SSL证书也有要求,如果要小程序可以安全有效的访问需要满足以下要求: 1、原厂SSL证书(原厂封)。 2、DV单域名或者DV通配符。 3、兼…...
Flutter中Get.snackbar和Get.dialog关闭冲突问题记录
背景: 在使用GetX框架时,同时使用了Get.snackbar提示框和Get.dialog加载框,当这两个widget同时存在时,Get.dialog加载框调用Get.back()无法正常关闭。 冲突解释: 之所以会产生冲突,是因为Get.snackbar在关…...
命令模式-Command Pattern
什么是命令模式 命令模式是一种行为类设计模式,核心是将每种请求或操作封装为一个独立的对象,从而可以集中管理这些请求或操作,比如将请求队列化依次执行、或者对操作进行记录和撤销。 命令模式通过将请求的发送者(客户端)和接收者(执行请求…...
【Linux笔记】Day1
基于韩顺平老师课程记录: https://www.bilibili.com/video/BV1Sv411r7vd 安装CentOS 给CentOS手动分区 分为三个区: boot分区(给1G就行) 交换分区(和内存相关,这里和虚拟机的内存2G一致) …...
如何明智地提问
如何明智地提问的重要总结,让我为主要观点添加一些具体的实践建议: 提问前的准备工作 尝试在 Google、Stack Overflow 等平台搜索相似问题阅读相关文档和错误日志尝试自己调试和排查问题记录下已尝试过的解决方案 选择合适的提问平台 Stack Overflow…...
【大前端】Vue3 工程化项目使用详解
目录 一、前言 二、前置准备 2.1 环境准备 2.1.1 create-vue功能 2.1.2 nodejs环境 2.1.3 配置nodejs的环境变量 2.1.4 更换安装包的源 三、工程化项目创建与启动过程 3.1 创建工程化项目 3.2 项目初始化 3.3 项目启动 3.4 核心文件说明 四、VUE两种不同的API风格 …...
window.print()预览时表格显示不全
问题描述:使用element的table组件,表格列宽为自适应,但使用window.print()方法预览的页面会直接按预览宽度截取表格内容进行展示,造成表格可能的显示不全问题 解决方法:添加如下样式 media print {::v-deep {// 表头…...
《解锁鸿蒙Next系统人工智能语音助手开发的关键步骤》
在当今数字化时代,鸿蒙Next系统与人工智能的融合为开发者带来了前所未有的机遇,开发一款人工智能语音助手应用更是备受关注。以下是在鸿蒙Next系统上开发人工智能语音助手应用的关键步骤: 环境搭建与权限申请 安装开发工具:首先需…...
计算机网络之---MAC协议
MAC协议的作用 在数据链路层中,MAC(媒介访问控制)协议负责控制设备如何访问共享的通信介质(如以太网、无线电波等),确保在多台设备共享同一传输媒介时能够有效地进行数据传输,避免冲突、控制流…...
系统思考—要素连接
“改变你的思维,就能改变你的世界”— 诺曼皮尔 世界上的所有事物,都在规律的支配下,以系统的方式运转。显性的部分是我们能看到的“要素”,而那些看不见的力量,正是推动系统运作的要素之间的相互作用。更隐秘的&…...