UEFI——获取UEFI MemoryMap

一、MemoryMap简介

首先讲一下什么是MemoryMap?

内存映射(Memory Mapping)是一种将文件内容映射到进程的虚拟地址空间的技术。在这种机制下,文件可以视为内存的一部分,从而允许程序直接对这部分内存进行读写操作,而无需传统的I/O调用。内存映射缓冲区利用虚拟内存机制,让操作系统将一部分磁盘文件映射到进程地址空间的一块连续区域当中。操作系统负责管理内存页的加载和卸载,应用程序只需要访问这块内存区域即可,从而避免了频繁的磁盘I/O操作和多余的系统调用。

UEFI的内存映射

  • UEFI内存映射是由UEFI固件提供的,主要用于在启动过程中描述系统的内存布局。它告诉操作系统和启动软件哪些内存区域是可用的,哪些是保留的。
  • 实现:UEFI内存映射通常是在系统启动的早期阶段由UEFI固件创建的,它包括了系统内存的详细信息,如内存大小、类型和状态。

UEFI MemoryMap指的是UEFI运行时环境中的一个数据结构,它包含了当前系统内存的详细信息.MemoryMap记录了系统内存的布局,包括所有可用的区域、内存类型、属性以及使用情况。

内存类型:MemoryMap中的每个内存区域都有一个与之关联的类型

        在UEFI中引入的内存类型由以下几种:

typedef enum {
  EfiReservedMemoryType, //没有用到的
  EfiLoaderCode, //已加载的UEFI应用程序的代码部分
  EfiLoaderData, //已加载的UEFI应用程序的数据部分和默认数据分配
  EfiBootServicesCode, //已加载的 Boot Services 驱动程序的代码部分
  EfiBootServicesData, //已加载的 Boot Services 驱动程序的数据部分
  EfiRuntimeServicesCode, //已加载的Runtime Services驱动的代码部分
  EfiRuntimeServicesData, //已加载的Runtime Services驱动的数据部分
  EfiConventionalMemory, //空闲(未分配)内存
  EfiUnusableMemory, //检测到错误的内存
  EfiACPIReclaimMemory, //保存ACPI表的内存
  EfiACPIMemoryNVS, //为固件保留的地址空间
  EfiMemoryMappedIO, //系统固件使用它来请求操作系统将内存映射的 IO 区域映射到虚拟地址,以便 EFI 运行时服务可以访问它。
  EfiMemoryMappedIOPortSpace, //系统内存映射的 IO 区域,处理器用它来将内存周期转换为 IO 周期。
  EfiPalCode, //固件为作为处理器一部分的代码保留的地址空间。
  EfiPersistentMemory, //一个内存区域,它作为 EfiConventionalMemory 运行,但它也恰好支持字节寻址的非易失性。
  EfiUnacceptedMemoryType, //描述未被接受的系统内存的内存区域。

  EfiMaxMemoryType,
  //
  // +---------------------------------------------------+
  // | 0..(EfiMaxMemoryType - 1)    - Normal memory type |
  // +---------------------------------------------------+
  // | EfiMaxMemoryType..0x6FFFFFFF - Invalid            |
  // +---------------------------------------------------+
  // | 0x70000000..0x7FFFFFFF       - OEM reserved       |
  // +---------------------------------------------------+
  // | 0x80000000..0xFFFFFFFF       - OS reserved        |
  // +---------------------------------------------------+
  //
  MEMORY_TYPE_OEM_RESERVED_MIN = 0x70000000,
  MEMORY_TYPE_OEM_RESERVED_MAX = 0x7FFFFFFF,
  MEMORY_TYPE_OS_RESERVED_MIN  = 0x80000000,
  MEMORY_TYPE_OS_RESERVED_MAX  = 0xFFFFFFFF
} EFI_MEMORY_TYPE;

内存属性:写保护、都保护、执行保护、非易失性、只读、运行时等等等等

内存区域:

使用情况:

UEFI MemoryMap的主要用途包括:

操作系统加载器:操作系统加载器在启动过程中会使用MemoryMap来确定哪些内存是可用的,以及如何配置和使用这些内存。

UEFI应用程序:UEFI应用程序可以使用MemoryMap来了解内存布局,以便它们可以请求和分配内存。

内存管理:UEFI固件使用MemoryMap来管理内存分配,确保不同组件之间不会发生内存冲突。

UEFI规范中用结构体EFI_MEMORY_DESCRIPTOR来描述内存区域,在UEFI环境中,EFI_MEMORY_DESCRIPTOR被用来标识内存映射(Memory Map)中的一个条目,即内存映射的每一项都是一个EFI_MEMORY_DESCRIPTOR结构。UEFI应用程序和驱动程序可以使用EFI_BOOT_SERVICES.GetMemoryMap服务来获取当前的内存映射,这个映射是一个EFI_MEMORY_DESCRIPTOR数组。通过便利这个数组,应用程序可以了解系统的内存布局,并据此进行内存管理操作。

EFI_MEMORY_DESCRIPTOR的代码原型为:

typedef struct {
  UINT32                Type;                   // 描述内存的类型
  EFI_PHYSICAL_ADDRESS  PhysicalStart;          // 内存区域的物理起始地址
  EFI_VIRTUAL_ADDRESS   VirtualStart;           // 内存区域的虚拟起始地址(如果适用)
  UINT64                NumberOfPages;          // 内存区域包含的页数
  UINT64                Attribute;              // 内存区域的属性
} EFI_MEMORY_DESCRIPTOR;

二、获取UEFI MemoryMap

UEFI的内存服务提供了EFI_GET_MEMORY_MAP.GetMemoryMap接口来获取当前的内存映射,其代码原型为:

//返回当前的Memory Map

  @retval EFI_SUCCESS           The memory map was returned in the MemoryMap buffer.
                                内存映射已经返回到Memmorymap缓冲区
  @retval EFI_BUFFER_TOO_SMALL  The MemoryMap buffer was too small. The current buffer size
                                needed to hold the memory map is returned in MemoryMapSize.
                                表示MemmoryMap缓冲区太小了,目前需要用来保存内存映射的缓冲区大小已经返回到MemmoryMapSize中。
  @retval EFI_INVALID_PARAMETER 1) MemoryMapSize is NULL.
                                2) The MemoryMap buffer is not too small and MemoryMap is
                                   NULL.
                                 表示MemorymapSize为空,
                                 或memmoryMap缓冲区并不小,但是MemMorymap是空的



typedef
EFI_STATUS
(EFIAPI *EFI_GET_MEMORY_MAP)(
  IN OUT UINTN                       *MemoryMapSize, //输入,输出参数,指向memorymap缓冲区大小的指针,单位为字节。在输入时,这是调用者分配的缓冲区大小。在输出时,如果缓冲区足够大,这是固件返回的缓冲区大小;如果缓冲区过小,则是包含映射所需要的缓冲区大小。
  OUT    EFI_MEMORY_DESCRIPTOR       *MemoryMap, //输出参数,指向固件存放当前内存映射的缓冲区的指针
  OUT    UINTN                       *MapKey, //输出参数,指向固件返回当前内存映射键的位置的指针
  OUT    UINTN                       *DescriptorSize, //输出参数,指向固件返回单个EFI_MEMORY_DESCRIPTOR的大小(以字节为单位)的位置的指针。
  OUT    UINT32                      *DescriptorVersion //指向固件返回与 EFI_MEMORY_DESCRIPTOR 关联的版本号的位置的指针。

  );

如果GetMemoryMap的返回值为EFI_BUFFER_TOO_SMALL,说明MemoryMap缓冲区太小,已经将用来保存内存映射的缓冲区大小返回到MemoryMapSize中了。接下来就需要使用AllocatePool重新分配缓冲区。其代码原型为

/**
    Allocates a buffer of type EfiBootServicesData.
    按照字节数分配EfiBootServicesData类型的内存,并返回一个指向所分配缓冲区的指针
**/
VOID *
EFIAPI
AllocatePool (
  IN UINTN  AllocationSize //需要分配的字节数
  );

完整代码如下:

#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>



EFI_STATUS
EFIAPI
MyMemMapEntry(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
)
{

  EFI_STATUS                Status;
  UINTN                     EfiMemoryMapSize;
  EFI_MEMORY_DESCRIPTOR     *EfiMemoryMap;
  EFI_MEMORY_DESCRIPTOR     *EfiMemoryMapEnd;
  EFI_MEMORY_DESCRIPTOR     *EfiEntry;
  UINTN                     EfiMapKey;
  UINTN                     EfiDescriptorSize;
  UINT32                    EfiDescriptorVersion;

  UINT64                    BootServicesDataPage = 0;
  UINT64                    BootServicesCodePage = 0;
  UINT64                    ACPIReclaimMemoryPage = 0;
  UINT64                    ACPIMemoryNVSPage = 0;
  UINT64                    ReservedMemoryTypePage = 0;
  UINT64                    RuntimeServicesCodePage = 0;
  UINT64                    RuntimeServicesDataPage = 0;
  UINT64                    LoaderCodePage = 0;
  UINT64                    LoaderDataPage = 0;
  UINT64                    MaxMemoryTypePage = 0;
  UINT64                    ConventionalMemoryPage = 0;
  UINT64                    UnusableMemoryPage = 0;
  UINT64                    MemoryMappedIOPage = 0;
  UINT64                    MemoryMappedIOPortSpacePage = 0;
  UINT64                    PalCodePage = 0;
  UINT64                    PersistentMemoryPage = 0;


  EfiMemoryMapSize = 0;
  EfiMemoryMap = NULL;
  Status = gBS->GetMemoryMap ( //获取MemoryMap
                  &EfiMemoryMapSize,
                  EfiMemoryMap,
                  &EfiMapKey,
                  &EfiDescriptorSize,
                  &EfiDescriptorVersion
                  );
  ASSERT (Status == EFI_BUFFER_TOO_SMALL); //过小重新分配
  do {

    EfiMemoryMap = AllocatePool (EfiMemoryMapSize);
    if (EfiMemoryMap == NULL){
      DEBUG ((EFI_D_ERROR, "ERROR!! Null Pointer returned by AllocatePool ()\n"));
      ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
      return Status;
    }
    Status = gBS->GetMemoryMap (
                    &EfiMemoryMapSize,
                    EfiMemoryMap,
                    &EfiMapKey,
                    &EfiDescriptorSize,
                    &EfiDescriptorVersion
                    );
    if (EFI_ERROR(Status)) {
      FreePool (EfiMemoryMap); //释放掉前面分配的
    }
  } while (Status == EFI_BUFFER_TOO_SMALL); //一直小一直分配,直到不再小

  DEBUG((DEBUG_ERROR | DEBUG_PAGE,"[CSDN] EfiMemoryMapSize=0x%x EfiDescriptorSize=0x%x EfiMemoryMap=0x%x \n", EfiMemoryMapSize, EfiDescriptorSize, (UINTN)EfiMemoryMap));

  EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)EfiMemoryMap + EfiMemoryMapSize); //映射区结尾
  EfiEntry = EfiMemoryMap; //映射区的开始地址

  DEBUG((DEBUG_ERROR | DEBUG_PAGE,"===========================%S============================== Start\n", L"CSDN MemMap"));

  while (EfiEntry < EfiMemoryMapEnd) { //循环获取各内存类型的信息

      if (EfiEntry->Type == EfiReservedMemoryType){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiReservedMemoryType  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         ReservedMemoryTypePage = ReservedMemoryTypePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiLoaderCode){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiLoaderCode  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         LoaderCodePage = LoaderCodePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiLoaderData){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiLoaderData  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         LoaderDataPage = LoaderDataPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiBootServicesCode){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiBootServicesCode  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         BootServicesCodePage = BootServicesCodePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiBootServicesData){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiBootServicesData  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         BootServicesDataPage = BootServicesDataPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiRuntimeServicesCode){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiRuntimeServicesCode  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         RuntimeServicesCodePage = RuntimeServicesCodePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiRuntimeServicesData){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiRuntimeServicesData  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         RuntimeServicesDataPage = RuntimeServicesDataPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiConventionalMemory){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiConventionalMemory  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         ConventionalMemoryPage = ConventionalMemoryPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiUnusableMemory){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiUnusableMemory  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         UnusableMemoryPage = UnusableMemoryPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiACPIReclaimMemory){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiACPIReclaimMemory  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         ACPIReclaimMemoryPage = ACPIReclaimMemoryPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiACPIMemoryNVS){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiACPIMemoryNVS  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         ACPIMemoryNVSPage = ACPIMemoryNVSPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiMemoryMappedIO){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMemoryMappedIO  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         MemoryMappedIOPage = MemoryMappedIOPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiMemoryMappedIOPortSpace){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMemoryMappedIOPortSpace  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         MemoryMappedIOPortSpacePage = MemoryMappedIOPortSpacePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiPalCode){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiPalCode  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         PalCodePage = PalCodePage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiPersistentMemory){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiPersistentMemory  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         PersistentMemoryPage = PersistentMemoryPage + EfiEntry->NumberOfPages;
      }else if (EfiEntry->Type == EfiMaxMemoryType){
         DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMaxMemoryType  %3d %16lx pn %16lx \n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->NumberOfPages));
         MaxMemoryTypePage = MaxMemoryTypePage + EfiEntry->NumberOfPages;
      }

    EfiEntry = NEXT_MEMORY_DESCRIPTOR(EfiEntry, EfiDescriptorSize);
  }
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiBootServicesData Page Number: %16lx  %dMB\n", BootServicesDataPage, BootServicesDataPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiBootServicesCode Page Number: %16lx  %dMB\n", BootServicesCodePage, BootServicesCodePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiACPIReclaimMemory Page Number: %16lx  %dMB\n", ACPIReclaimMemoryPage, ACPIReclaimMemoryPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiACPIMemoryNVS Page Number: %16lx  %dMB\n", ACPIMemoryNVSPage, ACPIMemoryNVSPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiReservedMemoryType Page Number: %16lx  %dMB\n", ReservedMemoryTypePage, ReservedMemoryTypePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiRuntimeServicesCode Page Number: %16lx  %dMB\n", RuntimeServicesCodePage, RuntimeServicesCodePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiRuntimeServicesData Page Number: %16lx  %dMB\n", RuntimeServicesDataPage, RuntimeServicesDataPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiLoaderCode Page Number: %16lx  %dMB\n", LoaderCodePage, LoaderCodePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiLoaderData Page Number: %16lx  %dMB\n", LoaderDataPage, LoaderDataPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMaxMemoryType Page Number: %16lx  %dMB\n", MaxMemoryTypePage, MaxMemoryTypePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiConventionalMemory Page Number: %16lx  %dMB\n", ConventionalMemoryPage, ConventionalMemoryPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiUnusableMemory Page Number: %16lx  %dMB\n", UnusableMemoryPage, UnusableMemoryPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMemoryMappedIO Page Number: %16lx  %dMB\n", MemoryMappedIOPage, MemoryMappedIOPage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiMemoryMappedIOPortSpace Page Number: %16lx  %dMB\n", MemoryMappedIOPortSpacePage, MemoryMappedIOPortSpacePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiPalCode Page Number: %16lx  %dMB\n", PalCodePage, PalCodePage*4/1024));
  DEBUG((DEBUG_ERROR | DEBUG_PAGE, "[CSDN] EfiPersistentMemory Page Number: %16lx  %dMB\n", PersistentMemoryPage, PersistentMemoryPage*4/1024));

  DEBUG((DEBUG_ERROR | DEBUG_PAGE,"===========================%S============================== End\n", L"CSDN MemMap"));

  return Status;
}

编写inf代码,编译,运行efi文件 

部分运行结果如下:

显示内存类型                                                物理地址                         页数

这是一个memmap dump demo,使用该demo可以得出当前BIOS的mem region分配情况,在shell下同样可以使用内置的memmap 命令来查看当前memory map。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

西门子WinCC开发笔记(一):winCC西门子组态软件介绍、安装

文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/142060535 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、Op…

Mysql Innodb存储引擎原理—链接如下

Mysql Innodb存储引擎| ProcessOn免费在线作图,在线流程图,在线思维导图 ProcessOn是一个在线协作绘图平台&#xff0c;为用户提供强大、易用的作图工具&#xff01;支持在线创作流程图、思维导图、组织结构图、网络拓扑图、BPMN、UML图、UI界面原型设计、iOS界面原型设计等。同…

大数据-124 - Flink State 01篇 状态原理和原理剖析:状态类型 执行分析

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

数学建模笔记—— 模糊综合评价

数学建模笔记—— 模糊综合评价 模糊综合评价1. 模糊数学概述2. 经典集合和模糊集合的基本概念2.1 经典集合2.2 模糊集合和隶属函数1. 基本概念2.模糊集合的表示方法3. 模糊集合的分类4. 隶属函数的确定方法 3. 评价问题概述4. 一级模糊综合评价模型典型例题 5. 多层次模糊综合…

SprinBoot+Vue停车场管理系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

iOS——GCD再学习

GCD 使用GCD好处&#xff0c;具体如下&#xff1a; GCD 可用于多核的并行运算&#xff1b;GCD 会自动利用更多的 CPU 内核&#xff08;比如双核、四核&#xff09;&#xff1b;GCD 会自动管理线程的生命周期&#xff08;创建线程、调度任务、销毁线程&#xff09;&#xff1b…

❤《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案

《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案 文章目录 《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案1、问题一&#xff1a;原生开发中 request请求中返回 的数据无法 使用this传递给 data{}中怎么办&#xff1f;2、刚登录后如何将token信息保存&#xf…

智汇云舟荣膺国家级专精特新“小巨人”企业称号

近日&#xff0c;北京市经济和信息化局发布了经工业和信息化部审核的第六批专精特新“小巨人”企业名单&#xff0c;智汇云舟凭借其在视频孪生领域的卓越贡献和技术实力成功入选&#xff0c;荣膺国家级专精特新“小巨人”企业称号。 专精特新“小巨人”&#xff0c;是目前全国中…

PDF 全文多语言 AI 摘要 API 数据接口

PDF 全文多语言 AI 摘要 API 数据接口 PDF / 文本摘要 AI 生成 PDF 文档摘要 AI 处理 / 智能摘要。 1. 产品功能 支持多语言摘要生成&#xff1b;支持 formdata 格式 PDF 文件流传参&#xff1b;快速处理大文件&#xff1b;基于 AI 模型&#xff0c;持续迭代优化&#xff1b;…

【鸿蒙开发工具报错】Build task failed. Open the Run window to view details.

Build task failed. Open the Run window to view details. 问题描述 在使用deveco-studio 开发工具进行HarmonyOS第一个应用构建开发时&#xff0c;通过Previewer预览页面时报错&#xff0c;报错信息为&#xff1a;Build task failed. Open the Run window to view details.…

哈希表,算法

一.什么是哈希表 哈希表是一种用于快速数据存取的数据结构。它通过哈希函数将键&#xff08;key&#xff09;映射到表中的一个位置&#xff0c;从而实现高效的插入、删除和查找操作。 二.哈希冲突 哈希冲突发生在多个键通过哈希函数映射到哈希表的同一位置时。由于哈希表的大…

【专题】2024年中国游戏出海洞察报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37570 2023 年全球游戏市场规模高达 6205.2 亿美元&#xff0c;且预计未来持续增长&#xff0c;这清晰地展示了该市场的巨大潜力和良好前景。 中国游戏在全球移动游戏市场的份额于 2023 年已达 37%&#xff0c;产业贡献超 30% 的市场…

Redis主从数据同步过程:命令传播、部分重同步、复制偏移量等

请记住胡广一句话&#xff0c;所有的中间件所有的框架都是建立在基础之上&#xff0c;数据结构&#xff0c;计算机网络&#xff0c;计算机原理大伙一定得看透&#xff01;&#xff01;~ 1. Redis数据同步 1.1 数据同步过程 大家有没想过为什么Redis多机要进行数据同步&#…

[数据集][目标检测]水面垃圾检测数据集VOC+YOLO格式2027张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2027 标注数量(xml文件个数)&#xff1a;2027 标注数量(txt文件个数)&#xff1a;2027 标注…

SAP 免费学习网站推荐

1、https://www.guru99.com/ 可以看到有很多的开发语言可以学习。其中就有SAP。 点击SAP菜单后&#xff0c;可以看到每个模块的操作 每个模块下面都有操作的截图&#xff0c;结合翻译软件看的话很容易看懂 2、https://community.sap.com/ 这个是SAP官方的社区&#xff0c…

[数据结构] 开散列法 闭散列法 模拟实现哈希结构(一)

标题&#xff1a;[数据结构] 开散列法 && 闭散列法 模拟实现哈希结构 个人主页&#xff1a;水墨不写bug 目录 一、闭散列法 核心逻辑的解决 i、为什么要设置位置状态&#xff1f;&#xff08;伪删除法的使用&#xff09; ii、哈希函数的设计 接口的实现 1、插入&a…

STM32 RTC实时时钟

RTC实时时钟 BKP可以在VBAT维持供电时&#xff0c;完成主电源掉电时&#xff0c;保存少量数据的任务。备份寄存器和VBAT引脚同时存在&#xff0c;更多是为了服务RTC的。 目前&#xff0c;Linux、Windows、安卓这些系统&#xff0c;底层的计时系统都是使用的Unix时间戳&#xf…

网络编程(UDP)

UDP编程 UDP&#xff1a;全双工通信、面向无连接、不可靠 UDP&#xff08;User Datagram Protocol&#xff09;用户数据报协议&#xff0c;是不可靠的无连接的协议。在数据发送前&#xff0c;因为不需要进行连接&#xff0c;所以可以进行高效率的数据传输。 适用场景 发送小尺寸…

Anaconda安装和环境配置教程(深度学习准备)

目录 1.下载选择 2.prompt配置 3.虚拟环境配置 4.检查是不是安装成功 5.安装jupter 6.关闭anaconda重新进入 7.总结 1.下载选择 我第一次使用的这个官网上面的邮箱的方式下载的&#xff0c;但是这个方式真的特别慢&#xff0c;于是用了这个清华的镜像网站&#xff0c;网…

基于JAVA+SpringBoot+Vue的工程教育认证的计算机课程管理平台

基于JAVASpringBootVue的工程教育认证的计算机课程管理平台 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接…