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

KVM/ARM——基于ARM虚拟化扩展的VMM

1. 前言

ARM架构为了支持虚拟化做了些扩展,称为虚拟化扩展(Virtualization Extensions)。原先为VT-x创建的KVM(Linux-based Kernel Virtual Machine)适配了ARM体系结构,引入了KVM/ARM (the Linux ARM hypervisor)。KVM/ARM没有在hypervisor中引入复杂的核心功能,而是利用了Linux内核中现有的功能进行适配。KVM/ARM与Linux集成,在可移植性和硬件支持方面会有所收益。其实也可以开发独立的bare metal hypervisor,这样可能会有更好地性能,但这种方法在ARM上不太好使。

在下面,我们将描述KVM/ARM的设计如何使得它可以从现有kernel的集成中获益,同时利用硬件虚拟化特性。

2. Split-mode虚拟化

KVM/ARM利用了现有的kernel功能,例如调度器(scheduler),但KVM/ARM如果直接在EL2中运行Linux kernel,会存在两个问题。

第一,linux中与底层体系结构相关的代码是为在kernel模式下工作而编写的,不经过修改就不能在EL2中运行,因为EL2是一个完全不同于普通kernel模式的CPU模式。Linux kernel社区不太可能接受在EL2中运行kernel所需的重大更改。更重要的是,为了保持与没有EL2的硬件的兼容性并将Linux作为Guest OS运行,必须编写低级代码以在两种模式下工作,这可能会导致缓慢而复杂的代码路径。举个简单的例子,页表错误处理程序需要获取导致页表错误的虚拟地址。在EL2中,这个地址存储在与kernel模式不同的寄存器中。

第二,在EL2中运行整个内核会对本机性能产生不利影响。例如,EL2有自己独立的地址空间,但是kernel模式使用两个页表基本寄存器(TTBR)在user地址空间和kernel地址空间之间提供熟悉的3GB/1GB分割,而EL2使用单个页表寄存器,因此不能直接访问地址空间的user空间部分。经常使用的访问user内存的函数需要kernel显式地将用户空间数据映射到kernel地址空间,然后执行必要的拆解和TLB维护操作,从而导致ARM的性能不好。

综上所述,KVM/ARM引入了split-mode虚拟化,这种一种新的hypervisor设计方法,它将核心hypervisor分开,以便它可以跨不同的特权CPU模式运行,从而利用每种CPU模式提供的特定优势和功能。KVM/ARM使用split-mode虚拟化来利用EL2支持的ARM硬件虚拟化,同时利用在kernel模式下运行的现有Linux kernel服务。Split-mode虚拟化允许KVM/ARM与Linux kernel集成,而无需对现有代码库进行重大修改。

这是通过将hypervisor分成两个组件来实现的:lowvisor和highvisor,如图1所示。

图1 KVM/ARM系统架构

Lowvisor的设计是利用EL2中提供的硬件虚拟化支持来提供三个关键功能。首先,lowvisor通过适当的硬件配置来设置正确的执行上下文,并在不同的执行上下文之间实施保护和隔离。Lowvisor直接与硬件保护功能交互,因此非常关键,且代码库需要保持绝对最小。其次,lowvisor从VM执行上下文切到到host执行上下文,反之亦然。Host执行上下文用于运行hypervisor和host Linux kernel。我们将执行上下文称为一个世界,将从一个世界切换到另一个世界称为世界切换(world switch),因为系统的整个状态都发生了变化。由于lowvisor是唯一在EL2中运行的组件,因此只有它可以负责执行世界切换所需的硬件重新配置。第三,lowvisor提供了一个虚拟化Trap处理程序,它处理必须Trap到hypervisor的中断和异常。所有发送到hypervisor的Trap必须首先发送到lowvisor。在世界切换到highvisor完成后,lowvisor只执行所需的最少量的处理,并将要完成的大部分工作推迟到highvisor。

Highvisor作为host linux kernel的一部分在kernel模式下运行。因此,它可以直接利用现有的linux功能,例如调度器,并且可以利用标准的kernel软件数据结构和机制来实现其功能,例如locking机制和memory分配功能。它使高层功能更容易在highvisor中实现。例如,虽然lowvisor提供低级Trap处理程序和低级机制来从一个世界切换到另一个世界,但highvisor处理来自VM和的stage-2页表错误并执行指令仿真。请注意,部分VM以kernel模式运行,就像highvisor一样,但是启用了stage-2转换和Trap到EL2。

因为hypervisor在kernel模式和EL2之间是分开的,所以在VM和highvisor之间的切换涉及到多个模式转换。在运行VM时,向highvisor发出Trap将首先向EL2中lowvisor发出Trap。然后lowvisor将发起另一个Trap去运行highvisor。类似地,从highvisor切换到VM需要从kernel模式切换到EL2,然后切换到VM。因此,在切换到或切换出highvisor时,split-mode虚拟化会产生双重Trap成本。在ARM上,执行这些模式转换到EL2或从EL2转换的唯一方法就是通过Trap。不过,这个额外的Trap在ARM上并不是一个显著的性能成本。

KVM/ARM使用内存映射接口根据需要在highvisor和lowvisor之间共享数据。由于内存管理可能很复杂,它利用了linux中现有内存管理子系统的功能来管理highvisor和lowvisor的内存。但是管理lowvisor的内存涉及到额外的挑战,它需要管理EL2的单独地址空间。一种简单的方法是重用host kernel的页表,并在EL2中使用它们来使地址空间相同。不过这样有问题,因为EL2使用和kernel模式不同的页表格式。因此,highvisor显式地管理EL2页表,将在EL2种执行的任何代码以及highvisor和lowvisor之间共享的任何数据结构映射到EL2和kernel模式中的相同虚拟地址。

3. CPU虚拟化

为了虚拟化CPU,KVM/ARM必须向VM提供一个接口,该接口本质上与底层的实际硬件相同,同时确保hypervisor仍然控制硬件。它包括确保在虚拟机中运行的软件必须与在物理CPU上运行的软件具有对相同寄存器状态的一致访问,以及确保切换VM运行中,与hypervisor及其host kernel相关联的物理硬件状态是一致的。为了不影响VM隔离,寄存器状态可以简单地通过保存VM状态和从VM切换到host时从内存恢复主机状态进行上下文切换,反之亦然。KVM/ARM将对所有其它敏感状态的访问配置为Trap到EL2,因此hypervisor可以模拟它。

表1为在kernel模式和user模式下运行的软件可以看到的寄存器状态,以及KVM/ARM对每个寄存器组的虚拟化方法。Lowvisor有自己专用的配置寄存器,仅供EL2使用。只要硬件支持,在世界切换期间,KVM/ARM上下文会切换寄存器,因为它允许VM直接访问硬件。例如,VM可以直接对stage-1页表基本寄存器进行编程,而不需要Trap到hypervisor,这在大多数Guest OS中是相当常见的操作。KVM/ARM在敏感指令和访问硬件状态时执行Trap和模拟,这些状态可能会影响hypervisor或将有关硬件的信息泄漏给VM,违反了虚拟化原理。例如。如果VM执行导致CPU断电的WFI指令,KVM/ARM会Trap,这样的操作应该只由hypervisor执行,以保持对硬件的控制。

表1 VM和Host的状态

在kernel或user模式下运行虚拟机与在kernel或user模式下运行hypervisor之间的区别取决于EL2在世界切换期间如何配置virtualization extensions。从host切换到VM的过程如下:

1.将所有host GP寄存器存到EL2的堆栈中;

2.给VM配置vGIC;

3.给VM配置timers;

4.将所有host特定的配置寄存器存到EL2堆栈中;

5.将VM的配置寄存器加载到硬件,这个操作不会影响当前执行,因为EL2使用它自己的配置寄存器,这些寄存器从host状态中分离出来。

6.配置EL2以Trap浮点操作(VFP寄存器的lazy context switching),trap中断,trap CPU halt指令(WFI/WFE),trap SMC指令,Trap特定配置寄存器访问和trao调试寄存器访问;

7.将虚拟机特定的ID写入shadow ID寄存器中,这由ARM虚拟化扩展定义,并由虚拟机访问,以代替ID寄存器中的硬件值;

8.设置stage-2页表基寄存器(VTTBR)并启用stage-2地址转换;

9.恢复所有guest GP寄存器;

10.进入user模式或kernel模式;

CPU将留在虚拟机世界,直到有event发生,触发进入EL2的trap。这样的事件可能由上面提到的任何Trap、stage-2页表错误或硬件中断引起。由于event需要highvisor的服务,要么模拟虚拟机预期的硬件行为,要么服务于设备中断,KVM/ARM必须执行另一个世界切换到highvisor和它的host。这需要先到lowvisor,然后再到highvisor。从VM世界切换到host世界需要以下步骤:

1.保存所有VM GP寄存器;

2.关闭stage-2转换;

3.配置EL2不Trap任何寄存器访问和指令;

4.保存所有VM特定的配置寄存器;

5.加载host的配置寄存器到硬件;

6.配置host的timers;

7.保存VM特定的vGIC状态;

8.恢复所有host GP寄存器

9.进入kernel模式;

4. 内存虚拟化

如图2所示,ARM提供了stage-2页表来将Guest物理地址转换为host物理地址。KVM/ARM通过启用stage-2转换,为在虚拟机中运行时为所有内存访问提供内存虚拟化。Stage-2转换只能在EL2中配置,它的使用对VM是完全透明的。Highvisor管理stage-2翻译页表,只允许访问专门为VM分配的内存。其它访问将导致stage-2页表错误,从而Trap到hypervisor。该机制确保虚拟机不能访问hypervisor或其它虚拟机的内存,包括任何敏感数据。在highvisor和lowvisor中运行时禁用stage-2转换,因为highvisor完全控制整个系统并直接管理host物理地址。当hypervisor切换到VM时,它启用stage-2转换并相应地配置stage-2页表基寄存器。尽管highvisor和VM共享相同的CPU模式,但stage-2转换确保highvisor不受VM的任何访问。

图2 ARM的两级页表

KVM/ARM是一个split-mode虚拟化来利用现有的kernel内存分配、页表引用计数和页表操作代码。KVM/ARM通过考虑出错的gPA来处理stage-2页表出错,以及该地址是否属于VM内存映射,KVM/ARM通过简单地调用一个现有的kernel函数(如get_user_pages)为VM分配一个页表,并将分配的页表映射到stage-2页表中的VM。

5. I/O虚拟化

KVM/ARM利用现有的QEMU和Virtio user空间设备模拟来提供I/O虚拟化。在硬件层面上,ARM架构上的所有I/O机制都是基于对MMIO设备区域的load/store操作。除了直接分配给VM的设备,所有硬件MMIO区域不能被虚拟机访问。KVM/ARM使用stage-2转换来确保不能从虚拟机直接访问物理设备。在为VM分配的RAM区域之外的任何访问都将被hypervisor Trap,hypervisor可以根据fault地址将访问路由到QEMU中的特定模拟设备。

6. 中断虚拟化

KVM/ARM利用其与Linux的集成来重用现有的设备驱动程序和相关功能,包括处理中断。当在虚拟机中运行时,KVM/ARM配置CPU将所有硬件中断Trap到EL2。在每个中断中,它执行到hypervisor的世界切换,host处理中断,这样hypervisor就可以保留完全控制硬件资源。当在host和highvisor中运行时,中断直接Trap到kernel模式,避免通过EL2的开销。在这两种情况下,所有硬件中断处理都是通过重用Linux现有的中断处理功能在host中完成的。

但是,虚拟机必须以虚拟中断的形式接收来自模拟设备的通知,multicore Guest OSs必须能够从一个虚拟core发送虚拟IPIs到另一个虚拟xore。KVM/ARM通过vGIC向虚拟机注入虚拟中断,减少EL2的Trap数量。通过编程vGIC hypervisor CPU控制接口中的list registers,虚拟中断被提升到虚拟CPU。KVM/ARM配置stage-2页表,来防止虚拟机访问控制接口,只允许访问vGIC虚拟CPU接口,保证只有hypervisor可以编程控制接口,虚拟机可以直接访问vGIC虚拟接口。然而,Guest OS仍然会尝试访问GIC分发器来配置GIC,并讲IPI从一个虚拟core发送到另一个虚拟core。这样的访问将Trap到hypervisor,hypervisor必须模拟分发程序。

KVM/ARM引入了虚拟分发器,这是GIC分发器的一个软件模型,作为highvisor的一部分。虚拟分发器向User空间公开接口,因此user空间中的模拟设备可以向虚拟分发器发起虚拟中断,并向VM公开与物理GIC分发器相同的MMIO接口。虚拟分发器保存关于每个中断状态的内部软件状态,并在调度VM时使用此状态,编程list registers以注入虚拟中断。例如,如果虚拟CPU0向虚拟CPU1发送一个IPI,分发程序将编程虚拟CPU1的list registers以引发一个虚拟IPI中断给虚拟CPU1。

理想情况下,虚拟分发程序只在必要时访问硬件list registers,因为设备MMIO操作通常比cache内存访问慢得多。当调度不同的VM在物理core上运行时,需要list registers的完整上下文切换,但当简单地在VM和hypervisor之间切换时,则不一定需要上下文切换。例如,如果没有挂起的虚拟中断,则不需要访问任何list register。注意,一旦hypervisor在切换到VM时将一个虚拟中断写入一个list register,那么在切换会hypervisor时,它还必须读回这个list register,因为list register描述了虚拟中断的状态。KVM/ARM的初始未优化版本使用了一种简单的方法,该方法完全切换所有vGIC状态,包括每个世界开关上的list register。

7. 计时器虚拟化

读取计数器和编程计时器是许多操作系统中用于进程调度和定期轮询设备状态的常见操作。例如,Linux读取一个计数器来确定进程是否已经过期,并编程计数器来确保进程没有超出其允许的时间片段。由于各种原因,应用程序工作负载也经常使用计时器。为每个这样的操作Trap到hypervisor可能会导致明显的性能开销,并且允许VM直接访问计时硬件通常意味着放弃对硬件资源的定时控制,因为VM可以禁用计时器并在较长的时间内控制CPU。

KVM/ARM利用ARM的通用定时器的硬件虚拟化特性来允许虚拟机直接访问可读计数器和编程计时器,而不会引起Trap到EL2,同时确保hypervisor仍然控制硬件。由于使用EL2控制对物理计时器的访问,因此任何控制EL2模式的软件都可以访问物理计时器。KVM/ARM通过使用hypervisor中的物理计时器和不允许从虚拟机访问物理计时器来维护硬件控制。作为Guest OS运行的Linux kernel只能访问虚拟计时器,因此可以直接访问计时器硬件,而不会被hypervisor Trap住。

不过由于体系结构的限制,虚拟计时器不能直接引发虚拟中断,而总是引发硬件中断,而硬件中断会Trap到hypervisor。KVM/ARM检测虚拟机的虚拟计时器是否到期,并向虚拟机注入相应的虚拟中断,在highvisor中执行所有硬件ACK和EOI操作。硬件只为每个物理CPU提供一个虚拟计时器,多个虚拟CPU可以再这个硬件实例上复用。为了在这种情况下支持虚拟计时器,KVM/ARM在虚拟机进入hypervisor时检测未过期的计时器,并利用现有的OS功能在虚拟计时器本来触发的时候编程一个软件计时器,让虚拟机处于运行状态。当这样的软件计时器触发时,将执行一个回调函数,该函数使用上面描述的虚拟分发器向VM引发一个虚拟计时器中断。

8. ARM体系结构的改进

根据KVM/ARM开发人员的经验,对ARM架构进行了一系列改进,以避免对KVM/ARM等type-2 hypervisor进行split-mode虚拟化的需要。这些改进包括Virtualization Host Extensions(VHE),它现在是ARM 64-bit架构新版本ARMv8.1的一部分。VHE允许将设计在EL1中运行的OS运行在EL2中,而无需对OS源代码进行实质性修改。

VHE是通过添加一个新的控制位E2H来提供的,该位在系统启动时安装使用VHE的type-2 hypervisor是设置的。如果该位没有设置,ARMv8.1在硬件虚拟化方面与ARMv8相同,保留了与现有hypervisor的向后兼容性。如果该位设置了,那么VHE将开启三个主要特性。

首先,VHE扩展了EL2,向CPU添加额外的物理寄存器状态,这样EL1中可用的任何寄存器和功能也可以在EL2中使用。例如,EL1有两个寄存器:TTBR0_EL1和TTBR1_EL1。第一个用于查找页表中较低VA范围内的虚拟地址,第二个用于较高VA范围内的虚拟地址。它提供了一种在user空间和kernel空间之间分隔虚拟空间的方便、高效地方法。但是,如果没有VHE,EL2只有一个页表基寄存器TTBR0_EL2,这使得在EL2中运行时支持EL1的分隔VA空间存在问题。对于VHE,EL2将有第二个页表基寄存器TTBR1_EL2,从而可以支持分隔VA空间EL2的方式与EL1相同。用于启用与host集成的type-2 hypervisor操作系统去支持在EL2种分割VA空间,这是运行EL2种的host OS所必需的,以便管理user空间和kernel之间的VA空间。

其次,VHE提供了一种机制来透明地访问额外的EL2寄存器状态。简单地提供额外的EL2寄存器不足以在EL2中运行未修改的OS,因为现有的操作系统被写入访问EL1寄存器。例如,Linux使用了TTBR1_EL1,不影响EL2种运行的translation系统。提供额外的寄存器TTBR1_EL2仍然需要修改Linux,以便分别在EL2中运行时使用TTBR1_EL2而不是TTBR1_EL1。为了避免迫使OS供应商给软件增加这种额外的复杂性,VHE允许未经修改的软件在EL2种执行,并使用EL1寄存器访问函数指令编码透明的访问EL2寄存器。例如,当前的操作系统软件用指令MRS x1,TTBR1_EL1读取TTBR1_EL1寄存器。对于VHE,软件仍然执行相同的指令,但是硬件实际访问的是TTBR1_EL2寄存器。只要设置了E2H bit,访问EL1寄存器实际上访问了EL2寄存器,从而透明地重写了对EL2的寄存器访问。添加了一组新的特殊指令来访问EL2中EL1寄存器,hypervisor可以使用这些指令在将EL1中运行的VM之间进行切换。例如,如果hypervisor希望访问Guest的TTBR1_EL1,它将使用指令MRS x1,TTBR_EL21。

第三,VHE扩展了EL2的memory translation能力。在ARMv8种,EL2和EL1使用不同的页表格式,因此编写在EL1种运行的软件必须经过修改才能在EL2中运行。在ARMv8.1中,当设置了E2H位时,EL2页表格式现在与EL1格式兼容。因此,以前在EL1中运行的OS现在可以在EL2中运行而无需修改,因为它可以使用相同的EL1页表格式。

图3显示type-1和type-2 hypervisor程序如何映射到具有VHE的体系结构。


图3 虚拟化扩展(VHE)

Type-1 hypervisor(如Xen)可以明确地设计为在EL2中运行,而无序任何额外的支持,它不设置VHE引入的E2H位,并且EL2的行为与ARMv8完全相同。Type-2 hypervisor(如KVM/ARM)在系统启动时设置了E2H位,host OS kernel只在EL2中运行,不会在EL1中运行。Type-2 hypervisor kernel可以在EL2中不加修改地运行,因为VHE为每个EL1寄存器提供了一个等效的EL2寄存器,并透明地将EL1寄存器访问从EL2重写为EL2寄存器访问,而且EL1和EL2之间的页表格式现在是兼容的。从host user空间到host kernel的转换直接从EL0到EL2进行,例如处理系统调用,如图7.4的箭头所示。从VM到hypervisor的转换现在无须上下文切换EL1状态,因为hypervisor不使用EL1。

相关文章:

KVM/ARM——基于ARM虚拟化扩展的VMM

1. 前言 ARM架构为了支持虚拟化做了些扩展,称为虚拟化扩展(Virtualization Extensions)。原先为VT-x创建的KVM(Linux-based Kernel Virtual Machine)适配了ARM体系结构,引入了KVM/ARM (the Linux ARM hypervisor)。KVM/ARM没有在hypervisor中引入复杂的…...

池化层Pooling Layer

1. 定义 池化是对特征图进行的一种压缩操作,通过在一个小的局部区域内进行汇总统计,用一个值来代表这个区域的特征信息,常用于卷积神经网络(CNN)中。 2. 作用 提取代表性信息的同时降低特征维度,具有平移…...

为AI聊天工具添加一个知识系统 之63 详细设计 之4:AI操作系统 之2 智能合约

本文要点 要点 AI操作系统处理的是 疑问(信念问题)、缺省(逻辑问题)和异常(不可控因素 ) 而 内核 的三大功能 (资源分配/进程管理/任务调度)以及外围的三类接口( CLI、GUI和表面模型的 运行时…...

代码随想录——二叉树(二)

文章目录 前言二叉树最大深度二叉树的最小深度翻转二叉树对称二叉树完全二叉树的节点个数平衡二叉树二叉树的所有路径左叶子之和找左下角的值路径总和从中序与后序序列构造二叉树最大二叉树合并二叉树二叉搜索树中的搜索验证二叉搜索树二叉搜索树的最小绝对差二叉树中的众数二叉…...

一个基于Python+Appium的手机自动化项目~~

本项目通过PythonAppium实现了抖音手机店铺的自动化询价,可以直接输出excel,并带有详细的LOG输出。 1.excel输出效果: 2. LOG效果: 具体文件内容见GitCode: 项目首页 - douyingoods:一个基于Pythonappium的手机自动化项目,实现了…...

深入剖析SpringBoot启动机制:run()方法详尽解读

摘要 本文深入解析SpringBoot的启动机制,以run()方法为核心,逐步追踪并详细解释其关键步骤。首先探讨run()方法的工作原理,然后深入代码层面分析各个关键环节。文章提供刷新后钩子和启动后任务的代码示例,帮助读者理解SpringBoot源…...

deepseek v1手机端部署

在iPhone上部署DeepSeekR1 1. 安装快捷指令: 打开iPhone上的Safari浏览器,访问[这个链接](https://www.icloud.com/shortcuts/e0bc5445c39d45a78b90e1dc896cd010)下载快捷指令。 下载后,按照提示完成安装。 2. 获取并配置API Key&a…...

idea对jar包内容进行反编译

1.先安装一下这个插件java Bytecode Decompiler 2.找到这个插件的路径,在idea的plugins下面的lib文件夹内:java-decompiler.jar。下面是我自己本地的插件路径,以作参考: D:\dev\utils\idea\IntelliJ IDEA 2020.1.3\plugins\java-d…...

KMP算法原理 JAVA实现

KMP算法原理 JAVA实现 一、什么是KMP算法二、为什么需要KMP算法1. 算法背景1.1 暴力匹配过程1.2 暴力匹配的优劣 2. KMP算法的诞生3. next数组3.1 kmp算法的关键 三、求解KMP 一、什么是KMP算法 实际上KMP只是发明这个算法的三个人的英文名首字母短称,KMP本身无意义…...

利用Redis实现数据缓存

目录 1 为啥要缓存捏? 2 基本流程(以查询商铺信息为例) 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除(半自动) 3.3 主动更新(手动) 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…...

基于 RAMS 的数据驱动建模与应用实践:从理论到具体操作

基于 RAMS 的数据驱动建模与应用实践:从理论到具体操作 RAMS(区域大气建模系统)因其模块化设计、高分辨率模拟能力和广泛的应用领域,成为区域大气建模的强大工具。而数据驱动建模技术的崛起,使得 RAMS 的能力得到进一…...

计算机图形学实验练习(实验1.2-4.1AND补充实验12)

实验1.2 OpenGL与着色器编程 1.理论知识 1.1 OpenGL的含义 OpenGL是一种应用程序编程接口(Application Programming Interface,API),它是一种可以对图形硬件设备特性进行访问的软件库。OpenGL最新的4.3版本包含了超过500个不同的命令,可以用于设置所需的对象、图像和操…...

javascript-es6 (一)

作用域(scope) 规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问 局部作用域 函数作用域: 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问 function getSum(){ //函数内部是函数作用…...

uni-app 程序打包 Android apk、安卓夜神模拟器调试运行

1、打包思路 云端打包方案(每天免费次数限制5,最简单,可以先打包尝试一下你的程序打包后是否能用): HBuilderX 发行App-Android云打包 选择Android、使用云端证书、快速安心打包本地打包: HBuilderX …...

yolov11 解读简记

1 文章详细介绍了YOLOv11的架构设计,包括以下几个关键组件: C3k2块:这是YOLOv11引入的一种新型卷积块,替代了之前版本中的C2f块。C3k2块通过使用两个较小的卷积核代替一个大的卷积核,提高了计算效率,同时保…...

CommonAPI学习笔记-1

CommonAPI学习笔记-1 一. 整体结构 CommonAPI分为两层:核心层和绑定层,使用了Franca来描述服务接口的定义和部署,而Franca是一个用于定义和转换接口的框架(https://franca.github.io/franca/)。 ​ 核心层和通信中间…...

从入门到精通:RabbitMQ的深度探索与实战应用

目录 一、RabbitMQ 初相识 二、基础概念速览 (一)消息队列是什么 (二)RabbitMQ 核心组件 三、RabbitMQ 基本使用 (一)安装与环境搭建 (二)简单示例 (三)…...

深入理解若依RuoYi-Vue数据字典设计与实现

深入理解若依数据字典设计与实现 一、Vue2版本主要文件目录 组件目录src/components:数据字典组件、字典标签组件 工具目录src/utils:字典工具类 store目录src/store:字典数据 main.js:字典数据初始化 页面使用字典例子&#xf…...

Cursor 帮你写一个小程序

Cursor注册地址 首先下载客户端 点击链接下载 1 打开微信开发者工具创建一个小程序项目 选择TS-基础模版 官方 2 然后使用Cursor打开小程序创建的项目 3 在CHAT聊天框输入自己的需求 比如 小程序功能描述:吃什么助手 项目名称: 吃什么小程序 功能目标…...

进程控制的学习

目录 1.进程创建 1.1 fork函数 1.2 fork函数返回值 1.3 写时拷贝 1.4 fork 常规用法 1.5 fork 调用失败的原因 2. 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 2.2.1 从main 返回 2.2.2 echo $? 查看进程退出码 2.2.2.1 我们如何得到退出码代表的含…...

一文讲解Java中的接口和抽象类

抽象类和接口有什么区别? 一个类只能继承一个抽象类;但一个类可以实现多个接口。所以我们在新建线程类的时候,一般推荐使用Runnable接口的方式,这样线程类还可以继承其他类,而不单单是Thread类;抽象类符合…...

Vue 3 30天精进之旅:Day 05 - 事件处理

引言 在前几天的学习中,我们探讨了Vue实例、计算属性和侦听器。这些概念为我们搭建了Vue应用的基础。今天,我们将专注于事件处理,这是交互式Web应用的核心部分。通过学习如何在Vue中处理事件,你将能够更好地与用户进行交互&#…...

STM32完全学习——RT-thread在STM32F407上移植

一、写在前面 关于源码的下载,以及在KEIL工程里面添加操作系统的源代码,这里就不再赘述了。需要注意的是RT-thread默认里面是会使用串口的,因此需要额外的进行串口的初始化,有些人可能会问,为什么不直接使用CubMAX直接…...

Shodan Dorks安装指南,通过Shodan搜索漏洞

Shodan Dorks是一种基于Shodan的工具,不知道Shodan是什么的不必阅读下面的内容。简单的说就是,利用预定义的查询(dorks),通过Shodan轻松搜索漏洞和机密信息。 推荐渗透测试人员自行测试。 安装方法: 1.确…...

poi在word中打开本地文件

poi版本 5.2.0 方法1:使用XWPFFieldRun(推荐) 比如打开当前相对路径的aaaaa.docx XWPFFieldRun run paragraph.createFieldRun();CTRPr ctrPr run.getCTR().addNewRPr();CTFonts font ctrPr.addNewRFonts();// 设置字体font.setAscii(&quo…...

Linux查看服务器的内外网地址

目录: 1、内网地址2、外网地址3、ping时显示地址与真实不一致 1、内网地址 ifconfig2、外网地址 curl ifconfig.me3、ping时显示地址与真实不一致 原因是dns缓存导致的,ping这种方法也是不准确的,有弊端不建议使用,只适用于测试…...

OAuth1和OAuth2授权协议

OAuth 1 授权协议 1. 概述 OAuth1 是 OAuth 标准的第一个正式版本,它通过 签名和令牌 的方式,实现用户授权第三方访问其资源的功能。在 OAuth1 中,安全性依赖于签名机制,无需传递用户密码。 2. 核心特性 使用 签名&#xff08…...

DeepSeek学术题目选择效果怎么样?

论文选题 一篇出色的论文背后,必定有一个“智慧的选题”在撑腰。选题足够好文章就能顺利登上高水平期刊;选题不行再精彩的写作也只能“当花瓶”。然而许多宝子们常常忽视这个环节,把大量时间花在写作上,选题时却像抓阄一样随便挑一…...

数据结构(一)顺序表和链表

目录 1. 时间复杂度和空间复杂度 2. 顺序表 3. 链表 1. 时间复杂度和空间复杂度 如何估算一个算法的效率高低一般就是使用到时间复杂度和空间复杂度; 时间复杂度是评价一个算法运行快慢的, 而空间复杂度是算法额外需要空间大小. 1.1 时间复杂度的计算: 准确来说时间复杂度是…...

单相可控整流电路——单相桥式全控整流电路

以下是关于单相桥式整流电路的介绍: 电路构成(带阻性负载的工作情况) - 二极管:是电路0的核心元件,通常采用四个同型号或根据需求选择不同型号的二极管,如1N4001、1N4007等,如图Vt1和Vt4是一对…...

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展 在人工智能领域,大模型的竞争一直备受关注。最近,DeepSeek 团队发布了 DeepSeek-R1 模型,并开源了模型权重,这一举动无疑为 AI 领域带来了新的活力。今…...

【Maui】提示消息的扩展

文章目录 前言一、问题描述二、解决方案三、软件开发(源码)3.1 消息扩展库3.2 消息提示框使用3.3 错误消息提示使用3.4 问题选择框使用 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移…...

001 mybatis入门

文章目录 mybatis是什么ORM是什么ORM框架和MyBatis的区别#{}和${}的区别编码流程UserDaoImpl.javaUserDao.javaUser.javadb.propertiesSqlMapConfig.xmlUserMapper.xmlMybatisTest.javapom.xmluser.sql 表现层 SpringMVC 业务层 Spring 持久层 Mybatis https://mybatis.org/myb…...

tomcat的accept-count、max-connections、max-threads三个参数的含义

tomcat的accept-count、max-connections、max-threads三个参数的含义 tomcat的accept-count、max-connections、max-threads三个参数的含义 max-connections:最大连接数 最大连接数是指,同一时刻,能够连接的最大请求数 需要注意的是&#x…...

8.2 从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元

从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元 引言:AI 的多模态跃迁 随着人工智能技术的快速发展,我们正迈入一个新的智能交互时代。传统的 AI 模型主要聚焦于文本处理,而多模态 AI 模型如 GPT-4 with Vision(GPT-4V) 则能够同时处理图像和文本。GPT-4…...

.strip()用法

.strip("") 是 Python 字符串方法 strip() 的一个用法,它会去除字符串两端指定字符集中的字符。 基本语法: string.strip([chars])string: 这是你要操作的字符串。chars: 可选参数,表示你想要去除的字符集(默认为空格…...

蓝桥杯例题三

无论前方困难如何重重,我们都要坚定信念,勇往直前。面对挑战和困境,不要退缩,不要放弃,要坚持走下去。当我们感到疲惫时,要告诉自己:“我可以,我一定行!”相信自己的实力…...

关于pygame窗口输入法状态异常切换现象的分析报告

一、问题描述 1.1 需求说明 我们准备使用Pygame开发一个键盘输入测试程序,需要确保输入时窗口始终处于英文输入模式,也就是禁止中文输入; 1.2 现象描述 控制台种显示,程序在初始化时,会有两次IMM状态切换操作&…...

【JavaEE进阶】应用分层

目录 🎋序言 🍃什么是应用分层 🎍为什么需要应用分层 🍀如何分层(三层架构) 🎄MVC和三层架构的区别和联系 🌳什么是高内聚低耦合 🎋序言 通过上⾯的练习,我们学习了SpringMVC简单功能的开…...

两数相加:链表操作的基础与扩展

两数相加:链表操作的基础与扩展 引言 链表(Linked List)是一种灵活且高效的数据结构,特别适用于动态增删操作。无论是初学者还是资深程序员,链表的基本操作都是算法学习中的重要一环。而 “两数相加” 问题则是链表操…...

ChatGPT从数据分析到内容写作建议相关的46个提示词分享!

在当今快节奏的学术环境中,研究人员面临着海量的信息和复杂的研究任务。幸运的是,随着人工智能技术的发展,像ChatGPT这样的先进工具为科研人员提供了强大的支持。今天就让我们一起探索如何利用ChatGPT提升研究效率进一步优化研究流程。 ChatG…...

解析“in the wild”——编程和生活中的俚语妙用

解析“in the wild”——编程和生活中的俚语妙用 看下面的技术文章中遇到 in the wild这个词,想要研究一下,遂产生此文。 Are there ever pointers to pointers to pointers? There is an old programming joke which says you can rate C programmers…...

rocketmq原理源码分析之控制器模式- dledger

简介 RocketMQ 4.5 版本之前,RocketMQ 的broker是 Master/Slave部署架构,一组 broker 有一个 Master ,有0到若干Slave,Slave复制Master消息存储,随时替代下线的Master。Master/Slave部署架构提供一定的高可用性&#x…...

Hello Moto

“Hello Moto” 是摩托罗拉(Motorola)的一句经典广告口号,用于推广其品牌和产品,特别是在手机领域。以下是它的含义和背景: 1. 品牌宣传的标志性语句 直白含义:简单地向摩托罗拉打招呼(“Hell…...

存储基础 -- SCSI命令格式与使用场景

SCSI命令格式与使用场景 1. SCSI命令描述符块(CDB) 1.1 CDB基本概念 SCSI命令通过**命令描述符块(CDB, Command Descriptor Block)**表示。 CDB长度:SCSI命令根据使用场景有不同长度的CDB,常见的有6字节…...

ceph基本概念,架构,部署(一)

一、分布式存储概述 1.存储分类 存储分为封闭系统的存储和开放系统的存储,而对于开放系统的存储又被分为内置存储和外挂存储。 外挂存储又被细分为直连式存储(DAS)和网络存储(FAS),而网络存储又被细分网络接入存储(NAS)和存储区域网络(SAN)等。 DAS(D…...

CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据)

CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据) 目录 CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据)预测效果基本介绍CNN-GRU卷积门控循环单元时间序列预测一、引言1.1、研究背景与意义1.2、研究现状1…...

Ubuntu 顶部状态栏 配置,gnu扩展程序

顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏,或者使用Just Perfection 直…...

React应用深度优化与调试实战指南

一、渲染性能优化进阶 1.1 精细化渲染控制 typescript 复制 // components/HeavyComponent.tsx import React, { memo, useMemo } from react;interface Item {id: string;complexData: {// 复杂嵌套结构}; }const HeavyComponent memo(({ items }: { items: Item[] }) &g…...

Spring中的事件和事件监听器是如何工作的?

目录 一、事件(Event) 二、事件发布器(Event Publisher) 三、事件监听器(Event Listener) 四、使用场景 五、总结 以下是关于Spring中的事件和事件监听器的介绍与使用说明,结合了使用场景&…...