StratoVirt 的 vCPU 拓扑(SMP)
CPU 拓扑用来表示 CPU 在硬件层面的组合方式,本文主要讲解 CPU 拓扑中的 SMP(Symmetric Multi-Processor,对称多处理器系统)架构,CPU 拓扑还包括其他信息,比如:cache 等,这些部分会在后面进行补充。CPU 拓扑除了描述 CPU 的组成关系外,还为内核的调度器提供服务,从而提供更好的性能。在 StratoVirt 中,支持 CPU 拓扑为后续的 CPU 热插拔开发打下一个基础。
常见的 CPU SMP 结构是:
Socket --> die --> cluster --> core --> thread
-
socket:对应主板上的 CPU 插槽
-
die:处理器在生产过程中,从晶圆上切割下来的一个个小方块,Die 之间的组件是通过片内总线互联的。
-
cluster:簇,大核或者小核的一种组合
-
core:表示独立的物理 CPU
-
thread:逻辑 CPU,英特尔超线程技术引入的新概念
CPU 拓扑的获取原理
因为 x86 和 ARM 的拓扑获取方式不同,下面将会分开进行介绍。
x86
在 x86 架构下面,操作系统会通过读取 CPUID 来获取 CPU 拓扑结构。在 x86 体系结构中,CPUID 指令(由 CPUID 操作码标识)是处理器补充指令(其名称源自 CPU 标识),允许软件发现处理器的细节。程序可以使用 CPUID 来确定处理器类型。
CPUID 隐式使用 EAX 寄存器来确定返回的信息的主要类别,这被称为 CPUID 叶。跟 CPU 拓扑相关的 CPUID 叶分别是:0BH 和 1FH。1FH 是 0BH 的扩展,可以用来表示更多的层级。Intel 建议先检查 1FH 是否存在,如果 1FH 存在会优先使用它。当 EAX 的值被初始化为 0BH 的时候,CPUID 会在 EAX,EBX,ECX 和 EDX 寄存器中返回 core/logical 处理器拓扑信息。这个函数(EAX=0BH)要求 ECX 同时被初始化为一个 index,这个 index 表示的是在 core 层级还是 logical processor 层级。OS 调用这个函数是按 ECX=0,1,2..n 这个顺序调用的。返回处理器拓扑级别的顺序是特定的,因为每个级别报告一些累积数据,因此一些信息依赖于从先前级别检索到的信息。在 0BH 下,ECX 可以表示的层级有:SMT 和 Core,在 1FH 下,可以表示的层级有:SMT,Core,Module,Tile 和 Die。
下表是一个更详细的一个解释:
Initial EAX Value | Information Provided about the Processor |
---|---|
0BH | EAX Bits 04 - 00: Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type*. All logical processors with the same next level ID share current level. Bits 31 - 05: Reserved. EBX Bits 15 - 00: Number of logical processors at this level type. The number reflects configuration as shipped by Intel. Bits 31- 16: Reserved. ECX Bits 07 - 00: Level number. Same value in ECX input. Bits 15 - 08: Level type. Bits 31 - 16: Reserved. EDX Bits 31- 00: x2APIC ID the current logical processor. |
1FH | EAX Bits 04 - 00: Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type*. All logical processors with the same next level ID share current level. Bits 31 - 05: Reserved. EBX Bits 15 - 00: Number of logical processors at this level type. The number reflects configuration as shipped by Intel. Bits 31- 16: Reserved. ECX Bits 07 - 00: Level number. Same value in ECX input. Bits 15 - 08: Level type. Bits 31 - 16: Reserved. EDX Bits 31- 00: x2APIC ID the current logical processor |
来源: Intel 64 and IA-32 Architectures Software Developer's Manual
ARM
在 ARM 架构下,如果操作系统是依靠 Device Tree 启动的,则会通过 Device Tree 去获取 CPU 拓扑。如果是以 ACPI 的方式启动的话,操作系统会通过解析 ACPI 的 PPTT 表去获取 CPU 拓扑结构。
ACPI——PPTT
ACPI 是 Advanced Configuration and Power Interface (高级配置和电源接口)的缩写,ACPI 是一种与体系结构无关的电源管理和配置框架。这个框架建立了一个硬件寄存器集合来定义电源状态。ACPI 是操作系统和固件之间的一个中间层,是他们两者之间的一个接口。ACPI 定义了两种数据结构:data tables 和 definition blocks。data tables 用于存储给设备驱动使用的 raw data。definition blocks 由一些字节码组成,这些码可以被解释器执行。
为了使硬件供应商在选择其实施时具有灵活性,ACPI 使用表格来描述系统信息、功能和控制这些功能的方法。这些表列出了系统主板上的设备或无法使用其他硬件标准检测或电源管理的设备,以及 ACPI 概念中所述的功能。它们还列出了系统功能,如支持的睡眠电源状态、系统中可用的电源平面和时钟源的说明、电池、系统指示灯等。这使 OSPM 能够控制系统设备,而不需要知道系统控制是如何实现的。
PPTT 表就是其中的一个表格,PPTT 表全称是 Processor Properties Topology Table,处理器属性拓扑表用于描述处理器的拓扑结构,该表还可以描述附加信息,例如处理器拓扑中的哪些节点构成物理包。
下表是 PPTT 表的结构,包含一个表头和主体,表头和其他的 ACPI 表差别不大。其中 Signature
用于表示这是 PPTT 表,Length
是整张表的大小,其他的信息可以查看下面的这张表。表的主体是一系列处理器拓扑结构。
下面的表表示处理器层级节点结构,表示处理器结构的话 Type
要设置为 0,Length
表示这个节点的字节数。Flags
用来描述跟处理器相关的信息,详细的看后面关于 Flags
的详细信息。Parent
用于指向这个节点的上一级节点,存放的是一个偏移量地址
下表是 Flags
的结构,Flags
占据 4 个字节的长度。Physical package
:如果处理器拓扑的此节点表示物理封装的边界,则设置 Physical package
为 1。如果处理器拓扑的此实例不表示物理软件包的边界,则设置为 0。Processor is a Thread
:对于叶条目:如果代表此处理器的处理元素与兄弟节点共享功能单元,则必须将其设置为 1。对于非叶条目:必须设置为 0。Node is a Leaf
:如果节点是处理器层次结构中的叶,则必须设置为 1。否则必须设置为 0。
参考:https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-properties-topology-table-pptt
Device Tree
Device Tree 是一种描述硬件的数据结构。内核的启动程序会将设备树加载入内存中,然后通过解析 Device Tree 来获取硬件细节。Device Tree 是树形结构,由一系列被命名的节点和属性组成,节点可以包含子节点,它们之间的关系构成一棵树。属性就是 name 和 value 的键值对。
一个典型的设备树如下图:
ARM 的 CPU 拓扑是定义在 cpu-map 节点内,cpu-map 是 cpu 节点的子节点。在 cpu-map 节点里可以包含三种子节点:cluster 节点,core 节点,thread 节点。整个 dts 的例子如下:
cpus {#size-cells = <0>;#address-cells = <2>;cpu-map {cluster0 {cluster0 {core0 {thread0 {cpu = <&CPU0>;};thread1 {cpu = <&CPU1>;};};core1 {thread0 {cpu = <&CPU2>;};thread1 {cpu = <&CPU3>;};};};cluster1 {core0 {thread0 {cpu = <&CPU4>;};thread1 {cpu = <&CPU5>;};};core1 {thread0 {cpu = <&CPU6>;};thread1 {cpu = <&CPU7>;};};};};};//...
};
参考:https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/topology.txt
图来源:https://www.devicetree.org/specifications/
StratoVirt 具体实现
CPUID
首先我们需要计算每个拓扑结构唯一的 topology ID,然后获取或者自己建立相对应的 CPUID entry,当 entry 的 function 的值等于 0xB 和 0X1F 的时候,我们需要根据 CPUID 的规范去设置相对应的 EAX, EBX, ECX 的值。EAX 设置为拓扑 ID,EBX 用来表示那个层级的有几个逻辑处理器,ECX 表示层级号。0xB 需要配置 index 等于 0,1 对应的值,0x1F 需要配置 index 等于 0,1,2 对应的值。下面是相对应的代码:
// cpu/src/x86_64/mod.rs
const ECX_INVALID: u32 = 0u32 << 8;
const ECX_THREAD: u32 = 1u32 << 8;
const ECX_CORE: u32 = 2u32 << 8;
const ECX_DIE: u32 = 5u32 << 8;impl X86CPUState {fn setup_cpuid(&self, vcpu_fd: &Arc<VcpuFd>) -> Result<()> {// 计算 topology IDlet core_offset = 32u32 - (self.nr_threads - 1).leading_zeros();let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset;let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset;// 获取 KVM 的 fd 和 获取它支持的 CPUID entriesfor entry in entries.iter_mut() {match entry.function {// ...0xb => {// Extended Topology Enumeration Leafentry.edx = self.apic_id as u32;entry.ecx = entry.index & 0xff;match entry.index {0 => {entry.eax = core_offset;entry.ebx = self.nr_threads;entry.ecx |= ECX_THREAD;}1 => {entry.eax = pkg_offset;entry.ebx = self.nr_threads * self.nr_cores;entry.ecx |= ECX_CORE;}_ => {entry.eax = 0;entry.ebx = 0;entry.ecx |= ECX_INVALID;}}}// 0x1f 扩展,支持 die 层级0x1f => {if self.nr_dies < 2 {entry.eax = 0;entry.ebx = 0;entry.ecx = 0;entry.edx = 0;continue;}entry.edx = self.apic_id as u32;entry.ecx = entry.index & 0xff;match entry.index {0 => {entry.eax = core_offset;entry.ebx = self.nr_threads;entry.ecx |= ECX_THREAD;}1 => {entry.eax = die_offset;entry.ebx = self.nr_cores * self.nr_threads;entry.ecx |= ECX_CORE;}2 => {entry.eax = pkg_offset;entry.ebx = self.nr_dies * self.nr_cores * self.nr_threads;entry.ecx |= ECX_DIE;}_ => {entry.eax = 0;entry.ebx = 0;entry.ecx |= ECX_INVALID;}}}// ...}}
}
PPTT
根据 ACPI PPTT 表的标准来构建,我们需要计算每个节点的偏移值用于其子节点指向它。我们还需要计算每个节点的 uid,uid 初始化为 0,每增加一个节点 uid 的值加一。还需要根据 PPTT 表的标准计算 Flags 的值。最后需要计算整张表的大小然后修改原来的长度的值。
// machine/src/standard_vm/aarch64/mod.rs
impl AcpiBuilder for StdMachine {fn build_pptt_table(&self,acpi_data: &Arc<Mutex<Vec<u8>>>,loader: &mut TableLoader,) -> super::errors::Result<u64> {// ...// 配置 PPTT 表头// 添加 socket 节点for socket in 0..self.cpu_topo.sockets {// 计算到起始地址的偏移量let socket_offset = pptt.table_len() - pptt_start;let socket_hierarchy_node = ProcessorHierarchyNode::new(0, 0x2, 0, socket as u32);// ...for cluster in 0..self.cpu_topo.clusters {let cluster_offset = pptt.table_len() - pptt_start;let cluster_hierarchy_node =ProcessorHierarchyNode::new(0, 0x0, socket_offset as u32, cluster as u32);// ...for core in 0..self.cpu_topo.cores {let core_offset = pptt.table_len() - pptt_start;// 判断是否需要添加 thread 节点if self.cpu_topo.threads > 1 {let core_hierarchy_node =ProcessorHierarchyNode::new(0, 0x0, cluster_offset as u32, core as u32);// ...for _thread in 0..self.cpu_topo.threads {let thread_hierarchy_node =ProcessorHierarchyNode::new(0, 0xE, core_offset as u32, uid as u32);// ...uid += 1;}} else {let thread_hierarchy_node =ProcessorHierarchyNode::new(0, 0xA, cluster_offset as u32, uid as u32);// ...uid += 1;}}}}// 将 PPTT 表添加到 loader 中}
}
Device Tree
StratoVirt 的 microvm 使用 device tree 启动,所以我们需要配置 device tree 中的 cpus 节点下的 cpu-map 来使 microvm 支持解析 CPU 拓扑。在 StratoVirt 中,我们支持两层 cluster。我们使用了多层循环来创建这个 tree,第一层是创建第一层 cluster,第二层对应创建第二层的 cluster,第三层创建 core,第四层创建 thread。
impl CompileFDTHelper for LightMachine {fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> {// 创建 cpus 节点// ...// Generate CPU topology// 创建 cpu-map 节点let cpu_map_node_dep = fdt.begin_node("cpu-map")?;// 创建第一层 cluster 节点for socket in 0..self.cpu_topo.sockets {let sock_name = format!("cluster{}", socket);let sock_node_dep = fdt.begin_node(&sock_name)?;// 创建第二层 cluster 节点for cluster in 0..self.cpu_topo.clusters {let clster = format!("cluster{}", cluster);let cluster_node_dep = fdt.begin_node(&clster)?;// 创建 core 节点for core in 0..self.cpu_topo.cores {let core_name = format!("core{}", core);let core_node_dep = fdt.begin_node(&core_name)?;// 创建 thread 节点for thread in 0..self.cpu_topo.threads {let thread_name = format!("thread{}", thread);let thread_node_dep = fdt.begin_node(&thread_name)?;// 计算 cpu 的 id// let vcpuid = ...// 然后添加到节点中}fdt.end_node(core_node_dep)?;}fdt.end_node(cluster_node_dep)?;}fdt.end_node(sock_node_dep)?;}fdt.end_node(cpu_map_node_dep)?;Ok(())}
}
这个代码构建出来设备树的结构和前面原理中展示的结构基本一致
验证方法
我们可以通过下面的命令启动一个虚拟机,smp
参数用来配置 vCPU 拓扑
sudo ./target/release/stratovirt \-machine virt \-kernel /home/hwy/std-vmlinux.bin.1 \-append console=ttyAMA0 root=/dev/vda rw reboot=k panic=1 \-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true \-drive file=/home/hwy/openEuler-22.03-LTS-stratovirt-aarch64.img,id=rootfs,readonly=false \-device virtio-blk-pci,drive=rootfs,bus=pcie.0,addr=0x1c.0x0,id=rootfs \-qmp unix:/var/tmp/hwy.socket,server,nowait \-serial stdio \-m 2048 \-smp 4,sockets=2,clusters=1,cores=2,threads=1
接着,我们可以通过观察 /sys/devices/system/cpu/cpu0/topology
下面的文件来查看配置的 topology。
[root@StratoVirt topology] ll
total 0
-r--r--r-- 1 root root 64K Jul 18 09:04 cluster_cpus
-r--r--r-- 1 root root 64K Jul 18 09:04 cluster_cpus_list
-r--r--r-- 1 root root 64K Jul 18 09:04 cluster_id
-r--r--r-- 1 root root 64K Jul 18 09:04 core_cpus
-r--r--r-- 1 root root 64K Jul 18 09:04 core_cpus_list
-r--r--r-- 1 root root 64K Jul 18 09:01 core_id
-r--r--r-- 1 root root 64K Jul 18 09:01 core_siblings
-r--r--r-- 1 root root 64K Jul 18 09:04 core_siblings_list
-r--r--r-- 1 root root 64K Jul 18 09:04 die_cpus
-r--r--r-- 1 root root 64K Jul 18 09:04 die_cpus_list
-r--r--r-- 1 root root 64K Jul 18 09:04 die_id
-r--r--r-- 1 root root 64K Jul 18 09:04 package_cpus
-r--r--r-- 1 root root 64K Jul 18 09:04 package_cpus_list
-r--r--r-- 1 root root 64K Jul 18 09:01 physical_package_id
-r--r--r-- 1 root root 64K Jul 18 09:01 thread_siblings
-r--r--r-- 1 root root 64K Jul 18 09:04 thread_siblings_list
比如:
cat core_cpus_list
结果是
0
表示和 cpu0 同一个 core 的 cpu 只有 cpu0。
cat package_cpus_list
会显示
0-1
表示和 cpu0 同一个 socket 的 cpu 有从 cpu0 到 cpu1。
下面这些工具也可以辅助进行验证。
比如:lscpu
lscpu
通过执行 lscpu
命令会出现下面结果
Architecture: aarch64CPU op-mode(s): 32-bit, 64-bitByte Order: Little Endian
CPU(s): 64On-line CPU(s) list: 0-63
Vendor ID: ARMModel name: Cortex-A72Model: 2Thread(s) per core: 1Core(s) per cluster: 16Socket(s): -Cluster(s): 4Stepping: r0p2BogoMIPS: 100.00Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
NUMA:NUMA node(s): 4NUMA node0 CPU(s): 0-15NUMA node1 CPU(s): 16-31NUMA node2 CPU(s): 32-47NUMA node3 CPU(s): 48-63
Vulnerabilities:Itlb multihit: Not affectedL1tf: Not affectedMds: Not affectedMeltdown: Not affectedSpec store bypass: VulnerableSpectre v1: Mitigation; __user pointer sanitizationSpectre v2: VulnerableSrbds: Not affectedTsx async abort: Not affected
相关文章:
StratoVirt 的 vCPU 拓扑(SMP)
CPU 拓扑用来表示 CPU 在硬件层面的组合方式,本文主要讲解 CPU 拓扑中的 SMP(Symmetric Multi-Processor,对称多处理器系统)架构,CPU 拓扑还包括其他信息,比如:cache 等,这些部分会在…...
现在直播大部分都是RTMP RTMP VS RTC
一 RTMP 抓了下抖音直播的包,windows端,走的TCP,加密了,估计还是RTMP。 我以为直播带货,都是RTC了。 快手直播也是TCP,地址用了IPV6。 淘宝直播也是。现在大部分直播都是RTMP。 只有视频会议走的RTC。…...
【Unity实战100例】Unity循环UI界面切换卡片功能
目录 编辑 一:制作UI界面 二:代码逻辑 1.定义基础变量...

Monorepo or 物料市场?结合工作实际情况对公司现有前端体系的思考
前言 去年年中基于若依vue前端框架进行了改造,加上后端的配合,我写了一套脚手架和项目中后台模板。中后台模板中包含了许多基础代码,比如登录/注册、路由、权限等等相关功能。这个中后台模板是基于我们实际开发定制的,所以跟通用…...

GEE学习笔记八十八:在自己的APP中使用绘制矢量(上)
在GEE中尤其是自己的APP中调用绘制的矢量图形方法之前没有合适的方法,但是现在可以通过ui.Map.DrawingTools(...)以及ui.Map.GeometryLayer(...)结合来做。具体的API如下图: 在这一篇中我先通过一个简单的例子来展示一下使用这些API后可以实现什么效果&a…...
“笨办法”学Python 3 ——练习 39. 字典,可爱的字典
练习39 源代码 # create a mapping of state to abbreviation #创建一个州与缩写的映射 states {Oregon:OR,Florida:FL,California: CA, New York: NY,Michigan:MI} #创建一个字典,key为州名,value为州缩写#Create a basic set of states and some cit…...
模糊的照片如何修复清晰?
相信有很多人用手机拍照时,觉得拍出来的照片一定是很漂亮的,结果拍了之后,拿出来一看模糊一片,根本看不清是什么。或者是只显示一半另一半模糊一片。而这些精彩瞬间很多时候是无法重拍的。虽然谁也不想拍出的照片出现模糊…...

如何理解session、cookie、token的区别与联系?
session、cookie、token。 相信学过接口的朋友都特别熟悉了。 但是对我一个刚接触接口测试的小白来说,属实有点分不清楚。 下文就是我通过查阅各种资料总结出来的一点理解,不准确的地方还请各位指正。 (文末送洗浴中心流程指南)…...

【MyBatis】| MyBatis分页插件PageHelper
目录 一:MyBatis使⽤PageHelper 1. limit分⻚ 2. PageHelper插件 一:MyBatis使⽤PageHelper 1. limit分⻚ (1)概念: ①页码:pageNum(用户会发送请求,携带页码pageNum给服务器&am…...
Java枚举类详解
一、定义格式 public enum s { 枚举项1,枚举项2,枚举项3; } // 定义一个枚举类,用来表示春,夏,秋,冬这四个固定值 public enum Season {SPRING,SUMMER,AUTUMN,WINTER; } 二、枚举的特点 1、所有枚举类都是Enum的子类 2、我们可以通…...

C语言数组
一.数组定义 数组由数据类型相同的一系列元素组成 如 int main(){ float candy[365]; char code[12]; int states[50]; … } cnady是包含了365个float元素的数组。code是包含了12个char类型的数组。states包含了50个int类型的数组。 二.数组初始化和取值 我们使用花括号包含值&…...

Scala 入门(第一章Scala 环境搭建、插件的安装)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 第 1 章 Scala 入门1.1 概述1.1.1 为什么学习 Scala1.1.2 Scala 发展历史1.1.3 Scala 和 Java 关系1.1.4 Scala 语言特点1.2 Scala 环境搭建1.3 Scala 插件安装1.4 HelloWorl…...
math@多项式@求和式乘法@代数学基本定理
文章目录多项式求和式乘法应用代数学基本定理相关证明高次方程其他关于多项式的参考多项式求和式乘法 S∏j1(∑k1ajk) j∏j1m(∑k1njajk) jS\prod_{j1}\left(\sum\limits_{k1}a_{jk}\right)_{\!\!\!j} \\\prod_{j1}^{m}\left(\sum\limits_{k1}^{n_j}a_{jk}\r…...
Kafka系列之:基于SCRAM和Ranger机制完成动态新增kafka读写账号、赋予账号对指定Topic的读写权限
Kafka系列之:基于SCRAM和Ranger机制完成动态新增kafka读写账号、赋予账号对指定Topic的读写权限 一、需求背景二、添加写用户三、查看用户是否添加到zookeeper中四、查看用户五、赋予用户topic写权限六、生产者配置文件七、ranger给用户权限八、往Topic写数据九、删除添加的用…...
第五十三章 DFS进阶(一)——剪枝优化
第五十四章 DFS进阶(一)——剪枝优化一、什么是剪枝?二、剪枝优化的策略1、优化搜索顺序2、排除等效冗余3、可行性剪枝4、最优性剪枝5、记忆化搜索三、例题1、AcWing 165. 小猫爬山(DFS 剪枝优化)2、AcWing 167. 木棒…...

Java字节码深度知多少?
文章目录1、字节码结构1.1、基本结构1.2、实际观测2、内存表示3、方法调用指令4、invokedynamicEND结语Java真的是长盛不衰,拥有顽强的生命力。其中,字节码机制功不可没。字节码,就像是 Linux 的 ELF。有了它,JVM直接摇身一变&…...

【C++】智能指针(万字详解)
🌈欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤&…...

使用docker配置mysql主从复制
1.新建主服务器容器实例: docker run -p 3307:3306 --name mysql \ -v /docker/mysql/data:/var/lib/mysql \ -v /docker/mysql/conf:/etc/mysql/conf \ -v /docker/mysql/log:/var/log/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:5.7 设置容器卷之后…...
v3 异步组件及分包使用
1 app.vue <template> <!-- vue3异步组件必须使用suspense --> <Suspense> <template #default> <!-- 异步组件 --> <SyncVue></SyncVue> </template> <template v-slot:fallback> <!-- 优先显示骨架屏 --> <…...

实用调试技巧【上篇】
🔴本文章是在 Visual Studio 2022(VS2022)编译环境下进行操作讲解 文章目录🥳1. 什么是bug?🥳2.调试有多重要?2.1. 我们是如何写代码的?2.2.调试是什么?2.3.调试的基本步…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...