鸿蒙轻内核A核源码分析系列四(2) 虚拟内存

本文我们来熟悉下OpenHarmony鸿蒙轻内核提供的虚拟内存(Virtual memory)管理模块。

本文中所涉及的源码,以OpenHarmony LiteOS-A内核为例,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_a 获取。如果涉及开发板,则默认以hispark_taurus为例。

我们首先了解了虚拟内存管理的结构体、相关宏定义,接着会分析内核虚拟地址空间和用户进程虚拟地址空间如何初始化,然后分析虚拟内存区间常用操作包含查找、申请和释放等,最后分析动态内存堆的申请、释放接口的源代码,并简单介绍下内存区间预留接口源代码。

1、 虚拟内存管理相关的结构体

在文件kernel/base/include/los_vm_map.h中定义了进程地址空间结构体LosVmSpace,进程地址区间结构体LosVmMapRegion和进程地址区间范围结构体LosVmMapRange。每个用户态进程会创建自己的进程空间,内核态会创建2个进程空间,分别g_kVmSpaceg_vMallocSpace。从进程空间申请的虚拟内存块使用进程区间LosVmMapRegion来表示。每个进程空间维护一个红黑树来链接各个进程区间。

1.1 虚拟内存地址空间结构体LosVmSpace

typedef struct VmSpace {
    LOS_DL_LIST         node;           /**< 地址空间双向链表 */
    LosRbTree           regionRbTree;   /**< 地址区间的红黑树根节点 */
    LosMux              regionMux;      /**< 地址区间的红黑树的互斥锁 */
    VADDR_T             base;           /**< 地址空间开始地址 */
    UINT32              size;           /**< 地址空间大小 */
    VADDR_T             heapBase;       /**< 地址空间的堆开始地址heapBase */
    VADDR_T             heapNow;        /**< 地址空间的堆开始地址heapNow */
    LosVmMapRegion      *heap;          /**< 地址空间的地址区间 */
    VADDR_T             mapBase;        /**< 地址空间的映射区开始地址 */
    UINT32              mapSize;        /**< 地址空间的映射区大小 */
    LosArchMmu          archMmu;        /**< 地址空间的MMU结构体 */
#ifdef LOSCFG_DRIVERS_TZDRIVER
    VADDR_T             codeStart;      /**< 用户进程代码区开始地址 */
    VADDR_T             codeEnd;        /**< 用户进程代码区结束地址 */
#endif
} LosVmSpace;

1.2 虚拟内存地址区间LosVmMapRegion

typedef struct VmMapRange {
    VADDR_T             base;           /**< 虚拟内存地址区间开始地址 */
    UINT32              size;           /**< 虚拟内存地址区间大小 */
} LosVmMapRange;
......
struct VmMapRegion;
typedef struct VmMapRegion LosVmMapRegion;
......
struct VmMapRegion {
    LosRbNode           rbNode;         /**<  地址区间红黑树节点 */
    LosVmSpace          *space;         /**<  地址区间所在的地址空间 */
    LOS_DL_LIST         node;           /**<  地址区间双向链表 */
    LosVmMapRange       range;          /**<  地址区间地址范围 */
    VM_OFFSET_T         pgOff;          /**<  地址区间页偏移 */
    UINT32              regionFlags;    /**<  地址区间标记: cow, user_wired */
    UINT32              shmid;          /**<  共享地址区间编号 */
    UINT8               forkFlags;      /**<  地址区间fork标记: COPY, ZERO, */
    UINT8               regionType;     /**<  地址区间类型: ANON, FILE, DEV */
    union {
        struct VmRegionFile {
            unsigned int fileMagic;
            struct file *file;
            const LosVmFileOps *vmFOps;
        } rf;
        struct VmRegionAnon {
            LOS_DL_LIST  node;          /**< 地址区间类型的双向链表 */
        } ra;
        struct VmRegionDev {
            LOS_DL_LIST  node;          /**< 地址区间类型的双向链表 */
            const LosVmFileOps *vmFOps;
        } rd;
    } unTypeData;
};

2、 虚拟内存相关的宏定义

文件kernel/base/include/los_vm_common.hkernel/base/include/los_vm_zone.h定义了虚拟内存相关的宏。对于32位系统,虚拟进程空间大小为4GiB,OpenHarmony鸿蒙轻内核当前支持32位系统。⑴和⑵定义了用户进程虚拟地址空间的开始地址和大小,⑶是用户虚拟进程空间的结束地址,接着定义的是用户虚拟进程空间的堆区、映射区的开始地址和大小。

/* user address space, defaults to below kernel space with a 16MB guard gap on either side */
    #ifndef USER_ASPACE_BASE
⑴  #define USER_ASPACE_BASE            ((vaddr_t)0x01000000UL)
    #endif
    #ifndef USER_ASPACE_SIZE
⑵  #define USER_ASPACE_SIZE            ((vaddr_t)KERNEL_ASPACE_BASE - USER_ASPACE_BASE - 0x01000000UL)
    #endif

⑶  #define USER_ASPACE_TOP_MAX         ((vaddr_t)(USER_ASPACE_BASE + USER_ASPACE_SIZE))
    #define USER_HEAP_BASE              ((vaddr_t)(USER_ASPACE_TOP_MAX >> 2))
    #define USER_MAP_BASE               ((vaddr_t)(USER_ASPACE_TOP_MAX >> 1))
    #define USER_MAP_SIZE               ((vaddr_t)(USER_ASPACE_SIZE >> 3))

内核虚拟进程空间的宏定义如下,⑴处定义内核进程地址空间开始地址和大小,⑵处定义内核非缓存虚拟地址空间开始地址和大小,⑶处定义虚拟动态分配地址空间开始地址和大小,⑷处定义外设开始地址和大小,⑸处定义外设缓存区开始地址和大小,⑹处定义外设非缓存区开始地址和大小。

    #ifdef LOSCFG_KERNEL_MMU
    #ifdef LOSCFG_TEE_ENABLE
    #define KERNEL_VADDR_BASE       0x41000000
    #else
    #define KERNEL_VADDR_BASE       0x40000000
    #endif
    #else
    #define KERNEL_VADDR_BASE       DDR_MEM_ADDR
    #endif
    #define KERNEL_VADDR_SIZE       DDR_MEM_SIZE

    #define SYS_MEM_BASE            DDR_MEM_ADDR
    #define SYS_MEM_END             (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT)

    #define _U32_C(X)  X##U
    #define U32_C(X)   _U32_C(X)

    #define KERNEL_VMM_BASE         U32_C(KERNEL_VADDR_BASE)
    #define KERNEL_VMM_SIZE         U32_C(KERNEL_VADDR_SIZE)

⑴  #define KERNEL_ASPACE_BASE      KERNEL_VMM_BASE
    #define KERNEL_ASPACE_SIZE      KERNEL_VMM_SIZE

    /* Uncached vmm aspace */
⑵  #define UNCACHED_VMM_BASE       (KERNEL_VMM_BASE + KERNEL_VMM_SIZE)
    #define UNCACHED_VMM_SIZE       DDR_MEM_SIZE

⑶  #define VMALLOC_START           (UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE)
    #define VMALLOC_SIZE            0x08000000

    #ifdef LOSCFG_KERNEL_MMU
⑷  #define PERIPH_DEVICE_BASE      (VMALLOC_START + VMALLOC_SIZE)
    #define PERIPH_DEVICE_SIZE      U32_C(PERIPH_PMM_SIZE)
⑸  #define PERIPH_CACHED_BASE      (PERIPH_DEVICE_BASE + PERIPH_DEVICE_SIZE)
    #define PERIPH_CACHED_SIZE      U32_C(PERIPH_PMM_SIZE)
⑹  #define PERIPH_UNCACHED_BASE    (PERIPH_CACHED_BASE + PERIPH_CACHED_SIZE)
    #define PERIPH_UNCACHED_SIZE    U32_C(PERIPH_PMM_SIZE)
    #else
    #define PERIPH_DEVICE_BASE      PERIPH_PMM_BASE
    #define PERIPH_DEVICE_SIZE      U32_C(PERIPH_PMM_SIZE)
    #define PERIPH_CACHED_BASE      PERIPH_PMM_BASE
    #define PERIPH_CACHED_SIZE      U32_C(PERIPH_PMM_SIZE)
    #define PERIPH_UNCACHED_BASE    PERIPH_PMM_BASE
    #define PERIPH_UNCACHED_SIZE    U32_C(PERIPH_PMM_SIZE)
    #endif

虚拟地址空间分布示意图如下:

3、进程地址空间初始化

虚拟进程空间分用户虚拟进程空间和内核虚拟进程空间,每个用户进程都会创建属于自己的进程空间。内核会初始化2个进程空间。下文详细介绍。

3.1 内核虚拟地址空间初始化

3.1.1 函数OsKSpaceInit

函数OsKSpaceInit()初始化内核进程虚拟地址空间,⑴处的函数初始化虚拟空间链表互斥锁g_vmSpaceListMux,在操作内核进程空间时需要持有该互斥锁。⑵处开始的函数2个函数OsKernVmSpaceInitOsVMallocSpaceInit分别初始化内核进程虚拟空间g_kVmSpace和内核动态分配进程空间g_vMallocSpace。传入的第2个参数由函数OsGFirstTableGet()获取,即g_firstPageTable,这是内核的2个进程空间使用的一级页表基地址,大小为0x4000字节,后文在设置转化表基地址MMU virtTtb时会使用。下文会详细分析这2个函数。

VOID OsKSpaceInit(VOID)
{
⑴  OsVmMapInit();
⑵  OsKernVmSpaceInit(&g_kVmSpace, OsGFirstTableGet());
    OsVMallocSpaceInit(&g_vMallocSpace, OsGFirstTableGet());
}

3.1.2 函数OsKernVmSpaceInit

函数OsKernVmSpaceInit()初始化内核进程虚拟地址空间,⑴处设置地址空间的开始地址和大小,⑵处设置地址空间映射区的开始地址和大小,对于内核虚拟地址空间g_kVmSpace,这2个开始地址和大小是一样的。⑶处调用通用的地址空间初始化函数,后文分析此函数。

BOOL OsKernVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
{
⑴  vmSpace->base = KERNEL_ASPACE_BASE;
    vmSpace->size = KERNEL_ASPACE_SIZE;
⑵  vmSpace->mapBase = KERNEL_VMM_BASE;
    vmSpace->mapSize = KERNEL_VMM_SIZE;
#ifdef LOSCFG_DRIVERS_TZDRIVER
    vmSpace->codeStart = 0;
    vmSpace->codeEnd = 0;
#endif
⑶   return OsVmSpaceInitCommon(vmSpace, virtTtb);
}

3.1.3 函数OsVMallocSpaceInit

函数OsVMallocSpaceInit()初始化内核堆虚拟空间,设置的虚拟地址空间和映射区地址空间的开始地址和大小也是一样的,代码和函数OsKernVmSpaceInit()类似,不再赘述。

BOOL OsVMallocSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
{
    vmSpace->base = VMALLOC_START;
    vmSpace->size = VMALLOC_SIZE;
    vmSpace->mapBase = VMALLOC_START;
    vmSpace->mapSize = VMALLOC_SIZE;
#ifdef LOSCFG_DRIVERS_TZDRIVER
    vmSpace->codeStart = 0;
    vmSpace->codeEnd = 0;
#endif
    return OsVmSpaceInitCommon(vmSpace, virtTtb);
}

3.2 用户进程虚拟地址空间初始化

3.2.1 函数OsCreateUserVmSpace

在创建进程时,会调用函数OsCreateUserVmSpace()创建用户进程的虚拟地址空间。⑴为虚拟地址空间结构体申请内存。⑵申请一个内存页,并调用memset_s()初始化为0,这个内存页虚拟地址会作为页表转换基地址TTB(translation table base,ttb),虚实映射的页表会保存在这个内存区域。在虚实映射章节,会讲述为什么申请4KiB大小内存。⑶处调用函数OsUserVmSpaceInit初始化用户进程虚拟地址空间。⑷处获取虚拟地址对应的物理页结构体地址。如果初始化失败,则释放申请的内存。⑸处把物理页加入虚拟空间中的MMU的页表链表中,这个链表维护该进程空间映射的内存页。

LosVmSpace *OsCreateUserVmSpace(VOID)
{
    BOOL retVal = FALSE;

⑴   LosVmSpace *space = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmSpace));
    if (space == NULL) {
        return NULL;
    }

⑵  VADDR_T *ttb = LOS_PhysPagesAllocContiguous(1);
    if (ttb == NULL) {
        (VOID)LOS_MemFree(m_aucSysMem0, space);
        return NULL;
    }

    (VOID)memset_s(ttb, PAGE_SIZE, 0, PAGE_SIZE);
⑶  retVal = OsUserVmSpaceInit(space, ttb);
⑷  LosVmPage *vmPage = OsVmVaddrToPage(ttb);
    if ((retVal == FALSE) || (vmPage == NULL)) {
        (VOID)LOS_MemFree(m_aucSysMem0, space);
        LOS_PhysPagesFreeContiguous(ttb, 1);
        return NULL;
    }
⑸   LOS_ListAdd(&space->archMmu.ptList, &(vmPage->node));

    return space;
}

3.2.2 函数OsUserVmSpaceInit

函数OsUserVmSpaceInit初始化用户进程虚拟地址空间,⑴处设置虚拟地址空间的开始地址和大小。⑵处设置虚拟空间的映射区的开始地址和大小,开始地址在虚拟空间开始地址的1/2处,大小为用户虚拟空间大小的1/8。⑶处设置虚拟空间的堆区,开始地址为虚拟空间开始地址的1/4处。

BOOL OsUserVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb)
{
⑴  vmSpace->base = USER_ASPACE_BASE;
    vmSpace->size = USER_ASPACE_SIZE;
⑵  vmSpace->mapBase = USER_MAP_BASE;
    vmSpace->mapSize = USER_MAP_SIZE;
⑶  vmSpace->heapBase = USER_HEAP_BASE;
    vmSpace->heapNow = USER_HEAP_BASE;
    vmSpace->heap = NULL;
#ifdef LOSCFG_DRIVERS_TZDRIVER
    vmSpace->codeStart = 0;
    vmSpace->codeEnd = 0;
#endif
    return OsVmSpaceInitCommon(vmSpace, virtTtb);
}

3.3 虚拟地址空间初始化的通用函数

3.3.1 函数OsVmSpaceInitCommon

函数OsVmSpaceInitCommon用于进程虚拟地址空间的通用部分的初始化,⑴处初始化地址空间的红黑树根节点。⑵处初始化地址空间的地址区间操作互斥锁。⑶处把新创建的地址空间挂在虚拟地址空间双向链表g_vmSpaceList上。⑷处继续调用函数OsArchMmuInit()完成地址空间MMU部分的初始化。

STATIC BOOL OsVmSpaceInitCommon(LosVmSpace *vmSpace, VADDR_T *virtTtb)
{
⑴  LOS_RbInitTree(&vmSpace->regionRbTree, OsRegionRbCmpKeyFn, OsRegionRbFreeFn, OsRegionRbGetKeyFn);

⑵  status_t retval = LOS_MuxInit(&vmSpace->regionMux, NULL);
    if (retval != LOS_OK) {
        VM_ERR("Create mutex for vm space failed, status: %d", retval);
        return FALSE;
    }

    (VOID)LOS_MuxAcquire(&g_vmSpaceListMux);
⑶  LOS_ListAdd(&g_vmSpaceList, &vmSpace->node);
    (VOID)LOS_MuxRelease(&g_vmSpaceListMux);

⑷   return OsArchMmuInit(&vmSpace->archMmu, virtTtb);
}

3.3.2 函数OsArchMmuInit

函数OsArchMmuInit()用于初始化虚拟地址空间的MMU,MMU在后续系列会详细分析,此处快速了解一下即可。⑴处获取地址空间编号,如果获取失败则返回FALSE。⑵初始化MMU互斥锁,如果初始化失败则返回FALSE。⑶处初始化内存页双向链表。⑷处设置MMU的TTB虚拟地址。⑸处设置MMU的TTB物理地址,TTB虚拟地址基于内核虚拟地址空间开始地址的偏移(UINTPTR)virtTtb - KERNEL_ASPACE_BASE加上物理地址就等于TTB物理地址。

BOOL OsArchMmuInit(LosArchMmu *archMmu, VADDR_T *virtTtb)
{
#ifdef LOSCFG_KERNEL_VM
⑴   if (OsAllocAsid(&archMmu->asid) != LOS_OK) {
        VM_ERR("alloc arch mmu asid failed");
        return FALSE;
    }
#endif

⑵   status_t retval = LOS_MuxInit(&archMmu->mtx, NULL);
    if (retval != LOS_OK) {
        VM_ERR("Create mutex for arch mmu failed, status: %d", retval);
        return FALSE;
    }

⑶  LOS_ListInit(&archMmu->ptList);
⑷  archMmu->virtTtb = virtTtb;
⑸  archMmu->physTtb = (VADDR_T)(UINTPTR)virtTtb - KERNEL_ASPACE_BASE + SYS_MEM_BASE;
    return TRUE;
}

4、虚拟地址区间常用操作

虚拟地址区间操作分为查找、申请、释放等操作。

4.1 函数LOS_RegionFind

⑴处的函数LOS_RegionFind用于在进程虚拟地址空间内查找并返回指定虚拟地址对应的虚拟地址区间,两个传入参数分别是虚拟地址空间和虚拟内存地址。该函数有个兄弟函数LOS_RegionRangeFind(),见⑶处代码,可以用于在进程空间内查找并返回指定地址范围对应的虚拟地址区间,三个传入参数分别指定指定进程空间、虚拟内存开始地址和地址长度(长度单位字节)。这2个函数都调用函数OsFindRegion()实现地址区间的查找,⑵处的第3个参数为1的原因是地址区间是左闭右开区间,区间的结束地址会减1。下文会分析该函数的代码。

⑴   LosVmMapRegion *LOS_RegionFind(LosVmSpace *vmSpace, VADDR_T addr)
    {
        LosVmMapRegion *region = NULL;

        (VOID)LOS_MuxAcquire(&vmSpace->regionMux);
⑵      region = OsFindRegion(&vmSpace->regionRbTree, addr, 1);
        (VOID)LOS_MuxRelease(&vmSpace->regionMux);

        return region;
    }
⑶  LosVmMapRegion *LOS_RegionRangeFind(LosVmSpace *vmSpace, VADDR_T addr, size_t len)
    {
        LosVmMapRegion *region = NULL;

        (VOID)LOS_MuxAcquire(&vmSpace->regionMux);
        region = OsFindRegion(&vmSpace->regionRbTree, addr, len);
        (VOID)LOS_MuxRelease(&vmSpace->regionMux);

        return region;
    }

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://qr21.cn/FV7h05

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

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

相关文章

基于微信小程序的“最多跑一次”警务信息管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

Linux用户,用户组,所有者权限分配,sftp用户权限分配

注意以下命令执行需要在root用户下执行 tenant命令切换至root命令 sudo -do root 删除用户信息 1.不删除用户主目录 userdel user_name 2.删除用户主目录 userdel -r user_name usermod命令修改用户账户权限 更改用户名 sudo usermod -l newusername oldusername 更…

亚马逊竞品分析之如何查找竞品

初选之后,要对产品进行竞品分析,查找竞品的方法: 1.Best Seller榜单查找 进入到该类目的BS榜单去找跟你选中的产品的竞品 看完BS榜单会找出一部分竞品 这个找相似也可以点击,是插件的一个以图搜图的功能,不过有的时候不太好使,某些同款产品可能搜不到。 Edge浏览器搭…

The First项目报告:Stargate Finance重塑跨链金融的未来

Stargate Finance是一个基于LayerZero协议的去中心化金融平台&#xff0c;自2022年3月由LayerZero Labs创建以来&#xff0c;一直致力于为不同区块链之间的资产转移提供高效、低成本的解决方案。凭借其独特的跨链技术和丰富的DeFi服务&#xff0c;Stargate Finance已成为连接不…

Vue3相关语法内容,组件传值,事件监听,具名插槽。

1、Vue3相关语法内容 赋值语句(ref、reactive系列)组件传值(父子&#xff0c;子父)watch&#xff0c;watchEffect监听slot具名插槽 1、赋值语法&#xff08;ref&#xff0c;reactive&#xff09; 1.1、ref 、isRef、 shallowRef、triggerRef、customRef 支持所有的类型&…

视觉SLAM14精讲——相机与图像3.2

视觉SLAM14精讲 三维空间刚体运动1.0三维空间刚体运动1.1三维空间刚体运动1.2李群与李代数2.1相机与图像3.1 视觉SLAM14精讲——相机与图像3.2 视觉SLAM14精讲畸变有关重投影误差缩放实际使用 畸变 相机畸变是相机镜头光学缺陷所致的缺陷&#xff0c; 在光学领域这种问题是没…

【学习笔记】使用gcc编译程序并检查依赖的库

编译 gcc echo.c -o app -lfcgi-o app&#xff1a;指定编译后的输出文件名为 app。 -lfcgi&#xff1a;告诉编译器链接 FastCGI 库。 检查 ldd appldd 是一个在 Unix 和类 Unix 系统中用来打印一个已编译的程序所依赖的共享库列表的命令。当你运行 ldd app 命令时&#xff0…

机器人建模、运动学与动力学仿真分析(importrobot,loadrobot,smimport)

机器人建模、运动学与动力学仿真分析是机器人设计和开发过程中的关键步骤。 一、机器人建模 机器人建模是描述机器人物理结构和运动特性的过程。其中&#xff0c;URDF&#xff08;Unified Robot Description Format&#xff09;是一种常用的机器人模型描述方法。通过URDF&…

【CTS】android CTS测试

android CTS测试 1.硬件准备2. 软件准备3. 下载 CTS3.1 cts3.2 解压 CTS 包&#xff1a; 4 配置adb fastboot5 检查 Java 版本6 安装aapt26.1 下载并安装 Android SDK6.2 找到 aapt2 工具6.3 配置环境变量 7. 准备测试设备8. 运行 CTS 测试8.1 启动 CTS&#xff1a; 9. 查看测试…

vue-cli 快速入门

vue-cli &#xff08;目前向Vite发展&#xff09; 介绍&#xff1a;Vue-cli 是Vue官方提供一个脚手架&#xff0c;用于快速生成一个Vue的项目模板。 Vue-cli提供了如下功能&#xff1a; 统一的目录结构 本地调试 热部署 单元测试 集成打包上线 依赖环境&#xff1a;NodeJ…

第8章 函数

第8章 函数 8.1 定义函数8.1.1 向函数传递信息8.1.2 实参和形参 8.2 传递实参8.2.1 位置实参8.2.2 关键字实参8.2.3 默认值 8.3 返回值8.3.1 返回简单值8.3.2 让实参变成可选的8.3.3 返回字典8.3.4 结合使用函数和 while 循环 8.4 传递列表8.4.1 在函数中修改列表8.4.2 禁止函数…

DeepSpeed Mixture-of-Quantization (MoQ)

属于QAT (Quantization-Aware Training)的一种&#xff0c;训练阶段用量化。 特点是&#xff1a; 1. 从16-bit INT开始训练&#xff0c;逐渐减1bit&#xff0c;训练一些steps就减1bit&#xff0c;直至减至8bit INT&#xff1b; 2. &#xff08;可选&#xff0c;不一定非用&a…

Linux-笔记 全志平台镜像中添加git提交号

引言 通过在镜像中某个位置添加git提交号&#xff0c;可以方便排查与追溯是哪个提交编译出来的。 这里使用的全志T113平台&#xff0c;SDK为Tina5.0。 实现的办法有&#xff1a; 1、内核/proc/cpuinfo的打印信息及设备树中查看提交号 2、从设备树查看提交号 3、从uboot启动…

代码随想录-Day29

491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-25 多点电容触摸屏实验

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

双重循环、多重循环程序设计

双重循环格式&#xff1a; for(循环条件1){ 语句1&#xff1b; for(循环条件2){ 语句2&#xff1b; } } 例题1&#xff1a;输入一个整数n&#xff0c;输出一个n层的*三角形塔&#xff08;完成例1&#xff09;。 输入样例&#xff1a;6 输出样例&#xff1a; * ** *** **…

Codeforces Global Round 26

Codeforces Global Round 26 Codeforces Global Round 26 A. Strange Splitting 题意&#xff1a;非空数组的范围定义为该数组的最大值减去最小值&#xff0c;现在给出长度大于 n n n的整数数组 a a a&#xff0c;将其分成两组&#xff0c;每组至少一个元素&#xff0c;且两…

13DHCP 原理与配置

目录 2.1 DHCP工作原理 1、了解DHCP服务 2、使用DHCP的好处 3、DHCP的分配方式 4、DHCP的租约过程 2.2 使用 DHCP动态配置主机地址 2.2.1 配置DHCP服务器 1、安装DHCP服务器软件 2、建立主配置文件dhcpd.conf 3、 启动dhcpd服务 2.2.2 使用DHCP客户端 2.3 D…

WPS JSA 宏脚本入门和样例

1入门 WPS window版本才支持JSA宏的功能。 可以自动化的操作文档中的一些内容。 参考文档&#xff1a; WPS API 参考文档&#xff1a;https://open.wps.cn/previous/docs/client/wpsLoad 微软的Word API文档&#xff1a;Microsoft.Office.Interop.Word 命名空间 | Microsoft …

阿赵UE引擎C++编程学习笔记——场景加载和切换

大家好&#xff0c;我是阿赵。   继续学习UE引擎&#xff0c;这次来学习一下切换和加载场景的各种做法。 一、 蓝图实现 1、 切换关卡 所谓切换关卡&#xff0c;就是从当前关卡进入到一个新的关卡&#xff0c; 旧关卡的数据将会被放弃。进入新的关卡后&#xff0c;将会执行…