SEV内存加密位linux内核设置过程

news2025/3/22 20:18:54

1. KVM_MEMORY_ENCRYPT_OP初始化

main()                     // QEMU 入口函数
    qemu_init()              // 初始化虚拟机
        configure_accelerators
            kvm_init
                sev_guest_init
                    KVM_SEV_INIT
                    sev_launch_start
                        KVM_SEV_LAUNCH_START
                    ram_block_notifier_add(&sev_ram_notifier);   //sev_ram_block_added KVM_MEMORY_ENCRYPT_REG_REGION
                kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
                kvm_arch_init
                    kvm_vm_enable_cap
                kvm_memory_listener_register
                memory_listener_register
                cpus_register_accel
        machine_run_board_init()      //pc_init_isa
            pc_init1()    
                pc_memory_init
                pc_system_firmware_init
                    pc_system_flash_map
                        kvm_memcrypt_encrypt_data
                            kvm_state->memcrypt_encrypt_data

1.1、代码逻辑解析

此代码段属于 KVM 对 KVM_MEMORY_ENCRYPT_OP ioctl 的处理核心逻辑,其作用是为内存加密操作提供统一的入口,但实际功能由架构相关代码(如 AMD SVM)实现。以下为逐行分析:

case KVM_MEMORY_ENCRYPT_OP: {
    r = -ENOTTY; // 默认返回“不支持该操作”
    if (kvm_x86_ops->mem_enc_op) // 检查架构是否实现内存加密操作
        r = kvm_x86_ops->mem_enc_op(kvm, argp); // 调用具体实现(如 AMD SEV)
    break;
}

1.2、关键行为解读

1. 兼容性控制机制
  • -ENOTTY 的深层含义:表示当前内核或硬件不支持内存加密功能,可能原因包括:

    • 未启用 CONFIG_KVM_AMD_SEV 内核编译选项
    • 硬件无 SEV 支持(如非 AMD EPYC CPU)
    • 内核版本过旧(<4.16)或过新(≥5.10,此接口已废弃)
  • kvm_x86_ops->mem_enc_op 的动态绑定

    • AMD 实现:指向 svm_mem_enc_op()(定义于 arch/x86/kvm/svm/sev.c
    • Intel 实现:始终为 NULL(Intel 无等效 SEV 功能)
2. 参数传递流程
// 用户态(QEMU)调用示例:
struct kvm_sev_cmd cmd = {
    .id = KVM_SEV_INIT, // 子命令类型
    .data = (unsigned long)&init_params // 具体参数结构体
};
ioctl(kvm_fd, KVM_MEMORY_ENCRYPT_OP, &cmd);

// 内核态处理(svm_mem_enc_op):
static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
{
    struct kvm_sev_cmd cmd;
    copy_from_user(&cmd, argp, sizeof(cmd)); // 从用户空间复制命令
    return sev_handle_cmd(kvm, cmd.id, cmd.data); // 分发到 SEV 子处理函数
}

1.3、与新版接口的差异

旧版(KVM_MEMORY_ENCRYPT_OP) vs 新版(KVM_SEV_*)
特性旧版接口新版接口(≥5.10)
命令结构统一入口 + 子命令 ID独立 ioctl 命令(如 KVM_SEV_INIT
可维护性参数解析耦合度高类型安全,减少错误传递
功能扩展性需修改共用结构体独立命令互不影响
典型调用栈ioctl(KVM_MEMORY_ENCRYPT_OP)svm_mem_enc_op()sev_handle_cmd()ioctl(KVM_SEV_INIT)sev_guest_init()
淘汰原因分析(内核 ≥5.10)
  1. 安全性:旧接口允许任意 cmd.id 传递,存在非法命令注入风险
  2. 性能:新版省去多层间接跳转,调用路径更短
  3. 代码清晰度:每个 SEV 操作有独立入口,便于维护

1.4、实际应用场景验证

若在 5.4 内核 中执行以下 QEMU 命令:

qemu-system-x86_64 -machine confidential-guest-support=sev0...

内核日志将出现

kvm_amd: SEV supported: 255 ASIDs
kvm_amd: SEV-ES supported: 255 ASIDs
svm_mem_enc_op: handling command KVM_SEV_INIT

此时旧接口仍有效,但 5.10+ 内核会拒绝此调用,需改用 KVM_SEV_INIT

1.5、调试技巧

  1. 动态追踪调用路径

    echo 'p:svm_mem_enc_op arch/x86/kvm/svm/sev.c:1800 cmd_id=+0(%dx):u32' > /sys/kernel/debug/tracing/kprobe_events
    echo 1 > /sys/kernel/debug/tracing/events/kprobes/enable
    

    可捕获所有经过 svm_mem_enc_op 的命令 ID。

  2. 错误码解读

    • -ENOTTY → 检查硬件/Kconfig/内核版本
    • -EINVAL → 参数结构体不匹配(如 32/64 位模式混合)

1.6、迁移到新接口的代码示例

// 旧版(废弃):
struct kvm_sev_cmd cmd = {.id = KVM_SEV_INIT};
ioctl(kvm_fd, KVM_MEMORY_ENCRYPT_OP, &cmd);

// 新版(推荐):
ioctl(kvm_fd, KVM_SEV_INIT, &launch_params); // 直接使用独立命令

该代码段是 KVM 内存加密功能演进的历史产物,理解其工作原理有助于调试旧版本兼容性问题,但在新开发中应遵循 AMD 官方推荐使用 KVM_SEV_* 系列专用命令。

2 qemu中设置逻辑

kvm_init
    kvm_memory_listener_register
        memory_listener_register                              
            listener_add_address_space
                kvm_region_add        (kml->listener.region_add = kvm_region_add;)
                    kvm_set_phys_mem

3 KVM_SET_USER_MEMORY_REGION

3.1、函数调用链路解析

1. 内存槽操作核心流程
用户态 QEMU 调用 KVM_SET_USER_MEMORY_REGION
内核 kvm_vm_ioctl_set_memory_region
__kvm_set_memory_region
kvm_set_memslot?
调用 kvm_set_memslot
直接操作 memslots
触发 kvm_arch_commit_memory_region
调用 arch.encrypted_ops->region_add
l
2. 关键函数映射关系
用户预期名称内核实际名称/行为所属文件
memslot_region_addkvm_set_memslotvirt/kvm/kvm_main.c
__kvm_set_memory_regionvirt/kvm/kvm_main.c
sev_mem_enc_region_addarch/x86/kvm/svm/sev.c

3.2、调试与验证方法

1. 动态追踪技术验证
# 使用 ftrace 跟踪内存槽添加操作
echo 'p:kvm_set_memslot kvm_set_memslot' > /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/enable

# 观察输出示例:
#   qemu-system-x86-3248  [002] ...1  5678.901234: kvm_set_memslot: (kvm_set_memslot+0x0/0x3c0)
2. 代码交叉引用检查
  • sev.c 中搜索 region_add 的引用:
    git grep -n "region_add" arch/x86/kvm/svm/sev.c
    # 输出:sev.c:1873:    .region_add = sev_mem_enc_region_add,
    
  • 确认 sev_mem_enc_region_add 的实现细节:
    static int sev_mem_enc_region_add(struct kvm *kvm, u64 addr, u64 size) {
        // 具体实现:遍历 PFN 设置 C-bit
        for (pfn = addr >> PAGE_SHIFT; pfn < (addr + size) >> PAGE_SHIFT; pfn++)
            set_page_encryption_mask(pfn);
    }
    

3.3、版本兼容性说明

内核版本关键变化点影响分析
Linux 4.19使用 kvm_set_memory_region 旧接口直接操作 kvm_memory_slot
Linux 5.4+引入 kvm_set_memslot 统一接口抽象化内存槽操作
Linux 5.15加密回调通过 kvm_memory_encrypted_ops明确分离加密相关操作

3.4、开发者操作建议

  1. 正确跟踪回调函数

    // 在 sev.c 中添加调试输出
    static int sev_mem_enc_region_add(struct kvm *kvm, u64 addr, u64 size) {
        pr_info("SEV: Encrypting memory region 0x%llx-0x%llx\n", addr, addr + size);
        // ...原有代码...
    }
    

    通过 dmesg 观察加密区域触发情况。

  2. API 变更应对策略

    // 兼容多版本内核的代码示例
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
        ret = kvm_set_memslot(kvm, &old, &new, KVM_MR_MOVE);
    #else
        ret = kvm_set_memory_region(kvm, &mem);
    #endif
    
  3. 文档查阅指引

    • 官方内核文档:Documentation/virt/kvm/api.rst(搜索 KVM_SET_USER_MEMORY_REGION
    • AMD SEV 白皮书:AMD Secure Encrypted Virtualization API 第 4.2 节 “Memory Encryption”

4 KVM_MEMORY_ENCRYPT_REG_REGION

memory_region_init_ram_from_file
	qemu_ram_alloc_from_file
	    mr->ram_block = qemu_ram_alloc_from_fd  (qemu)
	    qemu_ram_alloc_from_fd  (qemu)
	    qemu_ram_alloc_internal  (qemu)
	        ram_block_add  (qemu)
	            ram_block_notify_add  (qemu)
	                ram_block_added = sev_ram_block_added   (qemu)
	                sev_ram_block_added    (qemu)
	                    kvm_arch_vm_ioctl (KVM_MEMORY_ENCRYPT_REG_REGION)
	                        svm_x86_ops->mem_enc_reg_region 
	                            svm_register_enc_region

qemu_ram_alloc_internal 函数解析

qemu_ram_alloc_internal 是 QEMU 内存管理子系统中的核心内部函数,负责为虚拟机分配物理内存块(RAM Block)。它是 QEMU 内存初始化和动态扩展的关键环节,直接关联虚拟机的内存布局与性能优化。以下是其详细说明:


4.1. 函数定义与参数

函数原型
RAMBlock *qemu_ram_alloc_internal(
    ram_addr_t size,        // 需要分配的内存大小(字节)
    ram_addr_t max_size,    // 最大可扩展大小(用于动态内存扩展)
    void (*resized)(const char*, uint64_t, void*), // 内存大小调整回调函数
    void *resized_opaque,   // 回调函数的用户参数
    uint32_t flags,         // 内存属性标志(如共享、只读等)
    const char *name,       // 内存块名称(调试用)
    Error **errp            // 错误处理指针
);
参数解析
  • size
    初始分配的物理内存大小(例如 4GB 的虚拟机内存)。
  • max_size
    允许动态扩展的最大内存大小。若无需扩展,通常与 size 相同。
  • resized 回调
    当内存块大小调整时触发(例如通过 balloon 驱动收缩内存)。
  • flags
    内存属性标志,常见值包括:
    • RAM_SHARED: 内存可共享(用于多进程或热迁移)。
    • RAM_PREALLOC: 预分配物理内存(避免惰性分配)。
    • RAM_NORESERVE: 不预留宿主内存(允许 overcommit)。
  • name
    唯一标识符,如 "pc.ram""virtio-mmio.region"

4.2. 核心功能

内存分配流程
  1. 创建 RAMBlock 结构体
    初始化 RAMBlock 对象,记录内存块的元数据(大小、宿主指针、映射关系等)。

    RAMBlock *block = g_new0(RAMBlock, 1);
    block->used_length = size;
    block->max_length = max_size;
    block->flags = flags;
    block->idstr = g_strdup(name);
    
  2. 宿主内存分配
    调用宿主系统的内存分配接口(如 mmapmemfd),根据 flags 选择策略:

    • 私有匿名映射(默认):
      ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
      
    • 大页内存(若启用):
      ptr = qemu_memfd_alloc(name, size, F_SEAL_GROW | F_SEAL_SHRINK, errp);
      
    • 共享内存RAM_SHARED):
      ptr = shm_open(name, O_CREAT | O_RDWR, 0666);
      ftruncate(fd, size);
      
  3. 注册到全局链表
    将新分配的 RAMBlock 添加到 QEMU 的全局 RAM 列表 ram_list,供地址空间映射使用:

    QLIST_INSERT_HEAD_RCU(&ram_list.blocks, block, next);
    
  4. 通知内存监听器
    触发 MEMORY_DEVICE_PREPLUG 事件,通知设备模型(如 VGA、virtio-balloon)内存已更新。


4.3. 调用场景

场景 1:虚拟机启动初始化

在 QEMU 启动参数 -m 4G 时,主函数调用链为:

main() → qemu_init() → pc_memory_init() → memory_region_allocate_system_memory()
                                       → qemu_ram_alloc_internal()
  • 作用:分配虚拟机的主内存(pc.ram)。
场景 2:热插拔内存设备

通过 QMP 命令 memory-device-add 动态添加 DIMM:

qmp_memory_device_add() → pc_dimm_plug() → pc_dimm_memory_plug()
                                        → qemu_ram_alloc_internal()
  • 作用:为热插拔的内存条分配新的 RAMBlock。
场景 3:内存动态调整

virtio-balloon 驱动收缩内存时:

virtio_balloon_handle_output() → qemu_balloon() → ram_block_discard_range()
                                               → 触发 resized 回调
  • 作用:释放部分内存并更新 RAMBlockused_length

4.4. 关键数据结构

RAMBlock 结构(部分)
struct RAMBlock {
    struct rcu_head rcu;        // RCU 同步保护
    ram_addr_t offset;          // 在全局地址空间中的偏移
    ram_addr_t used_length;     // 当前使用的内存大小
    ram_addr_t max_length;      // 允许的最大内存大小
    void *host;                 // 宿主内存指针
    uint32_t flags;             // 内存属性标志
    char idstr[256];            // 唯一标识名称
    QLIST_ENTRY(RAMBlock) next; // 全局链表指针
    // ...(其他字段省略)
};
ram_list 全局变量
RAMList ram_list = {
    .blocks = QLIST_HEAD_INITIALIZER(ram_list.blocks),
    .mru_block = NULL,
};

4.5. 性能优化机制

  1. 内存预分配RAM_PREALLOC
    避免宿主系统的惰性分配(lazy allocation),减少虚拟机运行时缺页中断的开销。

  2. 透明大页对齐
    若宿主支持 THP(Transparent Huge Pages),qemu_ram_alloc_internal 会确保分配的内存按大页对齐(如 2MB),提升 TLB 命中率。

  3. 内存锁(mlock)
    在实时性要求高的场景(如 vhost-user),调用 mlock 锁定物理内存,防止被换出。


4.6. 错误处理

  • 内存不足:返回 NULL 并通过 error_setg(errp, "Cannot allocate memory") 设置错误信息。
  • 重复名称:若 name 已存在,触发 assert(调试模式)或忽略(生产环境可能不同)。

4.7. 代码示例(简化版)

// 分配 1GB 内存,允许扩展到 2GB
RAMBlock *ram = qemu_ram_alloc_internal(
    1 * GiB,
    2 * GiB,
    NULL,  // 无需 resized 回调
    NULL,
    RAM_SHARED, // 共享内存
    "guest.ram",
    &error_fatal
);

// 将 RAMBlock 映射到地址空间
MemoryRegion *mr = g_new(MemoryRegion, 1);
memory_region_init_ram_ptr(mr, NULL, "sysmem", ram->max_length, ram->host);
address_space_add_memory(&address_space_memory, mr, 0);

小结

qemu_ram_alloc_internal 是 QEMU 内存管理的底层基石,负责:

  • 物理内存分配:对接宿主系统的内存分配机制。
  • 元数据管理:维护 RAMBlock 的全局状态。
  • 扩展性支持:为热插拔、动态调整提供基础设施。

5. KVM_MEMORY_ENCRYPT_OP和KVM_MEMORY_ENCRYPT_REG_REGION区别

5. 1、 KVM_MEMORY_ENCRYPT_OP

功能目标
  • 通用加密操作:执行与内存加密相关的全局性操作,例如初始化加密上下文、密钥管理(如生成/注入密钥)、加密状态控制等。
  • 示例
    • AMD SEV 中的 KVM_SEV_INIT(初始化安全加密虚拟化环境)
    • KVM_SEV_LAUNCH_START(启动虚拟机加密流程)
参数处理
  • 直接传递指针:通过 argp 直接传递操作类型和参数(通常是架构定义的联合体或结构体)。
  • 无显式数据拷贝:依赖底层实现解析 argp 指针,操作可能直接修改内核或硬件状态。
操作层级
  • 架构相关实现:通过 kvm_x86_ops->mem_enc_op 调用具体硬件(如 AMD SEV 或 Intel TDX)的加密操作接口。
  • 影响范围:通常作用于整个虚拟机或加密会话。

5.2、 KVM_MEMORY_ENCRYPT_REG_REGION

功能目标
  • 内存区域注册:将特定内存区域标记为加密/受保护区域,或从加密状态中解除注册。
  • 示例
    • AMD SEV 的 KVM_SEV_LAUNCH_UPDATE_DATA(标记某块内存需加密)
    • 解除内存区域的加密保护(如迁移完成后)。
参数处理
  • 结构化数据拷贝:从用户空间复制 kvm_enc_region 结构体,明确包含内存区域的物理地址长度
    struct kvm_enc_region {
        __u64 addr;  // 内存区域起始地址
        __u64 size;  // 内存区域大小
    };
    
  • 安全性检查:通过 copy_from_user 验证用户空间数据的合法性,避免非法内存访问。
操作层级
  • 内存粒度操作:通过 kvm_x86_ops->mem_enc_reg_region 针对特定物理地址范围进行加密配置。
  • 影响范围:仅作用于单个内存区域,用于精细化管理。

关键差异总结

维度KVM_MEMORY_ENCRYPT_OPKVM_MEMORY_ENCRYPT_REG_REGION
操作类型全局性加密控制(如初始化、密钥管理)内存区域级加密配置(注册/解注册)
参数形式可能为复合结构体(通过 argp 直接解析)固定结构体(明确地址和大小)
数据传递无显式用户空间拷贝需从用户空间拷贝 kvm_enc_region
影响范围虚拟机级别或加密会话级别特定物理内存区域

典型应用场景

  1. 虚拟机启动加密

    • 调用 KVM_MEMORY_ENCRYPT_OP 初始化 SEV 环境。
    • 多次调用 KVM_MEMORY_ENCRYPT_REG_REGION 逐块注册内存到加密区域。
  2. 热迁移保护

    • 迁移前通过 KVM_MEMORY_ENCRYPT_OP 导出加密密钥。
    • 迁移后通过 KVM_MEMORY_ENCRYPT_REG_REGION 重新绑定内存区域。
  3. 动态内存加密

    • 运行时按需加密敏感内存区域(如 GPU 显存),通过 KVM_MEMORY_ENCRYPT_REG_REGION 动态注册。

底层实现依赖

  • 硬件支持:两者最终通过 kvm_x86_ops 调用具体实现(如 AMD 的 sev_mem_enc_opsev_mem_enc_reg_region)。
  • 安全性约束:内存加密操作通常需要 Hypervisor 与安全处理器(如 AMD PSP)协同完成,确保密钥不泄露。

此设计体现了 KVM 对内存加密的灵活支持:既提供全局控制接口,又允许细粒度内存管理,适配不同硬件加密方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2319758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习(赵世钰版)-学习笔记(8.值函数方法)

本章是算法与方法的第四章&#xff0c;是TD算法的拓展&#xff0c;本质上是将状态值与行为值的表征方式&#xff0c;从离散的表格形式&#xff0c;拓展到了连续的函数形式。 表格形式的优点是直观&#xff0c;便于分析&#xff0c;缺点是数据量较大或者连续性状态或者行为空间时…

STM32F4与串口屏通信

淘晶池串口屏操作指令集 那我们就来谈一谈串口屏与STM32F4嵌入式板子的通信 第一&#xff0c;串口屏传输数据给F4板子 这时&#xff0c;我们就该来谈一谈prints函数和printh函数的用法 prints att,length att:变量名称 length:长度(0为自动长度) printh hex hex:需要发送的…

车载以太网网络测试-20【传输层-DOIP协议-3】

1 摘要 本文继续对ISO 13400-2定义的节点管理报文进行介绍&#xff0c;主要对路由激活请求/响应报文以及在线检查请求/响应报文的作用、帧结构以及示例进行介绍。 上文回顾&#xff1a; 车载以太网网络测试-19【传输层-DOIP协议-2】 在进行详细介绍之前&#xff0c;还是先回顾…

`chromadb` 是什么

chromadb 是什么 chromadb 是一个开源的向量数据库,它专门用于存储、索引和查询向量数据。在处理自然语言处理(NLP)、计算机视觉等领域的任务时,通常会将文本、图像等数据转换为向量表示,而 chromadb 可以高效地管理这些向量,帮助开发者快速找到与查询向量最相似的向量数…

关于“碰一碰发视频”系统的技术开发文档框架

以下是关于“碰一碰发视频”系统的技术开发文档框架&#xff0c;涵盖核心功能、技术选型、开发流程和关键模块设计&#xff0c;帮助您快速搭建一站式解决方案 --- 随着短视频平台的兴起&#xff0c;用户的创作与分享需求日益增长。而如何让视频分享更加便捷、有趣&#xff0c…

vue3之写一个aichat---已聊天组件部分功能

渲染聊天数据 这个不必多说&#xff0c;直接从stores/chat中取出聊天列表数据渲染就好&#xff0c;因为前面添加的消息都是按照用户消息、AI助手消息这样添加的&#xff0c;效果如图 但是需要注意每条助手消息的状态&#xff0c;需要根据状态显示不同的图标或不显示图标&…

基于STC89C51的太阳自动跟踪系统的设计与实现—单片机控制步进电机实现太阳跟踪控制(仿真+程序+原理图+PCB+文档)

摘 要 随着我国经济的飞速发展&#xff0c;促使各种能源使用入不敷出&#xff0c;尤其是最主要的能源&#xff0c;煤炭石油资源不断消耗与短缺&#xff0c;因此人类寻找其他替代能源的脚步正在加快。而太阳能则具有无污染﹑可再生﹑储量大等优点&#xff0c;且分布范围广&…

第五: redis 安装 / find 查找目录

redis 安装的 两种方式&#xff1a; mac上安装redis的两种方法_如何在mac上安装redis-CSDN博客 首先可以先看一下brew的常用命令如下&#xff1a; brew search ** //查找某个软件包 brew list //列出已经安装的软件的包 brew install ** //安装某个软件包,默认安装的是…

Springboot 项目如何输出优雅的日志

我们先看效果图&#xff1a; 我个人比较喜欢这种格式的日志输出&#xff0c;对其完整&#xff1b; 这种格式其实就是默认的&#xff0c;不需要大家配置任何的 logback-spring 文件和xml中配置日志level 没有做任何多余的配置&#xff1b;

Linux——进程(5)进程地址空间

先看一个程序和现象 预期现象是&#xff0c;子进程和父进程相互独立&#xff0c;子进程的gval是100&#xff0c;101&#xff0c;102....而父进程一直都是100. 结果我们并不意外&#xff0c;只是我们发现&#xff0c;父子进程的gval的地址是一样的&#xff0c;这有点颠覆我们的认…

代码随想录_动态规划

代码随想录 动态规划 509.斐波那契数 509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n…

星越L_陡坡缓降使用讲解

目录 1.陡坡缓降 1.陡坡缓降 中控屏下滑-点击陡坡缓降功能 35km/h以下时生效。35km/h-60km/h该功能暂停 60km/h以上该功能关闭

XSS跨站脚本攻击漏洞(Cross Site Scripting)

前提概要 本文章主要用于分享XSS跨站脚本攻击漏洞基础学习&#xff0c;以下是对XSS跨站脚本攻击漏洞的一些个人解析&#xff0c;请大家结合参考其他文章中的相关信息进行归纳和补充。 XSS跨站脚本攻击漏洞描述 跨站脚本攻击&#xff08;XSS&#xff09;漏洞是一种常见且危害较…

html5基于Canvas的经典打砖块游戏开发实践

基于Canvas的经典打砖块游戏开发实践 这里写目录标题 基于Canvas的经典打砖块游戏开发实践项目介绍技术栈核心功能实现1. 游戏初始化2. 游戏对象设计3. 碰撞检测系统4. 动画系统5. 用户界面设计 性能优化1. 渲染优化2. 内存管理 项目亮点技术难点突破项目总结 项目介绍 在这个…

企业信息化的“双螺旋”——IT治理和数据治理

企业信息化的“双螺旋”——IT治理和数据治理 一、核心定义二、关键差异三、内在联系四、实践挑战与融合路径五、行业案例参考六、结论数据治理(Data Governance)和IT治理(IT Governance)是现代企业数字化转型中的关键概念,二者既有紧密关联又各有侧重。以下从定义、核心内…

CCBCISCN复盘

AWDP – ccfrum 自己搭了一下环境, 复现一下这道题目, 之前比赛的时候完全没想到这个漏洞要怎么打, 修也不知道要怎么修, 就仅仅是对用户名的账号和密码进行了一下过滤, 完全没起到作用, 唉, 实在太菜 如果想要尝试复现的话可以尝试拉取这个镜像, 我打完之后就直接把这个容器给…

糊涂人寄信——递推

思路分析&#xff1a;当有n封信&#xff0c;n个信封时。第k封信没有装在第k个信封里&#xff08;k从1~n&#xff09;&#xff0c;就算所有的信封都装错了。我们可以得知的是&#xff0c;当有1封信,时&#xff0c;装错类别数为0。当有两封信时&#xff0c;装错类别为1。 当有三…

使用 OpenCV 拼接进行图像处理对比:以形态学操作为例

图像处理在计算机视觉中起着至关重要的作用&#xff0c;而 OpenCV 作为一个强大的图像处理库&#xff0c;提供了丰富的函数来实现各类图像处理任务。形态学操作&#xff08;Morphological Operations&#xff09;是其中常用的技术&#xff0c;尤其适用于二值图像的处理。常见的…

OpenHarmony 入门——ArkUI 跨页面数据同步和页面级UI状态存储LocalStorage小结(二)

文章大纲 引言一、在代码逻辑使用LocalStorage二、从UI内部使用LocalStorage三、LocalStorageProp和LocalStorage单向同步四、LocalStorageLink和LocalStorage双向同步五、兄弟组件之间同步状态变量七、将LocalStorage实例从UIAbility共享到一个或多个视图 引言 前面一篇文章主…

Python数据可视化实战:从基础图表到高级分析

Python数据可视化实战&#xff1a;从基础图表到高级分析 数据可视化是数据分析的重要环节&#xff0c;通过直观的图表可以快速洞察数据规律。本文将通过5个实际案例&#xff0c;手把手教你使用Python的Matplotlib库完成各类数据可视化任务&#xff0c;涵盖条形图、堆积面积图、…