【Linux驱动】设备树模型的LED驱动 | 查询方式的按键驱动
🐱作者:一只大喵咪1201
🐱专栏:《Linux驱动》
🔥格言:你只管努力,剩下的交给时间!
目录
- 🍮设备树模型的LED驱动
- 🍩设备树文件
- 🍩驱动程序
- 🍮应用层读取按键值
- 🍩查询方式
- 🍩休眠唤醒方式
- 🍩poll方式
- 🍩异步通知方式
- 🍮查询方式实现按键驱动
- 编程
- 🍮总结
🍮设备树模型的LED驱动
目前有三种方式来写LED驱动程序:
- 最简单的驱动模型——硬件操作绑定在驱动函数中。
- 总线驱动模型。
- 设备树驱动模型。
下面设备树驱动模型来实现一下LED驱动程序,该模型主要分为两部分,设备树文件和驱动程序。
🍩设备树文件

如上图所示设备树文件,在设备树中增加Big-Miaomi-LED@0和Big-Miaomi-LED@1两个设备节点:
-
compatible属性:属性值都是BigMiaomi,LED_Driver。 -
pin属性:属性值是各自节点所用GPIO组和引脚编号组成的32位整数。 -
如果在设备树节点里使用
reg属性,内核在生成对应的platform_device时,reg属性会被转换成IORESOURCE_MEM类型的资源。 -
如果在设备树节点里使用
interrputs属性,内核在生成对应的platform_device时,interrupts属性会被转换成IORESOURCE_IRQ类型的资源。
但是本喵写的Big-Miaomi-LED节点中,属性名是pin,该属性名是本喵自己定义的,不在内核自动转换资源类型的的命名范围内。
所以就不能从转换后的platform_device结构体中的resources数组中获得引脚资源了,具体获取方式编程时候再说。

如上图,然后在内核目录中使用make dtbs指令编译设备树文件,转换为内核认识的dtb文件。
🍩驱动程序
驱动程序在总线驱动模型的基础上进行修改,驱动层的上层不用动,只需要改变下层中的部分代码:

如上图所示,由于现在支持了设备树,所以需要初始化platform_deiver结构体中driver成员里的of_match_table成员,这是一个struct of_device_id类型的数组。
所以需要定义一个struct of_device_id类型的数组,名为BigMiaomi_LEDs:
- 只用
platform_device和platform_driver匹配规则中优先级最高的compatible属性来匹配。 - 只支持LED设备,所以
compatible属性只有一个值。
compatible属性的值,必须和设备树中要支持节点的compatible属性值相同,才能匹配成功。
然后就是在匹配成功以后,会自动调用paltform_driver中的probe函数,在该函数中,原本是从paltform_device的resources数组中获取硬件资源,但是此时不能这样干了:

如上图所示probe函数,在该函数中首先要获取pin资源:
- 设备树中的
pin属性没有被转换到resources数组中,但是在第一次转换为device_node里的properties中是有该属性的。- 从匹配成功的
platform_device中得到当前节点的device_node结构体指针of_node。
- 从匹配成功的
- 使用
of_property_read_32函数,从np指向的当前节点deivce_node中的properties里找到pin属性,并且以32位整数的方式读取该属性的value值。- 将表示引脚资源的32位属性值放入到记录引脚资源的全局数组
g_ledpins中。
- 将表示引脚资源的32位属性值放入到记录引脚资源的全局数组
获取到引脚资源后的其他操作和总线模型中相同,也是要使用led_class_create_device在/dev目录下创建设备节点。
- 设备树文件中的设备节点,内核加载后并不会在
/dev目录下创建相应的文件,它不属于文件字符设备文件系统。

如上图代码所示,既然probe中的获取引脚资源的方式变了,那么在remove中获取引脚资源的方式和其他处理也要做出相应变化:
- 从要移除设备节点的
device_node中获取引脚资源led_pin。 - 遍历存放引脚资源的全局数组找到要移除的节点,移除后将对应的值修改为-1。
- 最后判断一下是否该类型的设备节点全部移除了,如果存放引脚资源的全局数组中,所有值都成了-1,则说明全部移除了。
此时整个驱动程序就修改完毕了,相比于总线驱动模型,只是在获取引脚资源的方式上做了改变。

如上图所示Makfile文件,只需要编译驱动层上层led_drv.c和下层chip_led_opr.c即可,board_A.c不用再参与编译了。
- 因为引脚资源不再由
board_A.c中的platform_device结构体提供了。 - 引脚资源由设备树文件提供,由内核将设备节点转换为
platform_device结构体。

如上图所示,将在Linux服务器中编译好的dtb设备树文件和led_drv.ko及chip_led_opr.ko驱动文件,还有led_drv_test测试文件拷贝到网络根文件系统中。
在开发板上将dtb设备树文件拷贝到/boot目录下,然后重启开发板.

如上图所示,在/sys/firmware/devicetree/base/路径下,存在Big-Miaomi-LED@0和Big-Miaomi-LED@1两个设备节点,这是我们在设备树文件中添加的两个节点,此时加载到了内核中。然后使用insmod led_drv.ko和insmod chip_led_opr.ko安装驱动程序。

如上图所示,此时执行测试程序,在命令行中输入./led_drv_test /dev/BigMiaomi_LED0 on,内核打印信息现实操作了GPIO3_1。
🍮应用层读取按键值
应用层读取按键值有4种方式:
- 查询方式
- 休眠-唤醒方式
- poll方式
- 异步通知方式
无论使用哪个方式都需要有按键驱动程序,通过这四种方式可以掌握一些驱动的基本技能:中断、休眠、唤醒、poll等机制。
这些基本技能是驱动开发的基础,其他大型驱动复杂的地方是它的框架及设计思想,但是基本技能就只有这些。
🍩查询方式

如上图所示查询方式的驱动模型,这种方式最简单,这里并不考虑驱动层中的架构,只看驱动层所做的工作。
在驱动程序中构造并注册一个file_operations结构体,里面提供对应的drv_open和drv_read函数,当应用层调用open系统调用时,在驱动层的drv_open函数中配置相应的引脚为输入引脚。
当应用层调用read系统调用时,在驱动层的drv_read函数中读取该GPIO引脚的寄存器,把引脚的状态返回给应用层。
- 读取引脚状态时,直接返回寄存器中的值,没有其他多余的动作。
🍩休眠唤醒方式

如上图所示休眠唤醒方式的驱动模型,在驱动层中的drv_open函数中,除了要把GPIO设置为输入引脚,还有注册GPIO的中断处理函数。
当应用层调用read系统调用时,在驱动层的drv_read驱动函数中:
- 如果有按键数据,则直接返回给应用层。
- 如果没有按键数据,则应用层的APP在内核态休眠。
当用户按下按键时,GPIO中断被触发,导致drv_open中注册的中断服务程序被执行,在中断服务程序中:
- 记录按键数据。
- 唤醒休眠中的应用层APP。
应用层的APP被唤醒以后,继续在内核态运行,即执行驱动层代码,把中断服务程序中记录的按键数据返回给应用层的APP。
- 没有读取到数据时,就会休眠,直到有按键数据到来才被唤醒。
🍩poll方式
上面的休眠-唤醒方式存在一个缺点:如果用户一直没有按下按键,那么应用层的APP就永远休眠阻塞不再执行了,所以可以给APP定个闹钟,这就是poll方式:

如上图所示poll驱动模型,poll是应用层实现多路转接的系统调用接口,在驱动层的file_operations结构体中,同样有一个poll函数指针:

如上图所示file_operations结构体的定义,所以当应用层的APP调用poll系统调用时,会调用到驱动层该结构体中poll函数指针指向的函数。
所以需要我们在驱动层去定义poll函数指针指向的函数,使得整个驱动层符合poll驱动模型。驱动层总体步骤为:
- 注册
file_operations结构体,里面提供open,read,poll等驱动层的函数。 - 应用层APP调用
open时,驱动层的drv_open会将GPIO设置为输入引脚,并且注册中断处理函数。 - 应用层APP调用
poll/select时,意图是查询按键数据是否就绪,并且可以指定一个超时时间:- 当按键数据就绪时,驱动层的
poll向应用层返回就绪状态,APP继续使用read读取按键数据。 - 当按键数据没有就绪时,驱动层的
poll就会在内核态休眠一段时间。
- 当按键数据就绪时,驱动层的
当APP被唤醒时,有两种情况:
- 在休眠期间,硬件按键被按下,按键数据就绪。
- 超时时间到了,硬件按键仍然没有按下,按键数据没有就绪。
被唤醒后进行判断,如果是数据就绪被唤醒,则调用read从按键的寄存器中读取按键数据,如果是超时被唤醒,则不调用read去读取了。
poll/select起到监视事件就绪的作用,驱动层的drv_poll都会告诉应用层APP所监视事件的状态。- APP根据驱动层告知的事件状态进行下一步动作。
🍩异步通知方式

如上图所示异步通知方式,在该模型中,应用层在打开要操作的设备时,要调用fcntl设置其fd的FASYNC标志,此时会调用驱动层的drv_fasync函数:

如上图所示,在file_operations结构体中也有一个fsync函数指针,在该模型中,该指针指向的函数只需要记录当前进程的PID。
除了设置给fd设置FASYNC表示异步通知外,还需要使用signal系统调用注册信号处理函数my_func。
此时该模型的处理步骤为:
- APP调用
open配置GPIO引脚为输入方式,并注册中断服务函数。 - APP调用
fcntl设置fd指向的文件为异步通知方式,并且注册信号处理函数. - 当硬件按键被按下时,中断服务程序会给记录下来的进程PID表示的进程发送信号,信号递达后执行注册的
my_func信号处理函数。 - 在信号处理函数中,调用
read来读取按键数据,此时必然是有按键数据的。
- 在没有按键按下时,APP正常执行,当按键按下后立刻去读取按键数据,使得应用层实现了中断的处理方式。
我们的驱动程序可以实现上述 4 种提供按键驱动的方法,但是驱动程序不应该限制APP使用哪种方法。
- 这就是驱动设计的一个原则:只提供能力,不提供策略。
就是说,APP想用哪种方法都行,驱动程序都可以提供;但是驱动程序不能限制APP使用哪种方法。
🍮查询方式实现按键驱动
前面介绍了按键的四种驱动模型,但是由于后面三种都涉及到中断方面的知识,而到目前为止本喵还没有介绍驱动程序中的中断,所以这里先仅用查询方式实现一下按键驱动程序:

如上图所示,采用简单的驱动层分层模型来实现查询方式的按键驱动层数:
- 应用层
open/read系统调用和驱动层的drv_open/drv_read通过file_operations结构体来建立联系。 - 驱动层上层的
drv_open/drv_read和驱动层下层的board_button_init/board_button_read通过button_opr结构体连建立联系。
驱动层下层的board_button_init/board_button_read由具体的单板提供:
- 驱动层下层的
board_button_init根据设备号确定哪个按键,并将GPIO配置为输入引脚。 - 驱动层下层的
board_button_read根据设备号确定哪个按键,并读取对应寄存器中的值返回引脚电平。
驱动层上层:

如上图所示,在button_operations.h中定义button_operations结构体:
count:表示按键设备个数init:驱动层下层提供的初始化按键设备方法。read:驱动层下层提供的读取按键状态方法。

如上图所示,在button_drv.c中,创建file_operations结构体,并且用drv_open和drv_read初始化open和read函数指针:
- 在
drv_open函数中,使用p_button_opr结构体中的init,根据次设备号进行初始化。 - 在
drv_read函数中,使用p_button_opr结构体中的read,根据次设备号读取按键状态。- 将读取到的按键状态
level拷贝到用户层缓冲区。
- 将读取到的按键状态

如上图所示,在入口函数button_init中使用register_chrdev向内核中注册file_operations结构体,并且获得主设备号。还要创建button_class设备类来提供设备信息。
在出口函数button_exit中,销毁设备类button_class,并且使用unregister_chrdev函数从内核中将前面注册的file_operations移除。

如上图,由于p_button_opr结构体指针是由驱动层下层提供的,所以驱动层上层要提供一个register_button_operations函数给下层,让下层向上层注册p_button_opr结构体。
- 在注册时,还要将所有按键设备使用
device_create在文件系统中创建设备节点文件。 - 在卸载时,使用
device_destroy将文件系统中的所有按键设备文件移除掉。
由于下层在使用这两个函数时会用到上层的button_class类,所以这两个函数需要使用EXPORT_SYMBOL导出给下层,供下层先使用。

如上图,最后完善一下设备信息,告诉内核哪个是入口函数,哪个是出口函数,并且声明该驱动程序使用GPL协议。
驱动层下层:

如上图所示,在驱动层下层的board_button.c文件中,创建button_operations结构体,并进行初始化:
count:按键设备有两个。init:初始化按键设备的函数board_button_init。read:读取按键设备状态的函数board_button_read。
在入口函数中,使用驱动层上层提供的register_button_operations函数将下层创建的my_button_oprs结构体对象注册到上层,供上层使用下层提供的初始化和读取数据的方法。
在出口函数汇中,使用上层提供的unregister_button_operations函数移除在文件系统中创建的设备节点文件。
最后完善一下设备信息。
对于驱动层下层,重点在于初始化和读取数据函数的实现:

如上图所示IMX6ULL按键的电路原理图:
KEY1:与GPIO5_1相连,按键按下时是低电平(0),未按下时是高电平(1)。KEY2:与GPIO4_IO14相连,按键按下时是低电平(0),未按下时是高电平(1)。
- 使能GPIO

如上图所示使能GPIO的寄存器:
CCM_CCGR1:物理地址是0x020C406C,其中的[31,30]控制GPIO5的使能,但是这里保留了,GPIO5默认使能,CCM_CCGR3:物理地址是0x020C4074,其中的[13,12]控制GPIO的使能,当这两个比特位为11时,GPIO4使能。
- 选择GPIO模式

如上图所示IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1寄存器:
- 物理地址是
0x0229000C。 MUX_MODE:这四个bit为101时,表示GPIO5_IO01引脚用作通用GPIO。

如上图所示IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B寄存器:
- 物理地址是
0x020E01B0。 MUX_MODE:这四个bit为0101时,表示GPIO4_IO14引脚用作通用GPIO。
- 设置GPIO方向

如上图所示内存映射表:
GPIO5:该组寄存器的基地址是0x020AC000。GPIO4:该组寄存器的基地址是0x020A8000。

如上图所示GPIO所有寄存器的内存映射表,以GPIO4为例:
- 一共8个寄存器,每组GPIO都是这样。
- 从
DR寄存器开始,到EDGE_SEL寄存器结束,地址从低到高,每个寄存器所占4个字节。
所以定义一个结构体来描述GPIO组中的所有寄存器:

如上图所示结构体,用该结构体创建gpio5和gpio4结构体对象来操作相应的GPIO。

如上图所示GDIR寄存器:
- 对于
GPIO5_1:将gpio5->gdir的bit1设置为0,表示输入。 - 对于
GPIO4_14:将gpio4->gdir的bit14设置为0,表示输入。
- 读取按键状态:

如上图所示PSR寄存器:
- 对于
GPIO5_1:gpio5->psr的bit1为0,表示按键按下,为低电平,为1,表示按键没有按下,为高电平。 - 对于
GPIO4_14:gpio4->gdir的bit14为0,表示按键按下,为低电平,为1,表示按键没有按下,为高电平。
编程

如上图所示,在驱动层下层board_button.c中,将用到的寄存器全部定义出来,并且创建gpio4和gpio5两个结构体变量来表示GPIO。
board_button_init:

如上图所示初始化函数中:
- 将所有涉及到的寄存器都在内存中映射相应的虚拟地址,只映射一次。
- 其中GPIO组进行整体映射,大小为
struct imx6ull_gpio结构体的大小。
- 其中GPIO组进行整体映射,大小为
- 根据次设备号对GPIO口进行初始化,控制相关寄存器。
- 使能GPIO组,设置引脚模式为通用GPIO,设置方向为输入。
board_button_read:

如上图所示读取按键数据的函数,根据次设备号确定读取gpio5还是gpio4中的psr寄存器,然后返回该寄存器中的值。
应用层测试函数:

如上图应用层测试函数,在测试的时候,命令行中输入./button_test /dev/BigMiaomi_button0或者./button_test /dev/BigMiaomi_button1,在mian函数中会使用read系统调用去获取按键状态,最终会调用驱动层下层的board_button_read函数。
- 如果打印1,表示按键没有按下。
- 如果打印0,表示按键按下。

如上图所示Makefile文件中,make以后:
- 会生成
button_test可执行程序,用来测试。 - 会生成
button_drv.ko和board_button.ko两个模块文件,用来安装驱动程序。

如上图所示,在开发板上安装两个按键的驱动程序,可以看到在./dev目录下有BigMiaomi_button0和BigMiaomi_button1两个设备节点。

如上图所示,在开发板上执行测试程序:
- 未在开发板上按下
KEY1和KEY2两个按键时,打印出的值是1,表示高电平,和电路逻辑相符。 - 按下开发板上按下
KEY1和KEY2两个按键时,打印出的值是0,表示低电平,和电路逻辑相符。
🍮总结
要会使用设备树向内核中注册设备节点,并且会对驱动程序做相应的修改。除此之外,要知道APP读取按键的四种方式,以及实现简单的APP按键驱动程序编程。
相关文章:
【Linux驱动】设备树模型的LED驱动 | 查询方式的按键驱动
🐱作者:一只大喵咪1201 🐱专栏:《Linux驱动》 🔥格言:你只管努力,剩下的交给时间! 目录 🍮设备树模型的LED驱动🍩设备树文件🍩驱动程序 …...
GZ075 云计算应用赛题第4套
2023年全国职业院校技能大赛(高职组) “云计算应用”赛项赛卷4 某企业根据自身业务需求,实施数字化转型,规划和建设数字化平台,平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”,拟采用开源OpenSt…...
小型肉制品厂废水处理设备加工厂家
诸城市鑫淼环保小编带大家了解一下小型肉制品厂废水处理设备加工厂家 在小型肉制品厂,处理肉类加工废水是非常重要的环保问题。废水中含有蛋白质、脂肪、悬浮物和有机物等,需要进行合适的处理以减少对环境的污染。以下是一些常见的小型肉制品厂废水处理设…...
SpringBoot整合ElasticSearch实现CRUD操作
本文来说下SpringBoot整合ES实现CRUD操作 文章目录 概述项目搭建ES简单的crud操作本文小结 概述 SpringBoot支持两种技术和es交互。一种的jest,还有一种就是SpringData-ElasticSearch。根据引入的依赖不同而选择不同的技术。反正作为spring全家桶,目前是…...
香橙派--关于jammy-xfce-arm64.f12a43b3e629442a073a7236bf9166ce.tar.lz4的rootfs定制与镜像制作
使用 x64 的 Ubuntu22.04 电脑编译 Linux SDK,即 orangepi-build,支持在安装有 Ubuntu 22.04 的电脑上运行,所以下载 orangepi-build 前,请首先确保自己电脑已安装的 Ubuntu 版本是 Ubuntu22.04。查看电脑已安装的 Ubuntu 版本的命…...
前端八股文(HTML篇)一
目录 1.什么是DOCTYPE,有何用呢? 2.说说对html语义化的理解 3.src和href的区别? 4.title与h1的区别,b与strong的区别,i与em的区别? 5.什么是严格模式与混杂模式? 6.前端页面有哪三层构成,分…...
数据结构与算法python版本之线性结构之无序表抽象数据类型有序链表抽象数据类型和总结
我们知道,列表List是一种简单强大的数据集结构,提供了丰富的操作接口;但是并不是所有的编程语言都提供了List数据类型,有时候需要程序员自己实现。 那么什么是列表呐? 列表是一种数据项按照相对位置存放的数据集&…...
识别pdf中论文标题并重命名PDF名称(2024.1.2,第二次更新)判断标题中是否以空格结尾
63~66行增加语句,判断标题是否以空格结尾 83~85行增加语句,判断选句是否以空格结尾 import os import timeimport fitzdef find_largest_font_sentence(pdf_path):largest_font_size 0largest_font_sentence maxsize0# 打开PDF文件document fitz.ope…...
01.02作业
整理思维导图复习课上代码全局变量,int monster 10000;定义英雄类hero,受保护的属性string name,int hp,int attck;公有的无参构造,有参构造,虚成员函数 void Atk(){blood-0;},法师类继承自英雄…...
WPF+Halcon 培训项目实战(11):HS组件封装
文章目录 前言相关链接项目专栏运行环境匹配图片封装组件新增类库项目选择依赖顺序并添加Nuget修改原本矩形方法运行结果: 对矩形进行抽象封装抽象基类矩形抽象改造 圆形抽象封装代码运行结果 前言 为了更好地去学习WPFHalcon,我决定去报个班学一下。原…...
VUE——IDEA 启动前端工程VS文件启动前端工程
IDEA 启动前端 目录 前言一、打开控制台二、输入npm install三、依赖下载完之后,输入npm run dev,运行前端项目1、IDEA启动前端工程2、文件目录启动前端工程 四、点击http://localhost:8080后续敬请期待 前言 启动已有的vue前端项目 一、打开控制台 选…...
自动驾驶论文
文章目录 一、Convolutional Social Pooling for Vehicle Trajectory Prediction二、QCNet:Query-Centric Trajectory Prediction三、VectorNet: Encoding HD Maps and Agent Dynamics from Vectorized Representation 一、Convolutional Social Pooling for Vehicl…...
Java经典框架之SpringDataJPA
SpringDataJPA Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Spring整合Hibernate 2…...
向爬虫而生---Redis 基石篇3 <拓展List>
前言: 继上一篇向爬虫而生---Redis 基石篇2 <拓展Hash>-CSDN博客.往下继续---挖一挖list 正文: 在Redis中,列表(List)是一个常用的数据结构,尤其在爬虫应用中。例如,可以用列表实现…...
CSS渲染性能优化
✨ 专栏介绍 HTML/CSS专栏合集是一个涵盖HTML和CSS两个方面的栏目。HTML是一种标记语言,用于创建网页的结构和内容,而CSS是一种样式表语言,用于控制网页的外观和布局。 在HTML/CSS专栏合集中,我们将深入探讨HTML和CSS的基础知识…...
【C++入门】类和对象(完)
前言 在谈论C时,常常会涉及到一些高级特性和概念,比如初始化列表、static成员、友元、内部类、匿名对象等。这些概念在C编程中起着非常重要的作用,对于想要深入了解C语言的开发者来说,掌握这些知识是至关重要的。本文,…...
webshell检测方式深度剖析 --- Pixy系列二(数据流分析)
开篇 书接上文,这次我们来聊聊数据流分析,数据流分析的内容非常广泛,我们力求深入浅出通俗易懂,在简短的篇幅内将这一概念描述清楚。 简单来说,数据流分析是一种用来获取相关数据沿着程序执行路径流动的信息分析技术…...
[DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]
文章目录 概要I Introduction小结 概要 提出的架构,双注意力U-Net与特征融合(DAU-FI Net),解决了语义分割中的挑战,特别是在多类不平衡数据集上,这些数据集具有有限的样本。DAU-FI Net 整合了多尺度空间-通…...
使用Triton部署ONNX模型
介绍 适用于各种 AI 工作负载的推理:借助 NVIDIA Triton™,在任何处理器(GPU、CPU 或其他)上,对使用基于任何框架的,经过训练的机器学习模型或深度学习模型,进行推理部署。Triton 是 NVIDIA AI…...
Python访问ElasticSearch
ElasticSearch是广受欢迎的NoSQL数据库,其分布式架构提供了极佳的数据空间的水平扩展能力,同时保障了数据的可靠性;反向索引技术使得数据检索和查询速度非常快。更多功能参见官网介绍 https://www.elastic.co/cn/elasticsearch/ 下面简单罗列…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...

