当前位置: 首页> 汽车> 行情 > 深圳福田公司_百度域名服务器_百度关键词下拉有什么软件_互联网app推广具体怎么做

深圳福田公司_百度域名服务器_百度关键词下拉有什么软件_互联网app推广具体怎么做

时间:2025/8/27 9:14:48来源:https://blog.csdn.net/m0_74186706/article/details/147026344 浏览次数: 0次
深圳福田公司_百度域名服务器_百度关键词下拉有什么软件_互联网app推广具体怎么做

一、不连续页分配器

1.系统接口

  1. 不连续页分配器提供的接口如下:

    • void *vmalloc(unsigned long size);
      分配不连续的物理页并且把物理页映射到连续的虚拟地址空间。
    • void vfree(const void *addr);
      释放 vmalloc 分配的物理页和虚拟地址空间。
    • void *vmap(struct type **pages, unsigned int count, unsigned long flags, pgprot_t prot);
      把已经分配的不连续物理页映射到连续的虚拟地址空间。
    • void vunmap(const void *addr);
      释放使用 vmap 分配的虚拟地址空间。
  2. 内核提供接口函数

    • void *kvmalloc(size_t size, gfp_t flags);
      首先尝试使用 kmalloc 分配内存块,若失败则使用 vmalloc 函数分配不连续的物理页。
    • void kvfree(const void *addr);
      若内存块由 vmalloc 分配,则使用 vfree 释放;否则使用 kfree 释放。

Linux 中常用内存分配函数:

  • 用户空间

    • malloc/calloc/realloc/free:不保证物理连续,大小受堆申请限制,单位为字节。场景:calloc 初始化为 0,realloc 改变内存大小。
    • mmap/munmap:场景为将文件利用虚拟内存技术映射到内存。
    • brk/sbrk:场景是虚拟内存到内存的映射。
  • 内核空间

    • vmalloc/vfree:虚拟连续、物理不连续,大小受 vmalloc 区限制,单位为页(vmalloc 区域)。场景:可能睡眠,不能从中断上下文或不允许阻塞的场景中调用。
    • slabkmalloc/kcalloc/krealloc/kfree):物理连续,大小限制为 64b-4MB,单位为 2order 字节(Normal 区域)。场景:大小有限,适合固定大小数据的频繁分配释放,通过 kmem_cache_create 管理,分配从缓存池获取,释放时不一定真正释放内存,由 slab 管理。
    • 伙伴系统
      • __get_free_pages/get_free_pages:物理连续,4MB(1024 页),单位为页(Normal 区域)。场景:__get_free_pages 限定不能使用 HIGHMEM
      • alloc_page/alloc_pages/free_pages:物理连续,4MB,单位为页(Normal/Vmalloc 区域均可)。场景:配置定义最大页面数 211,一次最多分配 1024 页。

2.内核源码数据结构

  • 每个虚拟内存区域对应一个 vmap_area 实例;
  • 每个 vmap_area 实例关联一个 vm_struct 实例。

以下是 vmalloc 分配内存时的详细流程和关键数据结构的关系:

1. vmalloc 的核心流程

当调用 vmalloc(size) 时,内核会完成以下步骤:

步骤 1:分配虚拟地址空间
  • 内核通过 vmap_area 管理器(全局红黑树)找到一个大小合适的 空闲虚拟地址区域

  • 创建一个新的 struct vmap_area,记录该区域的起始和结束虚拟地址(va_start 和 va_end)。

步骤 2:分配物理内存页
  • 内核根据请求的大小,分配 多个物理页(通过 alloc_page() 或 alloc_pages())。

  • 这些物理页可能 物理地址不连续,但会被分配到一个 `struct page 数组** 中。

步骤 3:建立映射关系
  • 创建一个 struct vm_struct,记录以下信息:

    • 虚拟地址范围(addr 字段,对应 vmap_area 的 va_start)。

    • 物理页数组(pages 字段,指向分配的 struct page 数组)。

    • 区域类型(flags 字段,例如 VM_ALLOC 表示 vmalloc 分配)。

  • 将 vmap_area 的 vm 字段指向这个 vm_struct,建立两者的关联。

步骤 4:建立页表映射
  • 内核通过 页表 将虚拟地址(vm_struct->addr)映射到物理页(vm_struct->pages 中的页)。

  • 对于每个物理页,内核修改页表项,设置虚拟地址到物理地址的映射关系。


2. 关键数据结构的关系

  • vmap_area:负责管理内核虚拟地址空间的分配,记录虚拟地址范围。

  • vm_struct:描述具体的内存区域,记录虚拟地址和物理页数组。

  • struct page:表示物理内存页,通过 vm_struct->pages 数组管理。

关系示意图:

vmap_area (管理虚拟地址)  │└──→ vm_struct (关联虚拟地址和物理页)  │└──→ pages[] (物理页数组)  │├──→ page 0  ├──→ page 1  └──→ ...  

3. 如何通过 vm_struct 找到物理内存?

  1. 从虚拟地址到 vm_struct

    • 给定一个 vmalloc 返回的虚拟地址 addr,内核可以通过遍历 vmap_area 的红黑树,找到对应的 vmap_area

    • 通过 vmap_area->vm 字段获取关联的 vm_struct

  2. 从 vm_struct 到物理页

    • vm_struct->pages 是一个 struct page* 数组,每个元素指向一个物理页。

    • 通过 vm_struct->pages[i] 可以获取第 i 个物理页的 struct page

  3. 从 struct page 到物理地址

    • 通过 page_to_phys(vm_struct->pages[i]) 可以获取该物理页的 物理地址


4. 示例:访问 vmalloc 分配的物理内存

假设通过 void *ptr = vmalloc(4096 * 4); 分配了 16KB 内存:

  1. 虚拟地址分配

    • vmap_area 记录 va_start=0xffff888800000000va_end=0xffff888800004000

  2. 物理页分配

    • 分配 4 个物理页(假设物理地址为 0x10000x50000x90000xd000)。

  3. vm_struct 关联

    • vm_struct->addr = 0xffff888800000000

    • vm_struct->pages[0] 指向物理页 0x1000 的 struct page

    • vm_struct->pages[1] 指向物理页 0x5000 的 struct page,依此类推。

  4. 页表映射

    • 虚拟地址 0xffff888800000000 映射到物理地址 0x1000

    • 虚拟地址 0xffff888800001000 映射到物理地址 0x5000,依此类推。


5. 物理内存的非连续性

  • vmalloc 的物理页可能不连续:这是 vmalloc 的核心特点!它适用于需要 大块连续虚拟地址,但物理内存可以零散的场景。

  • kmalloc vs. vmalloc

    • kmalloc 分配的物理内存是连续的(虚拟和物理均连续)。

    • vmalloc 分配的物理内存不保证连续(虚拟连续,物理不连续)。


6. 释放内存(vfree)的流程

  1. 通过虚拟地址找到对应的 vmap_area

  2. 通过 vmap_area->vm 获取 vm_struct

  3. 释放 vm_struct->pages 中的所有物理页。

  4. 解除虚拟地址的页表映射。

  5. 删除 vmap_area 和 vm_struct

        其中,即使已经通过 struct page 分配了物理内存,也必须设置页表映射,因为 CPU 访问内存时只能通过 虚拟地址,而虚拟地址到物理地址的转换需要依赖页表。以下是详细的解释:

1. 核心原因:CPU 只认虚拟地址

现代 CPU 的指令和硬件设计决定了:

  • 所有内存访问都基于虚拟地址:无论是用户程序还是内核代码,CPU 执行的指令中涉及的地址都是虚拟地址。

  • 页表是地址翻译的硬件依赖:CPU 的 MMU(内存管理单元)通过页表将虚拟地址转换为物理地址,没有页表映射,虚拟地址无法被正确翻译。

  • 即使内核也需要虚拟地址:内核代码和数据虽然运行在高特权级,但依然需要通过虚拟地址访问内存。


2. struct page 只是物理内存的描述符

  • struct page 的作用:记录物理页的元数据(如引用计数、状态等),但它本身不提供访问物理内存的路径

  • 物理内存的访问必须通过虚拟地址:内核需要通过虚拟地址才能读写物理内存,而虚拟地址必须通过页表映射到物理地址。


3. 以 vmalloc 为例的完整流程

(1) 分配物理页(struct page
  • 内核调用 alloc_page() 分配物理页,获得 struct page*

  • 此时物理页已存在,但没有虚拟地址可以访问它

(2) 分配虚拟地址(vmalloc
  • vmalloc 从内核虚拟地址空间分配一段连续的虚拟地址范围(由 vmap_area 管理)。

  • 此时虚拟地址范围已分配,但未与物理页关联

(3) 建立页表映射
  • 内核将虚拟地址范围与物理页通过页表映射关联:

    for (每个分配的物理页) {虚拟地址 = vmalloc分配的起始地址 + i * PAGE_SIZE;物理地址 = page_to_phys(pages[i]);修改页表项,将虚拟地址映射到物理地址;
    }
  • 只有完成这一步,才能通过虚拟地址访问物理内存。


4. 如果不设置页表会发生什么?

  • 访问虚拟地址会触发缺页异常(Page Fault):CPU 发现页表中没有对应的物理地址映射。

  • 内核无法处理这种异常:因为 vmalloc 分配的虚拟地址没有关联物理页,缺页处理程序无法自动修复映射。

  • 结果:访问未映射的虚拟地址会导致内核崩溃(如 "Unable to handle kernel paging request")。


5. 页表映射的本质

  • 页表是硬件与软件的桥梁

    • 硬件依赖:CPU 的 MMU 必须通过页表完成地址翻译。

    • 软件管理:内核负责维护页表,确保虚拟地址与物理地址的映射关系正确。

  • 即使内核知道物理地址,也必须通过虚拟地址访问内存

    // 示例:通过虚拟地址访问物理内存
    char *vaddr = vmalloc(4096);  // 返回虚拟地址
    strcpy(vaddr, "Hello");       // 通过虚拟地址写入数据(依赖页表映射)

6. 例外情况:直接物理地址访问

某些特殊场景(如设备驱动)可能需要直接操作物理地址,但仍需间接映射:

  • ioremap:将设备的物理地址映射到内核虚拟地址空间。

  • phys_to_virt:在内核线性映射区域中,将物理地址转换为固定的虚拟地址(仅适用于低端内存)。

  • 但这些都是通过隐式或显式的页表映射实现的。

3.技术原理

        malloc 虚拟地址空间的范围是(VMALLOC_START, VMALLOC_END),每种处理器架构都需要定义这两个宏,比如:ARM64 架构定义的宏如下:

        MODULES_END 是内核模块区域的结束地址;PAGE_OFFSET 是线性映射区域的起始地址;PUD_SIZE 是一个页上层目录表项映射的地址空间长度;VMEMMAP_SIZE 是 vmemmap 区域的长度。

vmalloc 虚拟地址空间的起始地址 = 内核模块区域的结束地址
vmalloc 虚拟地址空间的结束地址 = 线性映射区域的起始地址 - 一个页上层目录表项映射的地址空间长度 - vmemmap 区域的长度 - 64KB

vmalloc 函数执行过程分为三步:
        1、分配虚拟内存区域
        2、分配物理页
        3、在内核的页表中把虚拟页映射到物理页

 备注:函数vmap和vmalloc的区别在于不需要分配物理页。

一、vmalloc 虚拟地址空间起始地址设定原因

  1. 内核内存布局规划
    内核空间中,不同区域承担不同功能。MODULES_END 标识内核模块区域的结束,模块加载后,后续地址空间可用于其他动态内存分配场景。vmalloc 用于分配物理地址不连续、虚拟地址连续的内存,将其起始地址设为内核模块区域结束地址,既能充分利用内存空间,又能保证模块区与 vmalloc 区互不干扰,维持内核内存布局的清晰性。

二、vmalloc 虚拟地址空间结束地址设定原因

  1. 避开线性映射区域
    • PAGE_OFFSET 是线性映射区域(直接映射物理内存的虚拟地址区域)的起始地址。vmalloc 区域不能与线性映射区重叠,因此需从 PAGE_OFFSET 向前推算结束地址。
  2. 为其他内存管理结构预留空间
    • PUD_SIZE:页上层目录表项(如 PUD,页全局目录的上一级目录)映射的地址空间长度。内核页表层级管理需要预留这类结构的空间,避免地址冲突。
    • VMEMMAP_SIZEvmemmap 区域用于管理内存页的元数据(如页状态、引用计数等),需在内核地址空间中占据固定区域,因此计算时要减去该长度。
    • 64KB:通常是为地址对齐、边界保护或其他内核内部机制预留的缓冲空间,确保地址划分的严谨性,避免因边界误差导致内存管理错误。

三、整体布局意义

这种地址计算方式是内核内存管理的系统性设计:既保证 vmalloc 区域与内核模块区、线性映射区、内存元数据管理区等功能模块的地址隔离,又通过精确计算实现内存空间的高效利用,最终确保内核内存管理的稳定性和功能性。

二、页表

        页表是一种特殊数据结构,放在系统空间的页表区,存放逻辑页与物理页帧的对应关系。每一个进程都有自己的页表,PCB表中有指针指向页表。

        分页逻辑地址 = P 页号.D 页内位移,分页物理地址 = F 页帧号.D 页内位移。
        P = 线性逻辑地址 / 页面大小,D = 线性逻辑地址 - P * 页面大小。

        CPU 不直接访问物理内存地址,而是通过虚拟地址空间间接访问。虚拟地址空间是操作系统为执行进程分配的逻辑地址(如 32 位系统范围 0 - 4G - 1),操作系统在虚拟地址空间与物理内存地址间建立映射。

        通常将虚拟地址空间以 512byte - 8K 为单位(称页,从 0 编号,该单位大小即页面),物理地址按同样大小为单位(称框或块,从 0 编号),OS 维护表记录页与框的映射关系。Windows 系统页面大小为 4KB。

 

        系统为每个进程建立一个页表,在进程逻辑地址空间中每一页,依次在页表中有一个表项,记录该页对应的物理块号。通过查找页表就可以很容易地找到该页在内存中的位置。页表具有逻辑地址到物理地址映射作用。

  

【ARM处理器页表】

        Linux 内核把页表直接分为 4 级:页全局目录(PGD)、页上层目录(PUD)、页中间目录(PMD)、直接页表(PT)。如果选择三级(页全局目录、页中间目录、直接页表)。如果选择二级(页全局目录和直接页表)。

        五级页表的结构,每个进程有独立的页表,进程的 mm_struct 实例成员 pgd 指向页全局目录。前面四级页表的表项存放下一级页表的起始地址,直接页表的表项存放页帧号(PFN)。

查询页表,把虚拟地址转换成物理地址流程:

  1. 根据页全局目录的起始地址和页全局目录索引得到页全局目录表项的地址,然后再从表项得到页四级目录的起始地址;
  2. 根据页四级目录的起始地址和页四级目录索引得到页四级目录表项的地址,然后从表项得到页上层目录的起始地址;
  3. 根据页上层目录的起始地址和页上层目录索引得到页上层目录表项的地址,然后从表项得到页中间目录的起始地址;
  4. 根据页中间目录的起始地址和页中间目录索引得到页中间目录表项的地址,然后从表项得到直接页表的起始地址;
  5. 根据直接页表的起始地址和直接页表索引得到页表项的地址,然后从表项得到页帧号;
  6. 把页帧号和页内偏移组合形成物理地址。

    更多细节见:
四级页表与五级页表_arm64支持几级页表-CSDN博客

 

https://github.com/0voice

关键字:深圳福田公司_百度域名服务器_百度关键词下拉有什么软件_互联网app推广具体怎么做

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: