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

4.2 函数LOS_RegionAlloc

函数LOS_RegionAlloc用于从地址空间中申请空闲的虚拟地址区间。参数较多,LosVmSpace *vmSpace指定虚拟地址空间,VADDR_T vaddr指定虚拟地址,当为空时,从映射区申请虚拟地址;当不为空时,使用该虚拟地址。如果该虚拟地址已经被映射,会先相应的解除映射处理等。size_t len指定要申请的地区区间的长度。UINT32 regionFlags指定地区区间的标签。VM_OFFSET_T pgoff指定内存页偏移值。

我们具体看下代码,⑴处如果指定的虚拟地址为空,则调用函数OsAllocRange()申请内存。⑵如果指定的虚拟地址不为空,则调用函数OsAllocSpecificRange申请虚拟内存,下文会详细分析这2个申请函数。⑶处创建虚拟内存地址区间,然后指定地址区间的地址空间为当前空间vmSpace。⑷处把创建的地址区间插入地址空间的红黑树中。

LosVmMapRegion *LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff)
{VADDR_T rstVaddr;LosVmMapRegion *newRegion = NULL;BOOL isInsertSucceed = FALSE;/*** If addr is NULL, then the kernel chooses the address at which to create the mapping;* this is the most portable method of creating a new mapping.  If addr is not NULL,* then the kernel takes it as where to place the mapping;*/(VOID)LOS_MuxAcquire(&vmSpace->regionMux);if (vaddr == 0) {
⑴        rstVaddr = OsAllocRange(vmSpace, len);} else {/* if it is already mmapped here, we unmmap it */
⑵      rstVaddr = OsAllocSpecificRange(vmSpace, vaddr, len, regionFlags);if (rstVaddr == 0) {VM_ERR("alloc specific range va: %#x, len: %#x failed", vaddr, len);goto OUT;}}if (rstVaddr == 0) {goto OUT;}⑶  newRegion = OsCreateRegion(rstVaddr, len, regionFlags, pgoff);if (newRegion == NULL) {goto OUT;}newRegion->space = vmSpace;
⑷  isInsertSucceed = OsInsertRegion(&vmSpace->regionRbTree, newRegion);if (isInsertSucceed == FALSE) {(VOID)LOS_MemFree(m_aucSysMem0, newRegion);newRegion = NULL;}OUT:(VOID)LOS_MuxRelease(&vmSpace->regionMux);return newRegion;
}

4.3 函数LOS_RegionFree

函数LOS_RegionFree用于释放地区区间到地址空间中。⑴进行参数校验,参数不能为空。⑵处如果开启了虚拟文件系统宏,并且地址区间是有效的文件类型,则调用函数OsFilePagesRemove。⑶处如果开启了共享内存,并且地址区间是共享的,则调用函数OsShmRegionFree释放共享内存区间,分析共享内存部分时再详细看该函数的代码。⑷如果地址区间是设备类型的,则调用函数OsDevPagesRemove解除映射,否则执行⑸。这些函数都涉及虚实映射,会在虚实映射章节分析这些函数。⑹处把地址区间从红黑树上移除,并释放地址区间结构体占用的内存。

STATUS_T LOS_RegionFree(LosVmSpace *space, LosVmMapRegion *region)
{
⑴   if ((space == NULL) || (region == NULL)) {VM_ERR("args error, aspace %p, region %p", space, region);return LOS_ERRNO_VM_INVALID_ARGS;}(VOID)LOS_MuxAcquire(&space->regionMux);#ifdef LOSCFG_FS_VFS
⑵  if (LOS_IsRegionFileValid(region)) {OsFilePagesRemove(space, region);} else
#endif#ifdef LOSCFG_KERNEL_SHM
⑶   if (OsIsShmRegion(region)) {OsShmRegionFree(space, region);} else if (LOS_IsRegionTypeDev(region)) {
#else
⑷   if (LOS_IsRegionTypeDev(region)) {
#endifOsDevPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);} else {
⑸      OsAnonPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);}/* remove it from space */
⑹   LOS_RbDelNode(&space->regionRbTree, &region->rbNode);/* free it */LOS_MemFree(m_aucSysMem0, region);(VOID)LOS_MuxRelease(&space->regionMux);return LOS_OK;
}

4.4 虚拟内存内部实现函数

4.4.1 函数OsAllocRange

函数OsAllocRange用于从虚拟地址空间中申请指定长度的内存,返回值为申请到的虚拟地址。⑴处从进程空间中获取映射区开始地址对应的地址区间。当获取的地址区间不为NULL时,执行⑵,获取地址区间的红黑树节点,并获取该地址区间的结束地址。⑶处使用红黑树的宏对RB_MID_SCANRB_MID_SCAN_END,循环遍历红黑树节点pstRbNode及其后续节点。⑷处如果当前遍历节点和映射区获取的地址区间有重叠则继续遍历下一个节点。⑸处如果地址区间长度满足要求,则返回虚拟地址,否则执行⑹更新地址区间的结束地址继续遍历。

当从映射区获取的地址区间为NULL时,执行⑺。红黑树的宏对RB_SCAN_SAFERB_SCAN_SAFE_END会从第一个树节点循环遍历。循环体内的内容和上文重复,不再赘述。⑻如果映射区没有申请到合适的虚拟地址,则判断下在映射区后的地址区间是否满足条件。如果依旧申请不到合适的虚拟地址,返回0。

VADDR_T OsAllocRange(LosVmSpace *vmSpace, size_t len)
{LosVmMapRegion *curRegion = NULL;LosRbNode *pstRbNode = NULL;LosRbNode *pstRbNodeTmp = NULL;LosRbTree *regionRbTree = &vmSpace->regionRbTree;VADDR_T curEnd = vmSpace->mapBase;VADDR_T nextStart;⑴  curRegion = LOS_RegionFind(vmSpace, vmSpace->mapBase);if (curRegion != NULL) {
⑵      pstRbNode = &curRegion->rbNode;curEnd = curRegion->range.base + curRegion->range.size;
⑶      RB_MID_SCAN(regionRbTree, pstRbNode)curRegion = (LosVmMapRegion *)pstRbNode;nextStart = curRegion->range.base;
⑷          if (nextStart < curEnd) {continue;}
⑸          if ((nextStart - curEnd) >= len) {return curEnd;} else {
⑹              curEnd = curRegion->range.base + curRegion->range.size;}RB_MID_SCAN_END(regionRbTree, pstRbNode)} else {/* rbtree scan is sorted, from small to big */
⑺      RB_SCAN_SAFE(regionRbTree, pstRbNode, pstRbNodeTmp)curRegion = (LosVmMapRegion *)pstRbNode;nextStart = curRegion->range.base;if (nextStart < curEnd) {continue;}if ((nextStart - curEnd) >= len) {return curEnd;} else {curEnd = curRegion->range.base + curRegion->range.size;}RB_SCAN_SAFE_END(regionRbTree, pstRbNode, pstRbNodeTmp)}⑻  nextStart = vmSpace->mapBase + vmSpace->mapSize;if ((nextStart >= curEnd) && ((nextStart - curEnd) >= len)) {return curEnd;}return 0;
}

4.4.2 函数OsAllocSpecificRange

函数OsAllocSpecificRange用于从虚拟地址空间中申请指定长度的内存,如果指定的虚拟地址已经被映射,则取消映射,返回值为申请到的虚拟地址。⑴处验证虚拟内存块是否在虚拟地址空间范围内。⑵处判断虚拟地址是否已经属于某个地址区间,如果不属于任何地址区间,则执行⑸返回该虚拟地址;如果属于某个地址区间,则继续执行⑶,如果地址区间标签包含VM_MAP_REGION_FLAG_FIXED_NOREPLACE,不允许替换,则返回0;如果标签包含VM_MAP_REGION_FLAG_FIXED,则调用LOS_UnMMap取消映射。如果不包含上述标签,则执行⑷,重新申请地址区间。

VADDR_T OsAllocSpecificRange(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags)
{STATUS_T status;⑴  if (LOS_IsRangeInSpace(vmSpace, vaddr, len) == FALSE) {return 0;}⑵   if ((LOS_RegionFind(vmSpace, vaddr) != NULL) ||(LOS_RegionFind(vmSpace, vaddr + len - 1) != NULL) ||(LOS_RegionRangeFind(vmSpace, vaddr, len - 1) != NULL)) {
⑶      if ((regionFlags & VM_MAP_REGION_FLAG_FIXED_NOREPLACE) != 0) {return 0;} else if ((regionFlags & VM_MAP_REGION_FLAG_FIXED) != 0) {status = LOS_UnMMap(vaddr, len);if (status != LOS_OK) {VM_ERR("unmmap specific range va: %#x, len: %#x failed, status: %d", vaddr, len, status);return 0;}} else {
⑷          return OsAllocRange(vmSpace, len);}}⑸  return vaddr;
}

4.4.3 函数OsCreateRegion

函数OsCreateRegion用于根据虚拟地址、内存大小、地址区间标签等信息创建地址区间。⑴处为地址区间结构体申请内存,⑵处根据参数设置地址区间属性值。代码比较简单,自行阅读即可。

LosVmMapRegion *OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, unsigned long offset)
{
⑴  LosVmMapRegion *region = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmMapRegion));if (region == NULL) {VM_ERR("memory allocate for LosVmMapRegion failed");return region;}⑵  region->range.base = vaddr;region->range.size = len;region->pgOff = offset;region->regionFlags = regionFlags;region->regionType = VM_MAP_REGION_TYPE_NONE;region->forkFlags = 0;region->shmid = -1;return region;
}

4.4.4 函数OsInsertRegion

函数OsInsertRegion用于把红黑树节点插入红黑树。⑴处调用函数LOS_RbAddNode插入红黑树节点,LosVmMapRegion结构体的第一个成员是LosRbNode类型,二者可以强转。⑵处如果插入节点失败,则打印地址空间信息。代码比较简单。

BOOL OsInsertRegion(LosRbTree *regionRbTree, LosVmMapRegion *region)
{
⑴   if (LOS_RbAddNode(regionRbTree, (LosRbNode *)region) == FALSE) {VM_ERR("insert region failed, base: %#x, size: %#x", region->range.base, region->range.size);
⑵       OsDumpAspace(region->space);return FALSE;}return TRUE;
}

4.4.5 函数OsFindRegion

函数OsFindRegion实现根据虚拟内存地址查找地址区间。⑴处设置地址区间范围的开始地址和大小。⑵处调用函数LOS_RbGetNode()从红黑树上获取红黑树节点pstRbNode,获取成功时会继续执行⑶从红黑树节点转换为需要的地址区间。后续会有专门的系列讲解红黑树,届时再分析函数LOS_RbGetNode()

LosVmMapRegion *OsFindRegion(LosRbTree *regionRbTree, VADDR_T vaddr, size_t len)
{LosVmMapRegion *regionRst = NULL;LosRbNode *pstRbNode = NULL;LosVmMapRange rangeKey;
⑴  rangeKey.base = vaddr;rangeKey.size = len;⑵  if (LOS_RbGetNode(regionRbTree, (VOID *)&rangeKey, &pstRbNode)) {
⑶      regionRst = (LosVmMapRegion *)LOS_DL_LIST_ENTRY(pstRbNode, LosVmMapRegion, rbNode);}return regionRst;
}

5、VMalloc常用操作

内核动态分配虚拟地址空间操作分为申请和释放2个操作。

5.1 函数LOS_VMalloc

函数LOS_VMalloc用于从VMalloc动态分配内存堆虚拟地址空间中申请内存,参数为需要申请的字节数。
⑴处把申请的内存大小进行页对齐,并由字节数计算页数sizeCount
⑵处声明一个内存页双向链表。
⑶处申请指定数量的物理内存页并挂载到双向链表pageList上。
⑷处从动态内存分配堆进程空间g_vMallocSpace中申请虚拟内存地址区间。此时成功申请了虚拟内存和物理内存,而且页数也是一样的,下面执行
⑸循环遍历物理页双向链表上的每一个内存页进行虚实映射。
⑹处获取物理内存页的物理内存地址,然后把物理内存页的引用计数自增加1。
⑺处进行虚实映射,然后把虚拟内存地址增加一个内存页的大小,继续循环遍历。
⑻处返回申请到的虚拟地址区间的内存开始地址。虚实映射函数LOS_ArchMmuMapMMU虚实映射系列来详细讲解。

VOID *LOS_VMalloc(size_t size)
{LosVmSpace *space = &g_vMallocSpace;LosVmMapRegion *region = NULL;size_t sizeCount;size_t count;LosVmPage *vmPage = NULL;VADDR_T va;PADDR_T pa;STATUS_T ret;⑴  size = LOS_Align(size, PAGE_SIZE);if ((size == 0) || (size > space->size)) {return NULL;}sizeCount = size >> PAGE_SHIFT;⑵   LOS_DL_LIST_HEAD(pageList);(VOID)LOS_MuxAcquire(&space->regionMux);⑶  count = LOS_PhysPagesAlloc(sizeCount, &pageList);if (count < sizeCount) {VM_ERR("failed to allocate enough pages (ask %zu, got %zu)", sizeCount, count);goto ERROR;}/* allocate a region and put it in the aspace list */
⑷   region = LOS_RegionAlloc(space, 0, size, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE, 0);if (region == NULL) {VM_ERR("alloc region failed, size = %x", size);goto ERROR;}va = region->range.base;
⑸  while ((vmPage = LOS_ListRemoveHeadType(&pageList, LosVmPage, node))) {
⑹      pa = vmPage->physAddr;LOS_AtomicInc(&vmPage->refCounts);
⑺      ret = LOS_ArchMmuMap(&space->archMmu, va, pa, 1, region->regionFlags);if (ret != 1) {VM_ERR("LOS_ArchMmuMap failed!, err;%d", ret);}va += PAGE_SIZE;}(VOID)LOS_MuxRelease(&space->regionMux);
⑻   return (VOID *)(UINTPTR)region->range.base;ERROR:(VOID)LOS_PhysPagesFree(&pageList);(VOID)LOS_MuxRelease(&space->regionMux);return NULL;
}

5.2 函数LOS_VFree

函数LOS_VFree用于释放从VMalloc动态内存堆虚拟地址空间中申请的虚拟内存,传入参数为虚拟地址。 ⑴处根据虚拟地址获取虚拟地址区间,然后执行⑵释放地址区间,其中函数LOS_RegionFree在前文已经详细讲述。

VOID LOS_VFree(const VOID *addr)
{LosVmSpace *space = &g_vMallocSpace;LosVmMapRegion *region = NULL;STATUS_T ret;if (addr == NULL) {VM_ERR("addr is NULL!");return;}(VOID)LOS_MuxAcquire(&space->regionMux);⑴  region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)addr);if (region == NULL) {VM_ERR("find region failed");goto DONE;}⑵   ret = LOS_RegionFree(space, region);if (ret) {VM_ERR("free region failed, ret = %d", ret);}DONE:(VOID)LOS_MuxRelease(&space->regionMux);
}

6 其他

6.1 函数LOS_VmSpaceReserve

函数LOS_VmSpaceReserve用于在在进程空间中预留一块内存空间。⑴处先做参数校验。⑵处先判断虚拟地址和大小在指定的虚拟地址空间内。⑶处查询指定的虚拟地址的映射标签。⑷处加上标签VM_MAP_REGION_FLAG_FIXED申请一段地址区间。

STATUS_T LOS_VmSpaceReserve(LosVmSpace *space, size_t size, VADDR_T vaddr)
{UINT32 regionFlags = 0;⑴  if ((space == NULL) || (size == 0) || (!IS_PAGE_ALIGNED(vaddr) || !IS_PAGE_ALIGNED(size))) {return LOS_ERRNO_VM_INVALID_ARGS;}⑵  if (!LOS_IsRangeInSpace(space, vaddr, size)) {return LOS_ERRNO_VM_OUT_OF_RANGE;}/* lookup how it's already mapped */
⑶  (VOID)LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, &regionFlags);/* build a new region structure */
⑷  LosVmMapRegion *region = LOS_RegionAlloc(space, vaddr, size, regionFlags | VM_MAP_REGION_FLAG_FIXED, 0);return region ? LOS_OK : LOS_ERRNO_VM_NO_MEMORY;
}

总结

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

如果大家想更加深入的学习 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.mzph.cn/bicheng/26879.shtml

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

相关文章

WebSocket 详解--spring boot简单使用案例

一、什么是WebSocket WebSocket 是一种网络通信协议&#xff0c;专为在单个 TCP 连接上进行全双工通信而设计。WebSocket 允许客户端和服务器之间的消息能够实时双向传输。这与传统的 HTTP 请求-响应模式有很大的不同。 二、WebSocket 的关键特性 双向通信&#xff1a;WebSocke…

【StableDiffusion】Prompts 提示词语法;高阶用法;写作顺序是什么,先写什么后写什么

Prompt 写作顺序 第一步&#xff1a;画质词画风词 第一步先写“画质词”和“画风词” 画质词如下&#xff1a; 画风词如下&#xff1a; 第二步&#xff1a;画面主体描述 人物性别、年龄、发型、发色、情绪表情、衣服款式、衣服颜色、动作、饰品、身材、五官微调 第三步&…

多表联查小情景例子

这里有个需求&#xff1a; 需要给定套餐表setmeal 的 id 查询这个套餐内的所有菜品 dish 实际上如果可能&#xff0c;只需要查询套餐菜品表(setmeal_dish)&#xff0c;查看一个套餐内包含包含的所有菜品&#xff0c;然后把这些菜品全部取出即可。 看一下setmeal_dish 表&…

ZnO电阻片在低电场区域的泄漏电流及其电阻的负温度系数

在低电场区域,流过ZnO非线性电阻的泄漏电流小于1mA.泄漏电流不仅与施加的电压幅值有关,而且与温度高低有关。图2.6表示温度对泄漏电流的影响,温度越高,电子在电场作用下定向的运动就越激烈,导致泄漏电流增大。因此温度升高将导致电阳值下降,即ZnO 电阻呈现负温度特性。 一般以…

centos下创建raid6磁盘阵列

在CentOS系统中创建RAID 6阵列&#xff0c;可以使用mdadm工具。 以下是创建RAID 6阵列的基本步骤和示例代码&#xff1a; 安装mdadm工具&#xff08;如果尚未安装&#xff09;&#xff1a; sudo yum install mdadm 假设你有至少四个以上的磁盘设备&#xff08;例如 /dev/sdi…

STM32存储左右互搏 模拟U盘桥接SPI总线FATS读写FLASH W25QXX

STM32存储左右互搏 模拟U盘桥接SPI总线FATS读写FLASH W25QXX STM32的USB接口可以模拟成为U盘&#xff0c;通过FATS文件系统对连接的存储单元进行U盘方式的读写。 这里介绍STM32CUBEIDE开发平台HAL库模拟U盘桥接SPI总线FATS读写W25Q各型号FLASH的例程。 FLASH是常用的一种非易失…

maven archetype项目构架

1、设置环境变量 set MAVEN_HOMED:\SF\java\apache-maven-3.6.3 set path%path%;%MAVEN_HOME%\bin;2、制作archetype mvn -s "D:\SF\java\apache-maven-3.6.3\conf\settings.xml" archetype:create-from-project -DpackageNamecom.demo.esb-s:指定maven的setting文…

LeetCode | 28.找出字符串中第一个匹配项的下标 KMP

这是字符串匹配问题&#xff0c;朴素做法是两重遍历&#xff0c;依次从主串的i位置开始查看是否和模式串匹配&#xff0c;若不匹配就换下一个位置进行判断&#xff0c;直到找到或者遍历完&#xff0c;时间复杂度 O ( m n ) O(m \times n) O(mn) 还可以对主串进行处理&#xff…

vue.js+node.js+mysql在线聊天室源码

vue.jsnode.jsmysql在线聊天室源码 技术栈&#xff1a;vue.jsElement UInode.jssocket.iomysql vue.jsnode.jsmysql在线聊天室源码

单例模式、工厂模式 c++关键字 static

static 关键字的作用&#xff1a; 主要作用在于 控制变量或函数的作用域、生命周期以及它们如何被不同部分的程序访问&#xff0c;从而帮助程序员管理内存、避免命名冲突&#xff0c;并实现特定的设计模式&#xff08;如单例模式&#xff09;。 1. 静态局部变量&#xff1a;当…

区间预测 | Matlab实现EVO-CNN-SVM能量谷算法优化卷积神经网络支持向量机结合核密度估计多置信区间多变量回归区间预测

区间预测 | Matlab实现EVO-CNN-SVM能量谷算法优化卷积神经网络支持向量机结合核密度估计多置信区间多变量回归区间预测 目录 区间预测 | Matlab实现EVO-CNN-SVM能量谷算法优化卷积神经网络支持向量机结合核密度估计多置信区间多变量回归区间预测效果一览基本介绍程序设计参考资…

5.3 Python 闭包函数,递归函数,匿名函数

1. 函数对象 Python中所有的数据都是对象, 函数的值也是一个对象, 通过函数名获取函数对象. 使用函数名也就是使用函数对象, 函数对象加上括号立刻执行函数体代码块. 函数名的其它作用: * 1. 函数名可以作为其它变量的值. * 2. 函数名可以作为其它函数的参数. * 3. 函数名可以…

搜索与图论:染色法判别二分图

搜索与图论&#xff1a;染色法判别二分图 题目描述参考代码 题目描述 输入样例 4 4 1 3 1 4 2 3 2 4输出样例 Yes参考代码 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010, M 200010;int n, m; i…

多款可观测产品全面升级丨阿里云云原生 5 月产品月报

云原生月度动态 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》&#xff0c;从趋势热点、产品新功能、服务客户、开源与开发者动态等方面&#xff0c;为企业提供数字化的路径与指南。 趋势热点 &#x1f947; 阿里云云原生产品负责人李国强&#xff1a;推进可…

《Brave New Words 》9.1 AI 世界中的就业

Part IX: Work and What Comes Next 第九部分&#xff1a;工作及其未来发展 The one who plants trees, knowing that he will never sit in their shade, has at least started to understand the meaning of life. —Rabindranath Tagore 种树的人&#xff0c;虽然知道他永远…

如何舒适的使用VScode

安装好VScode后通常会很不好用&#xff0c;以下配置可以让你的VScode变得好用许多。 VScode的配置流程 1、设置VScode中文2、下载C/C拓展&#xff0c;使代码可以跳转3、更改编码格式4、设置滚轮缩放5、设置字体6、设置保存自动改变格式7、vscode设置快捷代码8、下载插件并学会…

Java多线程面试重点-1

0. 什么是并发&#xff1f;什么是并行&#xff1f; 并发&#xff1a;把时间分成一段一段&#xff0c;每个线程轮流抢占时间段。 如果时间段非常短&#xff0c;线程切换非常快&#xff0c;被称为伪并行。并行&#xff1a;多个线程可以同时运行。 并发与并行造成的影响&#xff…

ARM32开发--IIC软实现

知不足而奋进 望远山而前行 目录 文章目录 前言 开发流程 GD32F4软件I2C初始化 GD32F4软件I2C引脚功能 写操作 读操作 总结 前言 在嵌入式系统开发中&#xff0c;软件实现的I2C通信协议扮演着至关重要的角色。本文将深入探讨如何在GD32F4系列微控制器上实现软件I2C功能…

深入浅出 Go 语言的 GPM 模型(Go1.21)

引言 在现代软件开发中&#xff0c;有效地利用并发是提高应用性能和响应速度的关键。随着多核处理器的普及&#xff0c;编程语言和框架如何高效、简便地支持并发编程&#xff0c;成为了软件工程师们评估和选择工具时的一个重要考量。在这方面&#xff0c;Go 语言凭借其创新的并…