一种Linux系统下内存管理方法

阅读: 评论:0

著录项
  • CN201610197192.9
  • 20160331
  • CN105893269A
  • 20160824
  • 武汉虹信技术服务有限责任公司
  • 雷康;陈亮;王凤纯;胡志勇;肖伟明;余道敏
  • G06F12/02
  • G06F12/02 G06F12/10

  • 湖北省武汉市武汉东湖新技术开发区东信路5号——烽火光通讯产业大楼4楼
  • 湖北(42)
  • 武汉科皓知识产权代理事务所(特殊普通合伙)
  • 严彦
摘要
本发明提供一种Linux系统下内存管理方法,在Linux环境下使用大页内存,在大页内存基础上执行内存配置工作过程,内存申请过程和内存释放过程;内存配置工作过程,包括通过计算虚拟地址和物理地址的关系,得到映射的hugepage属于哪个numa节点,并按照物理地址进行排序;内存申请过程包括内存池配置申请和普通内存申请。本发明保持了内存静态分配的高效率的优点,采用2MB的hugepage页取代4kB节省了页的查询时间,并减少TLB?miss的可能,而且hugepage内存不会被交换到磁盘上,保证了内存永远为申请的应用程序使用,在内存申请时优先使用本地内存提高了内存访问速度。
权利要求

1.一种Linux系统下内存管理方法,其特征在于:在Linux环境下使用大页内存,在大页内存 基础上执行内存配置工作过程,内存申请过程和内存释放过程,

内存配置工作过程,包括通过计算虚拟地址和物理地址的关系,得到映射的hugepage属 于哪个numa节点,并按照物理地址进行排序,

内存申请过程包括内存池配置申请和普通内存申请,通过以下步骤实现,

步骤a1,根据内存池的配置,计算每个内存池需要的内存大小以及每个内存池中保存内存块 信息的大小;

步骤a2,在numa节点中到合适的内存分配给各个内存池以及内存池信息记录,然后将内 存池初始化,并将相关信息保存到内存池信息记录中;

步骤a3,在应用程序申请内存时,首先判断申请内存是否指定了numa节点,如果指定了numa 节点,则在指定numa节点上分配,进行步骤a4;未指定numa节点,则在应用程序所属的 numa节点上分配,然后进入步骤a4;

步骤a4,判断申请大小是否为内存池范围内,如果在范围内,进行步骤a5;如果不在范围内, 进行步骤a6;

步骤a5,判断内存池是否存在空闲的内存,如果存在,则分配一块内存给应用程序,修改内 存池信息,将该内存块从空闲链中摘去;如果不存在,则从其它numa节点的内存池中申请 一块内存,结束本流程;

步骤a6,将hugepage中除去内存池之外的内存都视为free heap,所有申请超过内存池大小的 都从free heap中申请,并将内存地址、申请大小保存到内存信息中;

内存释放过程,包括以下步骤,

步骤b1,释放内存时,首先判断要释放的内存是否属于内存池,如果属于内存池,进行步骤 b2;如果不属于内存池,进行步骤b3;

步骤b2,此时释放内存属于内存池,将内存重新挂回空闲链,结束本流程;

步骤b3,此时释放内存不属于内存池,则应该属于free heap,根据释放内存地址,得到内存 块信息,判断内存是否合法,

如果不合法,进行步骤b4;

如果合法,将内存设置为空闲,并根据内存大小进行偏移,判断上一个内存块是否空闲, 如果空闲,则将两块内存合并成一个大的空闲内存,结束本流程;

步骤b4,此时释放内存不合法,退出整个应用程序。

2.根据权利要求1所述Linux系统下内存管理方法,其特征在于:在Linux环境下使用大页内 存,是设置n个2M大小hugepage的物理内存,将hugetlbfs挂载到指定目录。

3.根据权利要求1或2所述Linux系统下内存管理方法,其特征在于:内存配置工作过程通过 以下步骤实现,

步骤c1,在挂载hugetlbfs的目录下,打开n个文件,每次打开一个文件时,将文件映射 到内存,映射区长度为一个hugepage页大小,映射内存区为可写可读,映射标识为共享,得 到映射内存的虚拟地址并保存;此次为第一次映射;

步骤c2,若在步骤c1中文件映射失败,则放弃整个内存管理工作,转到步骤c7;若在 步骤c1中文件映射成功,则读取/proc/self/pagemap文件,根据映射内存的虚拟地址计算出 hugepage的物理地址并保存;

步骤c3,若在步骤c2中计算hugepage的物理地址失败,则放弃整个内存管理工作,转 到步骤c7;若在步骤c2中计算hugepage的物理地址成功,则读取/proc/self/numa_maps文件, 取出所有hugepage的虚拟地址以及所在的numa节点,比较numa_maps中的虚拟地址是否和 步骤c1中映射的虚拟地址是否相等,如果相等则将numa节点的信息记录下来;

步骤c4,根据hugepage的虚拟地址,对应的物理地址,以及所在的numa节点,再按照 物理地址做一次排序;

步骤c5,整理排序后的物理地址,出连续的物理内存,然后将再次在hugetlbfs的目录 下,打开步骤c1中的n个文件,每次打开一个文件时,将文件映射到内存,映射区长度为一 个hugepage页大小,映射内存区为可写可读,映射标识为共享,得到映射内存的虚拟地址, 此次为第二次映射,此时映射的虚拟地址和物理地址是一样连续的,长度也是相同的;

步骤c6,解除步骤c1中的第一次映射关系,将第一次映射的虚拟地址空间返回给内核, 内存配置工作完成。

说明书
技术领域

本发明涉及计算机内存管理技术领域,提供了一种Linux系统下内存管理方法。

在Linux系统中,用户通过libc的库函数malloc(或类似函数)来在堆上分配内存空间。libc对内存的分配有两种途径,分别由两个系统调用函数brk和mmap来完成。

在内核中,堆是一个一端固定、一端可伸缩的虚拟地址(vma, virtual memory address)。可伸缩一端通过系统调用函数brk来调整。当进程申请内存小于128k时,使用函数brk分配内存,将_edata(指向数据段最高地址)往高地址推,给进程分配一块内存空间;当进程申请内存大于128k时,使用函数mmap系统调用,在堆和栈之间分配一块虚拟内存。

此时只分配了虚拟地址,还没有申请物理内存,当第一次向虚拟地址读/写数据时,这时由于没有物理内存的映射,因此CPU会产生一次缺页中断。内核捕获到中断,检查产生中断的地址是不是存在于一个合法的虚拟地址中。如果不是,给进程一个“段错误”,使其崩溃;如果是,则分配一个物理页,并为之建立映射。

在释放内存时,由mmap分配的内存可以单独释放,由brk分配的内存需要等到高地址内存释放以后才能释放。

以上就是Linux系统内存管理的流程,但是默认的内存管理函数在通信领域这样需要实时性的应用场景存在一定的不足。

首先在堆上分配和释放内存会有一些额外的开销。系统在接收到分配一定大小内存的请求时,首先查内部维护的内存空闲块表,根据一定的算法(例如分配最先到的不小于申请大小的内存块,或者分配最适于申请的内存块,或者分配最大空闲的内存块)来到合适大小的空闲内存块。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲块。然后系统更新内存空闲块表,完成一次内存分配。

类似地,在释放内存时,系统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,还要把相邻的空闲块合并成较大的空闲块,但是在前面已经说到brk分配的内存需要 高地址内存释放后才能释放,这个时候才能合并空闲块,完成堆的紧缩,在大多数情况下,由于频繁地在堆上分配和释放内存,会导致性能的损失,并且会使系统中出现大量的内存碎片,降低内存使用率。

默认的内存管理函数还考虑到多线程的应用,需要在每次分配和释放内存时加锁,同样增加了开销。

另外一个影响性能的重要因素是内存的地址映射,Linux内核使用页式内存管理,应用程序申请的内存地址是虚拟地址,它需要经过若干级页表一级一级的变换,才能变成真正的物理地址,这就意味着实现一次内存访问,实际上内存被访问了N+1次(N=页表级数),并且还需要做N此加法运算。所以地址映射采用了硬件内存管理单元来支持,并且需要有cache(缓存)来保存页表,这个cache就是TLB(Translation lookaside buffer, 转换后援缓冲器),根据虚拟地址查物理地址时,首先会从TLB中查相应的页表数据,如果TLB中正好存放着所需的页表(TLB Hit),就使用TLB中存放的物理地址,如果TLB没有所需的页表(TLB Miss),就必须访问物理内存中存放的页表,同时更新TLB的页表数据。Linux系统默认采用4k为1个页,物理内存较大时会有很多个分页,而TLB的大小有限,可能会发生大量的TLB miss。

最后,随着科学计算、事物处理对计算机性能要求的不断提高,numa系统的应用越来越广泛,由于访问本地内存资源远远快于远端内存资源,所以在内存分配时,要根据进程所属的numa节点尽量分配本地内存给申请进程。Numa (Non Uniform Memory Access Architecture,非统一内存访问架构)技术可以使众多服务器像单一系统那样运转,同时保留小系统便于编程和管理的优点。基于通信领域应用对内存访问提出的更高的要求,对近端、远端内存的访问就是numa系统的难点。

本发明目的在于克服现有方法的不足,提供内存管理效率,提供一种Linux系统下的内存管理方法,使用hugepage取代传统的4k页,减少转译查缓存丢失的可能(TLB miss),并将hugepage的内存做好地址映射获得物理地址进行排序,到物理地址所属的CPU节点,减少远端内存的申请、访问和释放,提高效率。

本发明的技术方案提供.一种Linux系统下内存管理方法,在Linux环境下使用大页内存,在大页内存基础上执行内存配置工作过程,内存申请过程和内存释放过程,

内存配置工作过程,包括通过计算虚拟地址和物理地址的关系,得到映射的hugepage属于哪个numa节点,并按照物理地址进行排序,

内存申请过程包括内存池配置申请和普通内存申请,通过以下步骤实现,

步骤a1,根据内存池的配置,计算每个内存池需要的内存大小以及每个内存池中保存内存块信息的大小;

步骤a2,在numa节点中到合适的内存分配给各个内存池以及内存池信息记录,然后将内存池初始化,并将相关信息保存到内存池信息记录中;

步骤a3,在应用程序申请内存时,首先判断申请内存是否指定了numa节点,如果指定了numa节点,则在指定numa节点上分配,进行步骤a4;未指定numa节点,则在应用程序所属的numa节点上分配,然后进入步骤a4;

步骤a4,判断申请大小是否为内存池范围内,如果在范围内,进行步骤a5;如果不在范围内,进行步骤a6;

步骤a5,判断内存池是否存在空闲的内存,如果存在,则分配一块内存给应用程序,修改内存池信息,将该内存块从空闲链中摘去;如果不存在,则从其它numa节点的内存池中申请一块内存,结束本流程;

步骤a6,将hugepage中除去内存池之外的内存都视为free heap,所有申请超过内存池大小的都从free heap中申请,并将内存地址、申请大小保存到内存信息中;

内存释放过程,包括以下步骤,

步骤b1,释放内存时,首先判断要释放的内存是否属于内存池,如果属于内存池,进行步骤b2;如果不属于内存池,进行步骤b3;

步骤b2,此时释放内存属于内存池,将内存重新挂回空闲链,结束本流程;

步骤b3,此时释放内存不属于内存池,则应该属于free heap,根据释放内存地址,得到内存块信息,判断内存是否合法,

如果不合法,进行步骤b4;

如果合法,将内存设置为空闲,并根据内存大小进行偏移,判断上一个内存块是否空闲,如果空闲,则将两块内存合并成一个大的空闲内存,结束本流程;

步骤b4,此时释放内存不合法,退出整个应用程序。

而且,在Linux环境下使用大页内存,是设置n个2M大小hugepage的物理内存,将hugetlbfs挂载到指定目录。

而且,内存配置工作过程通过以下步骤实现,

步骤c1,在挂载hugetlbfs的目录下,打开n个文件,每次打开一个文件时,将文件映射到内存,映射区长度为一个hugepage页大小,映射内存区为可写可读,映射标识为共享,得到映射内存的虚拟地址并保存;此次为第一次映射;

步骤c2,若在步骤c1中文件映射失败,则放弃整个内存管理工作,转到步骤c7;若在步骤c1中文件映射成功,则读取/proc/self/pagemap文件,根据映射内存的虚拟地址计算出hugepage的物理地址并保存;

步骤c3,若在步骤c2中计算hugepage的物理地址失败,则放弃整个内存管理工作,转到步骤c7;若在步骤c2中计算hugepage的物理地址成功,则读取/proc/self/numa_maps文件,取出所有hugepage的虚拟地址以及所在的numa节点,比较numa_maps中的虚拟地址是否和步骤c1中映射的虚拟地址是否相等,如果相等则将numa节点的信息记录下来;

步骤c4,根据hugepage的虚拟地址,对应的物理地址,以及所在的numa节点,再按照物理地址做一次排序;

步骤c5,整理排序后的物理地址,出连续的物理内存,然后将再次在hugetlbfs的目录下,打开步骤c1中的n个文件,每次打开一个文件时,将文件映射到内存,映射区长度为一个hugepage页大小,映射内存区为可写可读,映射标识为共享,得到映射内存的虚拟地址,此次为第二次映射,此时映射的虚拟地址和物理地址是一样连续的,长度也是相同的;

步骤c6,解除步骤c1中的第一次映射关系,将第一次映射的虚拟地址空间返回给内核,内存配置工作完成。

本发明保持了内存静态分配的高效率的优点,采用2MB的hugepage页取代4kB节省了页的查询时间,并减少TLB miss的可能,而且hugepage内存不会被交换到磁盘上,保证了内存永远为申请的应用程序使用,在内存申请时优先使用本地内存提高了内存访问速度。

图1 为本发明实施例的内存管理结构示意图。

以下结合附图和实施例详细说明本发明技术方案。

本发明实施例提供一种Linux系统下内存管理方法,采用2M的hugepage的物理内存页,减少TLB miss;通过计算虚拟地址和物理地址的关系,得到映射的hugepage属于哪个numa节点,并按照物理地址进行排序,在hugepage中申请定长的内存池;在hugepage中申请free heap;合法的内存释放。

具体包括在Linux操作系统中预先设置n个2M大小hugepage的物理内存,再使用Hugetlbfs文件系统,将预先设置的hugepage物理内存映射到Hugetlbfs挂载的目录下的hugefile文件;通过读取/proc/self/pagemap页面文件,得到本进程中虚拟地址与物理地址的映射关系,将前面提到hugefile文件中的虚拟地址统计页偏移计算得到物理地址,然后将hugepage物理内存按物理地址排序,然后再次映射得到虚拟地址,使虚拟地址也按照物理地址进行排序;根据hugepage物理内存的物理地址查所属的numa节点,将同一numa节点下的物理内存记录下来;然后在各个numa节点上申请10个内存池,分别为64B、128B、256B、512B、1kB、2kB、4kB、8kB、16kB、32kB,其它内存作为free heap内存;应用模块申请内存时,先判断申请内存是否存在numa节点的要求,如果有,则从指定numa节点的内存池中查申请内存大小的内存是否存在,如果存在,到空闲的内存分配给应用模块,如果不存在,则查其它numa节点中该内存大小空闲最多的内存池,分配一块空闲内存给应用模块,如果申请内存不符合内存池大小要求,则从free heap中申请,到符合要求的空闲内存给应用模块,不到符合要求的空闲内存则此次申请失败;当应用模块释放内存时,根据内存信息将内存返回所属的numa节点的内存池或free heap中。

实施例具体实现如下:

首先是在Linux环境下使用大页内存,采用2M的hugepage的物理内存页的实现方法,包括以下步骤:

步骤1,首先根据需要设置n个2M大小hugepage的物理内存,将Linux系统/sys/kernel/mm/hugepage/hugepage-2048kB/nr_hugepages的值设置为n。

具体实施时,在Linux系统目录/sys/kernel/mm/hugepage/hugepage-2048kB下,将nr_hugepages的值设置为n,即预定了n个2M的huagepage物理页。

具体实施时,n的取值可根据实际物理内存和程序需要申请的物理情况来确定,推荐使用物理内存的一半作为hugepage的内存。

步骤2,将hugetlbfs挂载到指定目录下,后面会在此目录下生成文件供hugepage来映射。

Hugepage:在Linux中物理内存时按页(page)划分的,默认情况是每个页4KB,如果物理内存很大,则页数会很多,则映射物理页的条目会非常多,影响CPU的检索效率。为了减少映射物理页的条目,就采取增大页的尺寸的方法,这个就叫做大页(hugepage)。页的范围可以是2MB-1GB,具体依赖于内核版本和硬件架构。

Hugetlbfs:是Linux中一种文件系统,用于挂载hugepage映射的文件,使该文件系统中的内存操作实现大页调度。

其次是内存配置工作,即进行虚拟地址和物理地址计算,得到numa节点,物理地址排序,包括以下步骤:

步骤1,在挂载hugetlbfs的目录下,打开n个文件,每次打开一个文件时,将文件映射到内存,映射区长度为一个hugepage页大小,即2MB,映射内存区为可写可读(PROT_READ|PROT_WRITE),映射标识为共享(MAP_SHARED),得到映射内存的虚拟地址,并将其保存;这是第一次映射;

步骤2,若在步骤1中文件映射失败,则放弃整个内存管理工作,转到步骤7;若在步骤1中文件映射成功,则读取/proc/self/pagemap文件,根据映射内存的虚拟地址计算出hugepage的物理地址,并将其保存;/proc目录下的文件都是Linux系统的文件,为现有文件,本发明不予赘述;

步骤3,若在步骤2中计算hugepage的物理地址失败,则放弃整个内存管理工作,转到步骤7;若在步骤2中计算hugepage的物理地址成功,则读取/proc/self/numa_maps文件,取出所有hugepage的虚拟地址以及所在的numa节点,比较numa_maps中的虚拟地址是否和步骤1中映射的虚拟地址是否相等,如果相等则将numa节点的信息记录下来;

步骤4,经过前面的步骤后,已经得到了hugepage的虚拟地址,对应的物理地址,以及所在的numa节点,再按照物理地址做一次排序;

步骤5,整理排序后的物理地址,出连续的物理内存,然后将再次在hugetlbfs的目录下,打开步骤1中的n个文件,每次打开一个文件时,将文件映射到内存,映射区长度为一个hugepage页大小,映射内存区为可写可读(PROT_READ|PROT_WRITE),映射标识为共享(MAP_SHARED),得到映射内存的虚拟地址,此次为第二次映射,此时映射的虚拟地址和物理地址是一样连续的,长度也是相同的;

步骤6,解除步骤1中的第一次映射关系,将第一次映射的虚拟地址空间返回给内核,至此内存配置工作完成,结束本流程;后续将进行内存申请阶段;

如果步骤6内存配置失败,退出内存配置,不再进行后续操作。

然后是内存申请,包括内存池配置申请和普通内存申请:

步骤1,根据内存池的配置,计算每个内存池需要的内存大小以及每个内存池中保存内存块信息的大小,实施例中内存池分为10个,分别是64B、128B、256B、512B、1kB、2kB、4kB、8kB、16kB、32kB;

步骤2,在numa节点中到合适的内存分配给各个内存池以及内存池信息记录,然后将内存池初始化,并将内存池中地址、序号、空闲链等信息保存到内存池信息记录中;通常保存的信息还有内存池的内存块总数、已使用内存块数目和内存块使用率,用于查看内存使用状态;

步骤3,在应用程序申请内存时,首先判断申请内存是否指定了numa节点,如果指定了numa节点,则在指定numa节点上分配,进行步骤4;未指定numa节点,则在应用程序所属的numa节点上分配,然后进入步骤4;

步骤4,判断申请大小是否为内存池范围内,如果在范围内,进行步骤5;如果不在范围内,进行步骤6;

步骤5,判断内存池是否存在空闲的内存,如果存在,则分配一块内存给应用程序,修改内存池信息,将该内存块从空闲链中摘去;如果不存在,则从其它numa节点的内存池中申请一块内存,结束本流程;

步骤6,将hugepage中除去内存池之外的内存都视为free heap,所有申请超过内存池大小的都从free heap中申请,并将内存地址、申请大小保存到内存信息中。模拟堆free heap是自定义的名字,用于区别操作系统的堆heap。功能和操作系统的堆类似,由用户申请和释放任意大小的内存,不同的是申请和释放内存是在hugepage上,而不是操作系统的堆上。

因为内存池的物理块有大小限制,最大的块为32kB,如果申请的内存大于32kB就不能在内存池中申请,而必须在free heap中申请。

最后是内存释放:

步骤1,释放内存时,首先判断要释放的内存是否属于内存池,如果属于内存池,进行步骤2;如果不属于内存池,进行步骤3;

步骤2,释放内存属于内存池,将内存重新挂回空闲链,结束本流程;

步骤3,释放内存不属于内存池,则应该属于free heap,根据释放内存地址,得到内存块信息,判断内存是否合法,如果不合法,进行步骤4;如果合法,将内存设置为空闲,并根据内存大小进行偏移,判断上一个内存块是否空闲,如果空闲,则将两块内存合并成一个大的空闲内存,结束本流程;

步骤4,释放内存不合法,退出整个应用程序。

hugepage的内存主要分为两个部分,一个是内存池,一个是free heap,步骤2是释放内存池的内存,步骤3是释放free heap的内存。

参见图1,各部分说明如下:

挂载文件:Map_0 … Map_n-1为"内存配置工作"步骤1中的打开文件,使用mmap将文件映射到物理内存;

物理内存:物理内存是n个2M的hugepage的集合,图1上每个2M小方块就是1个hugepage的内存,物理内存中不同样式的内存形式,如具体图例解释。

memseg(物理内存段):使用mmap映射n个hugepage的物理内存时,物理内存可能不是连续的,可能存在内存空洞,在图1中存在3个内存空洞,所以物理内存被分成了四块,所以有4个memseg,分别指向物理内存的首地址。

free_memseg(可用物理内存段):是每个memseg中可用内存的首地址,最开始memseg和free_memseg是指向相同的物理内存的,当使用物理内存后,free_memseg指向memseg中未被使用的内存地址,在图1中,第一个memseg指向的是最上面的内存地址,左斜线的内存被内存池使用,free_memseg就指向内存池下面未被使用的内存。

mempool(所有内存池信息保存结构):指向内存池信息的内存,在图1中只有1个内存池信息的内存,所以mempool中只有一条线指向内存池信息的内存。

内存池信息:记录内存池中各个长度的内存池的地址、使用情况、锁和调试debug信息的内存,便于用户做查和释放。

内存池:实际响应用户申请的内存,根据用户申请内存的大小,从不同的长度的内存池中分配内存给用户。

free_heap(模拟堆):模拟操作系统的堆(heap)工作在hugepage中申请的内存块,free_heap也可以有多个,图1中仅使用一个作为例子。当从free_heap中申请内存时,首先需要在free_heap的开始和结束的内存中申请first elem和last elem两个元素,用来保存申请内存的信息。

first elem(模拟堆首元素):保存free heap的低地址,用户申请的内存长度,所属memseg,本块内存是否使用,下块可用内存地址等信息,用于做下一步的内存申请和释放。

last elem(模拟堆最后元素):保存free heap的高地址,first elem地址等信息,用于做用于保护free heap,防止申请的内存越界。

Map_[0-1023]文件为hugepage通过mmap映射的挂载文件。即n=1024。

因为hugepage的内存可能不是连续的物理内存,所以需要使用memseg记录物理内存的首地址,有多少物理内存段,就有多少个memseg。物理内存段中可能有已经使用的内存和没有使用的内存,没有使用的内存的首地址也被记录到的free_memseg中。

在分配好的内存池时,会将各个内存池的信息记录在内存池信息的内存中,mempool保存的是内存池信息的内存地址。

free heap是模拟操作系统的堆(heap)功能在hugepage中申请的内存块,当申请内存大小不在内存池范围内时,就从free heap中申请内存。每次在free heap中申请内存时,都会额外多申请一个elem大小的结构,包括保存申请的长度,上一个内存块指针和下一个空闲内存块地址的指针,用以在释放内存时合并空闲内存块。

上述实施例描述仅为了清楚说明本发明的基本方法和装置,但本发明并不仅限于上述实施例;凡是依据本发明的技术实质上实施例所作的任何简单修改、等同变化与修饰,均落入本发明的技术方案的保护范围之内。

本文发布于:2023-04-13 13:38:55,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/3/86044.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图