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中引入复杂的…...

Windows系统中Docker可视化工具对比分析,Docker Desktop,Portainer,Rancher
Docker可视化工具对比分析,Docker Desktop,Portainer,Rancher Windows系统中Docker可视化工具对比分析1. 工具概览2. Docker Desktop官网链接:主要优点:主要缺点:版本更新频率: 3. Portainer官网…...

【架构面试】二、消息队列和MySQL和Redis
MQ MQ消息中间件 问题引出与MQ作用 常见面试问题:面试官常针对项目中使用MQ技术的候选人提问,如如何确保消息不丢失,该问题可考察候选人技术能力。MQ应用场景及作用:以京东系统下单扣减京豆为例,MQ用于交易服和京豆服…...

算法【完全背包】
完全背包与01背包的区别仅在于每种商品可以选取无限次。时间复杂度O(物品数量 * 背包容量) 下面通过题目加深理解。 题目一 测试链接:疯狂的采药 - 洛谷 分析:这是一道完全背包的模板题。对于第i个物品的可能性展开也有两种,第一种是不取第…...

二叉树的遍历
有一个结点的二叉树。给出每个结点的两个子结点编号,建立一棵二叉树,如果是叶子结点,则输入 0 0。 建好树这棵二叉树之后,依次求出它的前序、中序、后序列遍历。 输入格式: 第一行一个整数n ,表示结点数。 之后n 行…...

1.31 实现五个线程的同步
1.使用互斥锁实现 1>程序代码 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include &l…...

three.js+WebGL踩坑经验合集(6.1):负缩放,负定矩阵和行列式的关系(2D版本)
春节忙完一轮,总算可以继续来写博客了。希望在春节假期结束之前能多更新几篇。 这一篇会偏理论多一点。笔者本没打算在这一系列里面重点讲理论,所以像相机矩阵推导这种网上已经很多优质文章的内容,笔者就一笔带过。 然而关于负缩放…...

【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
本文项目编号 T 165 ,文末自助获取源码 \color{red}{T165,文末自助获取源码} T165,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

《大数据时代“快刀”:Flink实时数据处理框架优势全解析》
在数字化浪潮中,数据呈爆发式增长,实时数据处理的重要性愈发凸显。从金融交易的实时风险监控,到电商平台的用户行为分析,各行业都急需能快速处理海量数据的工具。Flink作为一款开源的分布式流处理框架,在这一领域崭露头…...

antdesignvue统计数据源条数、计算某列合计值、小数计算不精确多了很多小数位
1.在</a-table>下方加如下代码 <div>数据总条数:{ {tableData.length}}       <template>A列合计:{ {sum}}</template> </div> 注:tableData为<a-tabl…...

02.05、链表求和
02.05、[中等] 链表求和 1、题目描述 给定两个用链表表示的整数,每个节点包含一个数位。 这些数位是反向存放的,也就是个位排在链表首部。 编写函数对这两个整数求和,并用链表形式返回结果。 2、解题思路 本题要求对两个链表表示的整数…...

dmfldr实战
dmfldr实战 本文使用达梦的快速装载工具,对测试表进行数据导入导出。 新建测试表 create table “BENCHMARK”.“TEST_FLDR” ( “uid” INTEGER identity(1, 1) not null , “name” VARCHAR(24), “begin_date” TIMESTAMP(0), “amount” DECIMAL(6, 2), prim…...

Kafka 副本机制(包含AR、ISR、OSR、HW 和 LEO 介绍)
文章目录 Kafka 副本机制(包含AR、ISR、OSR、HW 和 LEO 介绍)1. 副本的基本概念2. 副本同步和一致性2.1 AR(Assigned Replicas)2.2 ISR(In-Sync Replicas)2.3 OSR(Out-of-Sync Replicas…...

爬虫基础(二)Web网页的基本原理
一、网页的组成 网页由三部分构成:HTML、JavaScript、CSS。 (1)HTML HTML 相当于网页的骨架,它通过使用标签来定义网页内容的结构。 举个例子: 它把图片标签为img、把视频标签为video,然后组合到一个界面…...

外网访问禅道软件项目管理系统
禅道项目管理软件是一款国产的开源免费项目管理软件,专注于研发项目管理,旨在帮助企业或团队提高项目管理的效率和质量。 本文将详细的介绍如何在 Windows 系统电脑端下载运行禅道软件项目管理系统,并且结合路由侠内网穿透实现外网访问本地的…...

Python 梯度下降法(五):Adam Optimize
文章目录 Python 梯度下降法(五):Adam Optimize一、数学原理1.1 介绍1.2 符号说明1.3 实现流程 二、代码实现2.1 函数代码2.2 总代码2.3 遇到的问题2.4 算法优化 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法(五&a…...

笔试-二进制
应用题 将符合区间[l,r]内的十进制整数转换为二进制表示,请问不包含“101”的整数个数是多少? 实现 l int(input("请输入下限l,其值大于等于1:")) r int(input("请输入上限r,其值大于等于l&#x…...

springboot 2.7.6 security mysql redis jwt配置例子
数据库结构用的是若依的数据库基本结构,ruoyi.vip。 总体参考了文章:https://blog.csdn.net/qq_45847507/article/details/126681110 本文章只包含不同的地方,相同的不再赘述。 1、创建spring工程,jdk1.8,maven。 pom.xml中依赖部…...

FreeRTOS从入门到精通 第十六章(任务通知)
参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili 一、任务通知简介 1、概述 (1)任务通知顾名思义是用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 (2&#…...

TensorFlow 简单的二分类神经网络的训练和应用流程
展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括: 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中,数据准备是通过两个 Numpy 数…...

无人机图传模块 wfb-ng openipc-fpv,4G
openipc 的定位是为各种模块提供底层的驱动和linux最小系统,openipc 是采用buildroot系统编译而成,因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢?因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来,从而…...

.cc扩展名是什么语言?C语言必须用.c为扩展名吗?主流编程语言扩展名?Java为什么不能用全数字的文件名?
.cc扩展名是什么语言? .cc是C语言使用的扩展名,一种说法是它是c with class的简写,当然C语言使用的扩展名不止.cc和.cpp, 还包含.cxx, .c, .C等,这些在不同编译器系统采用的默认设定不同,需要区分使用。当然,编译器提…...

【MyDB】4-VersionManager 之 3-死锁及超时检测
【MyDB】4-VersionManager 之 3-死锁及超时检测 死锁及超时检测案例背景LockTable锁请求与等待管理 addvm调用addputIntoList,isInList,removeFromList 死锁检测 hasDeadLock方法资源释放与重分配 参考资料 死锁及超时检测 本章涉及代码:top/…...

【Linux】使用管道实现一个简易版本的进程池
文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程: 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…...

【OpenGL】OpenGL游戏案例(二)
文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性,在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据,实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...

28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正
这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能:报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。 一、需求 数据统计方面&…...

Java 泛型<? extends Object>
在 Java 泛型中,<? extends Object> 和 <?> 都表示未知类型,但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性,使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型,以消除编译时…...

FPGA|使用quartus II通过AS下载POF固件
1、将开发板设置到AS下载挡位,或者把下载线插入到AS端口 2、打开quartus II,选择Tools→Programmer→ Mode选择Active Serial Programming 3、点击左侧Add file…,选择 .pof 文件 →start 4、勾选program和verify(可选࿰…...

“新月之智”智能战术头盔系统(CITHS)
新月人物传记:人物传记之新月篇-CSDN博客 相关文章链接(更新): 星际战争模拟系统:新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 目录 一、引言 二、智能头盔控制系统概述 三、系统架…...

php:代码中怎么搭建一个类似linux系统的crontab服务
一、前言 最近使用自己搭建的php框架写一些东西,需要用到异步脚本任务的执行,但是是因为自己搭建的框架没有现成的机制,所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦࿰…...