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

news2024/11/14 23:07:06

本文我们来熟悉下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

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

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

相关文章

超详细十大排序算法

一、排序总结 性能指标&#xff1a;稳定性&#xff0c;时间复杂度&#xff0c;空间复杂度 排序算法的稳定性是指当排序的元素中存在相同的值时&#xff0c;排序算法能够保持它们原先的相对顺序。如果一个排序算法是稳定的&#xff0c;那么在排序后&#xff0c;相同值的元素在原…

uni-admin:基于uni-app与uniCloud的云端管理后台开发神器

随着移动互联网和云计算技术的飞速发展&#xff0c;越来越多的企业和开发者开始寻求一种快速、高效且灵活的解决方案来构建自己的管理后台系统。在这样一个背景下&#xff0c;uni-admin应运而生&#xff0c;它基于uni-app和uniCloud&#xff0c;为开发者提供了一个功能强大、易…

YOLO检测环境安装配置

YOLO介绍 YOLO学习手册&#xff1a;YOLO教程 YOLO [ˈjoʊloʊ]&#xff08;You Only Look Once&#xff09;是一种快速而准确的目标检测算法&#xff0c;由Joseph Redmon等人在2016年提出。YOLO被广泛应用于计算机视觉领域&#xff0c;包括实时视频分析、自动驾驶、安防监控、…

理解 Bearer Token:什么是它以及如何运作?

在当前数字化时代&#xff0c;网络安全尤为关键。随着技术快速进步&#xff0c;需求日益增长&#xff0c;保障应用程序中用户数据的安全成为开发者们的首要任务。其中&#xff0c;Bearer Token 作为一种高效的验证策略&#xff0c;在防止未授权访问中发挥着不可或缺的作用。 解…

高清实拍类型视频素材去哪里找?高清实拍素材网站分享

在这篇文章中&#xff0c;我将为大家介绍一些高清实拍类型的视频素材资源&#xff0c;这些资源对于我们新媒体创作者来说至关重要。优质的视频素材能显著提升作品的吸引力&#xff0c;因此选择合适的视频素材平台非常关键。下面我将详细介绍几个非常实用的视频素材平台&#xf…

CV技术指南 | 其实Mamba是一种线性注意力?清华大学黄高团队揭秘开视觉Mamba的真实面目!

本文来源公众号“CV技术指南”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;其实Mamba是一种线性注意力&#xff1f;清华大学黄高团队揭秘开视觉Mamba的真实面目&#xff01; 前言 本文揭示了 Mamba 模型与 Linear Attention …

shell脚本和变量

一、shell脚本 脚本就是可运行的代码的集合&#xff0c;脚本语言&#xff08;计算机语言&#xff09;的特点&#xff1a;从上到下&#xff0c;按行执行。 Python&#xff1a;脚本语言&#xff0c;格式更严谨&#xff0c;严格地执行缩进。也是从上到下按行执行。 shell脚本运…

Linux文件权限信息和Linux文件与文件夹的管理

目录 前言一、系统环境二、Linux文件权限信息2.1 查看Linux文件权限信息2.2 修改Linux文件权限信息2.2.1 chmod命令2.2.2 chown命令 三、Linux文件与目录的管理3.1 查看文件或文件夹3.1.1 查看文件内容3.1.2 查看文件夹内容 3.2 新增文件或文件夹3.2.1 新增文件3.2.2 新增文件夹…

从零开始利用MATLAB进行FPGA设计(七)固化程序

FPGA开发板&#xff1a;AX7020&#xff1b;Xilinx 公司的 Zynq7000 系列的芯片XC7Z020-2CLG400I&#xff0c;400引脚 FBGA 封装。 HARDWARE PLATFORM在2019以后的vivado中已经没有了。本期实际上已经与MATLAB无关&#xff0c;主要介绍通过VITIS等方法进行程序固化。 目录 ZY…

光伏+储能,势在必行!

随着全球对可再生能源需求的不断增长&#xff0c;光伏行业正迎来前所未有的发展机遇。然而&#xff0c;光伏发电的间歇性和不稳定性特点&#xff0c;使得储能技术成为解决其并网难题、提高能源利用效率的关键。因此&#xff0c;“光伏储能”的组合&#xff0c;已成为行业发展的…

老生常谈!程序员为什么要阅读源代码?

大家好&#xff0c;我是码农先森。 阅读源码这是一个老生常谈的话题了&#xff0c;但又是很多人想做又没有付出行动的事情。前段时间我研究了 Swoole 的源代码&#xff0c;并且输出了系列的源码分析文章「感兴趣的朋友可以翻阅以前的文章」。虽然这个过程很枯燥和艰难&#xf…

c++ 6.11

作业&#xff1a; 思维导图&#xff1a; 作业题&#xff1a; 搭建一个货币的场景&#xff0c;创建一个名为 RMB 的类&#xff0c;该类具有整型私有成员变量 yuan&#xff08;元&#xff09;、jiao&#xff08;角&#xff09;和 fen&#xff08;分&#xff09;&#xff0c;并且…

VMware Workstation虚拟机进入U盘PE系统

注意事项 VMware Workstation虚拟机版本不能高于16.1.2版本&#xff01;&#xff01;&#xff01; 本实验使用的版本如下 实际操作 在已安装好的虚拟机处右键&#xff0c;点击设置。虚拟机安装win10教程请参考VMware Workstation安装win10操作系统-CSDN博客 在弹出的窗口点击…

卷产品 、卷生态,百度这款AI产品卷到了第一

万万没想到&#xff0c;最先跑出来的AI杀手级应用竟然是百度文库。 6月7日&#xff0c;“AI产品榜”&#xff08;aicpb.com&#xff09;发布了最新的5月国内总榜榜单&#xff0c;百度文库AI功能以6536万的月访问量一骑绝尘&#xff0c;连续两个月霸榜第一。 乍看有些不可思议&…

2-1基于matlab的拉普拉斯金字塔图像融合算法

基于matlab的拉普拉斯金字塔图像融合算法&#xff0c;可以使部分图像模糊的图片清楚&#xff0c;也可以使图像增强。程序已调通&#xff0c;可直接运行。 2-1 图像融合 拉普拉斯金字塔图像融合 - 小红书 (xiaohongshu.com)

docker安装elasticsearch8和kibana,带ik分词器

全文目录,一步到位 1.前言简介1.1 专栏传送门 2. elasticsearch和kibana安装2.1 准备工作2.1.1 下载elasticsearch2.1.2 下载kibana镜像 2.2 创建并运行es容器2.2.1 创建es网络2.2.2 创建es容器2.2.3 放行端口2.2.4 访问查看结果> 步骤一: 点击高级, 然后继续访问> 步骤二…

使用#sortablejs插件对表格中拖拽行排序#Vue3#后端接口数据

使用#sortablejs对表格中拖拽行排序#Vue3#后端接口数据 *效果&#xff1a; 拖动表格行排序 首先安装插件sortable npm install sortablejs --save代码&#xff1a; <template><!-- sortable.js 进行表格排序 --><!-- 演示地址 --><div class"dem…

我要成为算法高手-双指针篇

目录 什么是双指针?问题1&#xff1a;移动零问题2&#xff1a;复写零问题3&#xff1a;快乐数问题4&#xff1a;盛最多水的容器问题5&#xff1a;有效三角形个数问题6&#xff1a;查找总价格和为目标值的两个商品(两数之和)问题7&#xff1a;三数之和问题8&#xff1a;四数之和…

Nextjs 集成TinyMCE实现富文本编辑器

目录 一、导入依赖 二、动态导入 三、完整案例 四、参考文档 一、导入依赖 yarn add tinymce/tinymce-react二、动态导入 import dynamic from next/dynamic;const Editor dynamic(() > import(tinymce/tinymce-react).then(mod > mod.Editor),{ssr: false} ); 三…

力扣hot100:394. 字符串解码(递归/括号匹配,字符串之间相对顺序)

LeetCode&#xff1a;394. 字符串解码 本题容易想到用递归处理&#xff0c;在写递归时主要是需要明确自己的递归函数的定义。 不过我们也可以利用括号匹配的方式使用栈进行处理。 1、递归 定义递归函数string GetString(string & s,int & i); 表示处理处理整个numbe…