鸿蒙原生开发——轻内核A核源码分析系列三 物理内存(2)

3.1.2.3 函数OsVmPhysLargeAlloc

当执行到这个函数时,说明空闲链表上的单个内存页节点的大小已经不能满足要求,超过了第9个链表上的内存页节点的大小了。⑴处计算需要申请的内存大小。⑵从最大的链表上进行遍历每一个内存页节点。⑶根据每个内存页的开始内存地址,计算需要的内存的结束地址,如果超过内存段的大小,则继续遍历下一个内存页节点。

⑷处此时paStart表示当前内存页的结束地址,接下来paStart >= paEnd表示当前内存页的大小满足申请的需求;paStart < seg->startpaStart >= (seg->start + seg->size)发生溢出错误,内存页结束地址不在内存段的地址范围内。⑸处表示当前内存页的下一个内存页结构体,如果该结构体不在空闲链表上,则break跳出循环。如果在空闲链表上,表示连续的空闲内存页会拼接起来,满足大内存申请的需要。⑹表示一个或者多个连续的内存页的大小满足申请需求。

STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
{
    struct VmFreeList *list = NULL;
    LosVmPage *page = NULL;
    LosVmPage *tmp = NULL;
    PADDR_T paStart;
    PADDR_T paEnd;
⑴  size_t size = nPages << PAGE_SHIFT;

⑵  list = &seg->freeList[VM_LIST_ORDER_MAX - 1];
    LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {
⑶      paStart = page->physAddr;
        paEnd = paStart + size;
        if (paEnd > (seg->start + seg->size)) {
            continue;
        }

        for (;;) {
⑷          paStart += PAGE_SIZE << (VM_LIST_ORDER_MAX - 1);
            if ((paStart >= paEnd) || (paStart < seg->start) ||
                (paStart >= (seg->start + seg->size))) {
                break;
            }
⑸          tmp = &seg->pageBase[(paStart - seg->start) >> PAGE_SHIFT];
            if (tmp->order != (VM_LIST_ORDER_MAX - 1)) {
                break;
            }
        }
⑹      if (paStart >= paEnd) {
            return page;
        }
    }

    return NULL;
}

3.1.2.4 函数OsVmPhysFreeListDelUnsafeOsVmPhysFreeListAddUnsafe

内部函数OsVmPhysFreeListDelUnsafe用于从空闲内存页节点链表上删除一个内存页节点,名称中有Unsafe字样,是因为函数体内并没有对链表操作加自旋锁,安全性由外部调用函数保证。⑴处进行校验,确保内存段和空闲链表索引符合要求。⑵处获取内存段和空闲链表,⑶处空闲链表上内存页节点数目减1,并把内存块从空闲链表上删除。⑷处设置内存页的order索引值为最大值来标记非空闲内存页。

STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
{
    struct VmPhysSeg *seg = NULL;
    struct VmFreeList *list = NULL;

⑴  if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
        LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
    }

⑵  seg = &g_vmPhysSeg[page->segID];
    list = &seg->freeList[page->order];
⑶  list->listCnt--;
    LOS_ListDelete(&page->node);
⑷  page->order = VM_LIST_ORDER_MAX;
}

和空闲链表上删除对应的函数是空闲链表上插入空闲内存页节点函数OsVmPhysFreeListAddUnsafe。⑴处更新内存页的要挂载的空闲链表的索引值,然后获取内存页所在的内存段seg,并获取索引值对应的空闲链表。执行⑵把空闲内存页节点插入到空闲链表并更新节点数目。

STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
{
    struct VmPhysSeg *seg = NULL;
    struct VmFreeList *list = NULL;

    if (page->segID >= VM_PHYS_SEG_MAX) {
        LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
    }

⑴  page->order = order;
    seg = &g_vmPhysSeg[page->segID];

    list = &seg->freeList[order];
⑵   LOS_ListTailInsert(&list->node, &page->node);
    list->listCnt++;
}

3.1.2.5 函数OsVmPhysPagesSpiltUnsafe

函数OsVmPhysPagesSpiltUnsafe用于分割内存块,参数中oldOrder表示需要申请的内存页节点对应的链表索引,newOrder表示实际申请的内存页节点对应的链表索引。如果索引值相等,则不需要拆分,不会执行for循环块的代码。由于伙伴算法中的链表数组中元素的特点,即每个链表中的内存页节点的大小等于2的幂次方个内存页。在拆分时,依次从高索引newOrder往低索引oldOrder遍历,拆分一个内存页节点作为空闲内存页节点挂载到对应的空闲链表上。⑴处开始循环从高索引到低索引,索引值减1,然后执行⑵获取伙伴内存页节点,可以看出,申请的内存块大于需求时,会把后半部分的高地址部分放入空闲链表,保留前半部分的低地址部分。⑶处的断言确保伙伴内存页节点索引值是最大值,表示属于空闲内存页节点。⑷处调用函数把内存页节点放入空闲链表。

STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
{
    UINT32 order;
    LosVmPage *buddyPage = NULL;

    for (order = newOrder; order > oldOrder;) {
⑴      order--;
⑵      buddyPage = &page[VM_ORDER_TO_PAGES(order)];
⑶      LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);
⑷      OsVmPhysFreeListAddUnsafe(buddyPage, order);
    }
}

这里有必要放这一张图,直观演示一下。假如我们需要申请8个内存页大小的内存节点,但是只有freeList[7]链表上才有空闲节点。申请成功后,超过了应用需要的大小,需要进行拆分。把27个内存页分为2份大小为26个内存页的节点,第一份继续拆分,第二份挂载到freeList[6]链表上。然后把第一份26个内存页拆分为2个25个内存页节点,第一份继续拆分,第二份挂载到freeList[5]链表上。依次进行下去,最后拆分为2份2^3个内存页大小的内存页节点,第一份作为实际申请的内存页返回,第二份挂载到freeList[3]链表上。如下图红色部分所示。

另外,函数OsVmRecycleExtraPages会调用OsVmPhysPagesFreeContiguous来回收申请的多余的内存页,后文再分析。

3.2 释放物理内存页接口

3.2.1 释放物理内存页接口介绍

和申请物理内存页接口相对应着,释放物理内存页的接口有3个,分别用于满足不同的释放内存页需求。函数LOS_PhysPagesFreeContiguous的传入参数为要释放物理页对应的内核虚拟地址空间中的虚拟内存地址和内存页数目。⑴处调用函数OsVmVaddrToPage把虚拟内存地址转换为物理内存页结构体地址,然后⑵处把内存页的连续内存页数目设置为0。⑶处调用函数OsVmPhysPagesFreeContiguous()释放物理内存页。函数LOS_PhysPageFree用于释放一个物理内存页,传入参数为要释放的物理页对应的物理页结构体地址。⑷处对引用计数自减,当小于等于0,表示没有其他引用时才进一步执行释放操作。该函数同样会调用函数OsVmPhysPagesFreeContiguous()释放物理内存页。函数LOS_PhysPagesFree用于释放挂在双向链表上的多个物理内存页,返回值为实际释放的物理页数目。⑸处遍历内存页双向链表,从链表上移除要释放的内存页节点。⑹处代码和释放一个内存页的函数代码相同。⑺处计算遍历的内存页的数目,函数最后会返回该值。

VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
{
    UINT32 intSave;
    struct VmPhysSeg *seg = NULL;
    LosVmPage *page = NULL;

    if (ptr == NULL) {
        return;
    }

⑴   page = OsVmVaddrToPage(ptr);
    if (page == NULL) {
        VM_ERR("vm page of ptr(%#x) is null", ptr);
        return;
    }
⑵  page->nPages = 0;

    seg = &g_vmPhysSeg[page->segID];
    LOS_SpinLockSave(&seg->freeListLock, &intSave);

⑶   OsVmPhysPagesFreeContiguous(page, nPages);

    LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}

......

VOID LOS_PhysPageFree(LosVmPage *page)
{
    UINT32 intSave;
    struct VmPhysSeg *seg = NULL;

    if (page == NULL) {
        return;
    }

⑷  if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
        seg = &g_vmPhysSeg[page->segID];
        LOS_SpinLockSave(&seg->freeListLock, &intSave);

        OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
        LOS_AtomicSet(&page->refCounts, 0);

        LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
    }
}
······
size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
{
    UINT32 intSave;
    LosVmPage *page = NULL;
    LosVmPage *nPage = NULL;
    LosVmPhysSeg *seg = NULL;
    size_t count = 0;

    if (list == NULL) {
        return 0;
    }

    LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {
⑸      LOS_ListDelete(&page->node);
⑹      if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
            seg = &g_vmPhysSeg[page->segID];
            LOS_SpinLockSave(&seg->freeListLock, &intSave);
            OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
            LOS_AtomicSet(&page->refCounts, 0);
            LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
        }
⑺      count++;
    }

    return count;
}

3.2.2 释放物理内存页内部接口实现

3.2.2.1 函数OsVmVaddrToPage

函数OsVmVaddrToPage把虚拟内存地址转换为物理页结构体地址。⑴处调用函数LOS_PaddrQuery()把虚拟地址转为物理地址,该函数在虚实映射部分会详细讲述。⑵处遍历物理内存段,如果物理内存地址处于物理内存段的地址范围,则可以返回该物理地址对应的物理页结构体地址。

LosVmPage *OsVmVaddrToPage(VOID *ptr)
{
    struct VmPhysSeg *seg = NULL;
⑴  PADDR_T pa = LOS_PaddrQuery(ptr);
    UINT32 segID;

    for (segID = 0; segID < g_vmPhysSegNum; segID++) {
        seg = &g_vmPhysSeg[segID];
⑵      if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {
            return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);
        }
    }

    return NULL;
}
3.2.2.2 函数OsVmPhysPagesFreeContiguous

函数OsVmPhysPagesFreeContiguous()用于释放指定数量的连续物理内存页。⑴处根据物理内存页获取对应的物理内存地址。⑵处根据物理内存地址获取空闲内存页链表数组索引数值。⑶处获取索引值对应的链表上的内存页节点的内存页数目。⑷处如果要释放的内存页数nPages小于当前链表上的内存页节点的数目,则跳出循环执行⑹处代码,去释放到小索引的双向链表上。⑸处调用函数OsVmPhysPagesFree()释放指定链表上的内存页,然后更新内存页数量和内存页结构体地址。

⑹处根据内存页数量计算对应的链表索引,根据索引值计算链表上内存页节点的大小。⑺处调用函数OsVmPhysPagesFree()释放指定链表上的内存页,然后更新内存页数量和内存页结构体地址。

VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
{
    paddr_t pa;
    UINT32 order;
    size_t n;

    while (TRUE) {
⑴      pa = VM_PAGE_TO_PHYS(page);
⑵      order = VM_PHYS_TO_ORDER(pa);
⑶      n = VM_ORDER_TO_PAGES(order);
⑷      if (n > nPages) {
            break;
        }
⑸      OsVmPhysPagesFree(page, order);
        nPages -= n;
        page += n;
    }

    while (nPages > 0) {
⑹      order = LOS_HighBitGet(nPages);
        n = VM_ORDER_TO_PAGES(order);
⑺      OsVmPhysPagesFree(page, order);
        nPages -= n;
        page += n;
    }
}
3.2.2.3 函数OsVmPhysPagesFree

函数OsVmPhysPagesFree()释放内存页到对应的空闲内存页链表。内存页块释放时,会在当前链表找地址连续的伙伴内存页块进行合并,然后去上一级链表上继续查找是否存在连续的伙伴内存页块。⑴做传入参数校验。⑵处需要至少是倒数第二个链表,这样内存页节点可以和大索引链表上的节点合并。⑶处获取内存页对应的物理内存地址,然后后面会开始do-while循环,查找是否存在连续的内存页节点。⑷处的VM_ORDER_TO_PHYS(order)计算出链表索引值对应的伙伴位图,然后进行异或运算计算出伙伴内存页的物理内存地址。⑸处物理地址转换为内存页结构体,进一步判断:如果内存页不存在或者不在空闲链表上,则跳出循环while循环。否则如果伙伴内存节点存在,则执行⑹把伙伴页从链表上移除,然后索引值加1。⑺处链表索引加1,然后进行逻辑与计算得到物理内存地址。此时物理内存地址,和合并的两块内存页块地址连续。该内存地址在高一级的空闲链表上不一定存在,存在则继续合并,不存在则退出循环。当索引order为8,要插入到最后一个链表上时,或者没有再找到可以合并的节点时,则直接执行⑻插入内存页节点到空闲链表上。

VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
{
    paddr_t pa;
    LosVmPage *buddyPage = NULL;

⑴  if ((page == NULL) || (order >= VM_LIST_ORDER_MAX)) {
        return;
    }

⑵  if (order < VM_LIST_ORDER_MAX - 1) {
⑶        pa = VM_PAGE_TO_PHYS(page);        
        do {
⑷          pa ^= VM_ORDER_TO_PHYS(order);
⑸          buddyPage = OsVmPhysToPage(pa, page->segID);
            if ((buddyPage == NULL) || (buddyPage->order != order)) {
                break;
            }
⑹          OsVmPhysFreeListDel(buddyPage);
            order++;
⑺          pa &= ~(VM_ORDER_TO_PHYS(order) - 1);
            page = OsVmPhysToPage(pa, page->segID);
        } while (order < VM_LIST_ORDER_MAX - 1);
    }

⑻  OsVmPhysFreeListAdd(page, order);
}

3.3 查询物理页地址接口

3.3.1 函数LOS_VmPageGet()

函数LOS_VmPageGet用于根据物理内存地址参数计算对应的物理内存页结构体地址。⑴处遍历物理内存段,调用函数OsVmPhysToPage根据物理内存地址和内存段编号计算物理内存页结构体,该函数后文再分析。⑵处如果获取的物理内存页结构体不为空,则跳出循环,返回物理内存页结构体指针。

LosVmPage *LOS_VmPageGet(PADDR_T paddr)
{
    INT32 segID;
    LosVmPage *page = NULL;

    for (segID = 0; segID < g_vmPhysSegNum; segID++) {
⑴      page = OsVmPhysToPage(paddr, segID);
⑵      if (page != NULL) {
            break;
        }
    }

    return page;
}

继续看下函数OsVmPhysToPage的代码。⑴处如果参数传入的物理内存地址不在指定的物理内存段的地址范围之内则返回NULL。⑵处计算物理内存地址相对内存段开始地址的偏移值。⑶处根据偏移值计算出偏移的内存页的数目,然后返回物理内存地址对应的物理页结构体的地址。

LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
{
    struct VmPhysSeg *seg = NULL;
    paddr_t offset;

    if (segID >= VM_PHYS_SEG_MAX) {
        LOS_Panic("The page segment id(%d) is invalid\n", segID);
    }
    seg = &g_vmPhysSeg[segID];
⑴  if ((pa < seg->start) || (pa >= (seg->start + seg->size))) {
        return NULL;
    }

⑵  offset = pa - seg->start;
⑶  return (seg->pageBase + (offset >> PAGE_SHIFT));
}

3.3.2 函数LOS_PaddrToKVaddr

函数LOS_PaddrToKVaddr根据物理地址获取其对应的内核虚拟地址。⑴处遍历物理内存段数组,然后在⑵处判断如果物理地址处于遍历到的物理内存段的地址范围内,则执行⑶,传入的物理内存地址相对物理内存开始地址的偏移加上内核态虚拟地址空间的开始地址就是物理地址对应的内核虚拟地址。

VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
{
    struct VmPhysSeg *seg = NULL;
    UINT32 segID;

    for (segID = 0; segID < g_vmPhysSegNum; segID++) {
 ⑴     seg = &g_vmPhysSeg[segID];
 ⑵     if ((paddr >= seg->start) && (paddr < (seg->start + seg->size))) {
 ⑶          return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
        }
    }

    return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
}

3.4 其他函数

3.4.1 函数OsPhysSharePageCopy

函数OsPhysSharePageCopy用于复制共享内存页。 ⑴处进行参数校验, ⑵处获取老内存页, ⑶处获取内存段。⑷处如果老内存页引用计数为1,则把老物理内存地址直接赋值给新物理内存地址。⑸处如果内存页有多个引用,则先转化为虚拟内存地址,然后执行⑹进行内存页的内容复制。⑺刷新新老内存页的引用计数。

VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
{
    UINT32 intSave;
    LosVmPage *oldPage = NULL;
    VOID *newMem = NULL;
    VOID *oldMem = NULL;
    LosVmPhysSeg *seg = NULL;

 ⑴  if ((newPage == NULL) || (newPaddr == NULL)) {
        VM_ERR("new Page invalid");
        return;
    }

 ⑵  oldPage = LOS_VmPageGet(oldPaddr);
    if (oldPage == NULL) {
        VM_ERR("invalid oldPaddr %p", oldPaddr);
        return;
    }

 ⑶  seg = &g_vmPhysSeg[oldPage->segID];
    LOS_SpinLockSave(&seg->freeListLock, &intSave);
⑷  if (LOS_AtomicRead(&oldPage->refCounts) == 1) {
        *newPaddr = oldPaddr;
    } else {
⑸      newMem = LOS_PaddrToKVaddr(*newPaddr);
        oldMem = LOS_PaddrToKVaddr(oldPaddr);
        if ((newMem == NULL) || (oldMem == NULL)) {
            LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
            return;
        }
⑹      if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {
            VM_ERR("memcpy_s failed");
        }

⑺      LOS_AtomicInc(&newPage->refCounts);
        LOS_AtomicDec(&oldPage->refCounts);
    }
    LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
    return;
}

总结

本文首先了解了物理内存管理的结构体,接着阅读了物理内存如何初始化,然后分析了物理内存的申请、释放和查询等操作接口的源代码。

如果大家想更加深入的学习 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/699531.html

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

相关文章

02-DHCP原理与配置

1、DHCP的工作原理 当局域网中有大量的主机时&#xff0c;如果逐个为每一台主机手动设置IP地址、默认网关、DNS服务器地址等网络参数&#xff0c;这显然是一个费力也未必讨好的办法。 而DHCP服务器的应用&#xff0c;正好可以解决这一问题。 1.1 DHCP是什么 DHCP——动态主机…

[2024-06]-[大模型]-[Ollama] 0-相关命令

常用的ollama命令[持续更新中] ollama更新&#xff1a; curl https://ollama.ai/install.sh |sh带着flash attention启动&#xff1a; OLLAMA_FLASH_ATTENTION1 ollama serve停止ollama服务&#xff1a; sudo systemctl stop ollama note&#xff1a;目前遇到sudo systemctl …

驱动开发之 input 子系统

1.input 子系统介绍 input 就是输入的意思&#xff0c;input 子系统就是管理输入的子系统&#xff0c;和 pinctrl、gpio 子系统 一样&#xff0c;都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等 等这些都属于输入设备&#xff0c;不同的输入…

一文教你如何实现并发请求的失败自动重试及重试次数限制

需求 在并发接口请求的时候&#xff0c;能够自动对失败的请求进行重发尝试&#xff08;超过指定重试次数则不再重试&#xff09;,并将最终的结果返回&#xff08;包含每个请求是否成功、返回结果&#xff09; 核心思路 代码实现 使用案例 为了演示我们代码的最终实现效果&a…

使用 python 将 Markdown 文件转换为 ppt演示文稿

在这篇博客中&#xff0c;我们将展示如何使用 wxPython 创建一个简单的图形用户界面 (GUI)&#xff0c;以将 Markdown 文件转换为 PowerPoint 演示文稿。我们将利用 markdown2 模块将 Markdown 转换为 HTML&#xff0c;并使用 python-pptx 模块将 HTML 内容转换为 PowerPoint 幻…

HarmonyOS未来五年的市场展望

一、引言 随着科技的不断进步和消费者对于智能化设备需求的日益增长&#xff0c;操作系统作为连接硬件与软件的核心平台&#xff0c;其重要性愈发凸显。HarmonyOS&#xff08;鸿蒙系统&#xff09;&#xff0c;作为华为自主研发的分布式操作系统&#xff0c;自诞生以来便备受瞩…

6月11号作业

思维导图 #include <iostream> using namespace std; class Animal { private:string name; public:Animal(){}Animal(string name):name(name){//cout << "Animal&#xff1b;有参" << endl;}virtual void perform(){cout << "讲解员的…

UE4_后期_ben_模糊和锐化滤镜

学习笔记&#xff0c;不喜勿喷&#xff0c;侵权立删&#xff0c;祝愿生活越来越好&#xff01; 本篇教程主要介绍后期处理的简单模糊和锐化滤镜效果&#xff0c;学习之前首先要回顾下上节课介绍的屏幕扭曲效果&#xff1a; 这是全屏效果&#xff0c;然后又介绍了几种蒙版&#…

【PX4-AutoPilot教程-TIPS】PX4加速度计陀螺仪滤波器参数设置

PX4加速度计陀螺仪滤波器参数设置 前期准备滤波前FFT图滤波后FFT图 环境&#xff1a; 日志分析软件 : Flight Review PX4 &#xff1a;1.13.0 前期准备 进行滤波器参数设置的前提是飞机简单调试过PID已经可以稳定起飞&#xff0c;开源飞控的很多默认参数是可以让飞机平稳起…

springSecurity学习笔记(一)

简介 Spring Security是一个Java框架&#xff0c;用于保护应用程序的安全性。它提供了一套全面的安全解决方案&#xff0c;包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念&#xff0c;可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验…

记一次华为2288H V5更换主板的辛酸

1、开机提示找不到设备&#xff0c;通过带外检查硬盘raid是否正常&#xff0c;如果正常就不是硬件问题&#xff0c;也不会是线没接好 2、网络不通&#xff0c;服务重启啥的都正常不会报错&#xff0c;就是ping不通网关&#xff0c;后来通过带外发现是网卡漂移了。 核对mac地址发…

Docker 国内镜像源更换

实现 替换docker 镜像源 前提要求 安装 docker docker-compose 参考创建一键更换docker国内镜像源 Docker 镜像代理DaoCloud 镜像站百度云 https://mirror.baidubce.com南京大学镜像站

若依RuoYi-Vue分离版—增加通知公告预览及缩放功能

若依RuoYi-Vue分离版—增加通知公告预览及缩放功能 前言开发通知公告 前言 若依分离版的通知公告没有预览功能&#xff0c;想开发通知公告功能 开发通知公告 效果如下 具体开发内容 修改若依notice代码如下。 <template><div class"app-container"&g…

16. 《C语言》——【牛客网BC124 —— BC130题目讲解】

亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&#xff0c;成为一名优…

orbslam2代码解读(4):loopclosing回环检测线程

书接上回&#xff0c;介绍完了局部建图线程&#xff0c;局部建图线程在进行局部BA之后&#xff0c;也会将新的关键帧mpLoopCloser放进回环线程的mlpLoopKeyFrameQueue容器中。所以这时候回环检测线程就根据这个新的关键帧来进行回环检测的操作。 回环检测的主要程序 // 线程主…

websocket php workerman 服务器nginx配置wss协议

首先 Nginx的版本要高&#xff0c;尽量用当前最新稳定版本。 其次 WSS协议&#xff0c;是在HTTPS协议的基础上&#xff0c;进行协议升级&#xff0c;进行通讯的&#xff0c;所以先要保证你有一个 HTTPS正常的WEB站点。 所以&#xff0c;通过Nginx -V 请保证 一定有 --with-ht…

Spring-Security(二)OAuth2认证详解(持续更新)

Spring Security & Oauth2系列&#xff1a; Spring Security&#xff08;一&#xff09; 源码分析及认证流程 Spring Security&#xff08;二&#xff09;OAuth2认证详解及自定义异常处理 文章目录 1、OAuth2.0 简介1.1 OAuth2.0 相关名词解释1.2 四种授权模式 1.3 、OAu…

2023 hnust 湖科大 嵌入式 实验报告+代码及复习资料等

2023 hnust 湖科大 嵌入式 实验报告代码及复习资料等 目录 流水灯 1 8位数码管动态扫描 3 按键输入 5 温度与关照 7 看门狗 9 内容 报告 代码 下载链接 https://pan.baidu.com/s/1LIN8rm42yrukXliI3XyZ1g?pwd1111

Java高阶数据结构-----并查集(详解)

目录 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; &#x1f92a;二.并查集代码&#xff1a; &#x1f602;三&#xff1a;并查集的一些习题&#xff1a; A.省份数量 B.等式方程的可满足性 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; 并查集概念&…

vue操作蓝牙教程

项目背景 想在VUE中使用蓝牙功能&#xff0c;百度了好久也尝试了好多都没法实现。 概念讲价 如果要在浏览器中使用蓝牙&#xff0c;去搜索关键字【navigator.bluetooth】&#xff0c;搜索后发现这根本不是想要的结果。 解决方法 去搜索关键字【uniappbluetoothvue】&#x…