目录
PreALLOC 预分配导论
PreALLOC 使用场景
PreALLOC 方式分配匿名内存
PreALLOC 方式分配共享内存
PreALLOC 方式分配文件映射内存
PreALLOC 方式分配 PFNMAP 映射内存
PreALLOC 方式分配 MMIO 映射内存
PreALLOC 方式分配 DMA 内存
PreALLOC 方式分配 VMALLOC 内存
PreALLOC 方式分配透明大页内存
PreALLOC 方式分配 HugeTLB 大页内存
PreALLOC 方式分配 DAX 内存
PreALLOC 预分配导论
PreALLOC 分配方式是用户进程分配虚拟内存的一种方式,其在分配虚拟内存的时候同时也将物理内存分配好,并建立好页表映射,那么当进程访问虚拟内存时都会以最快的速度进行访问. 相比于惰性分配方式,PreALLOC 分配方式分配的虚拟内存并不会引起缺页. 与惰性内存分配相比,其具有如下特点:
- 即时可用性: 分配后的内存立即可用,无需等待后续的缺页处理,这在需要快速访问内存的情况下是有利的
- 确定性: 由于物理内存已经分配,因此减少了因内存不足而无法分配的风险, 这在内存需求可预测且稳定的环境下是一个优点
- 性能: 避免了运行时的缺页处理开销, 缺页(PageFault)处理可能涉及到复杂的内存管理操作,预先分配可以减少这些操作,从而提高性能
- 内存利用率: 可能导致内存利用率降低,特别是在分配了但长时间未使用的内存情况下, 这在内存资源紧张的系统中可能成为一个问题
- 启动延迟: 程序启动时需要分配和初始化所有预先分配的内存,可能会导致启动延迟
- 内存碎片: 长期运行的程序可能导致物理内存碎片化,特别是在频繁申请和释放大块内存的场景下
- 扩展性问题: 对于需要大量内存的应用,预先分配可能导致无法满足需求,特别是在内存受限的环境中
因此开发者在选择PreALLOC 分配方式之前需要对自己的场景充分考虑其带来的优点和缺点. 在内存充足且对性能有高要求的环境下,预先分配是一种合理的选择. 然而在内存紧张或应用具有不确定的内存使用模式的情况下,可能更倾向于使用惰性分配的方式.
Linux 提供的 mmap 系统调用可以实现 PreALLOC 分配方式重要接口, 应用程序可以使用该接口为其分配虚拟内存,同时也分配物理内存,最终建立页表映射到物理内存上. mmap 系统调用为了能够实现 PreAlloc 方式的分配虚拟内存,那么 6 个参数应该按如下要求进行设置:
- 参数一: 该参数可以设置从进程地址空间的指定位置分配虚拟内存,如果应用程序有这个需求可以通过该参数实现.
- 参数二: 该参数用于设置分配虚拟内存的大小
- 参数三: 该参数用于设置虚拟内存的访问权限,目前支持 PROT_READ、PROT_WRITE、PROT_EXEC, 三者可以混合搭配使用,也支持 PROT_NONE 将虚拟内存设置为即不可读也不可写, 但这些搭配也要符合系统的要求.
- 参数四: 该参数用于选择虚拟内存的映射方式和特殊需求,参数通过指定的标志进行选择,例如 MAP_SHARED 表示采用共享映射、MAP_PRIVATE 表示采用匿名映射等,对于 PreALLOC 最重要的标志是 MAP_POPULATE, 也就是进程想分配 PreALLOC 的内存就必须带该标志.
- 参数五: 当虚拟内存用于映射文件,那么需要将文件的 FD 填入该位置,否则该参数保持 “-1”.
- 参数六: 该虚拟内存需要映射到指定物理区域时,可以将指定物理区域的起始地址填入到该位置; 反之如果支持映射到随机的物理页上,那么该参数设置为 0.
PreALLOC分配的虚拟内存支持映射到多种物理区域上,有的可以通过采用合适的 mmap 参数就可以实现,有的需要借助特殊系统提供的文件,有的则需要设备驱动提供专门的接口,但无论采用何种接口都需要 mmap 系统调用完成,那么对于不同类型的物理内存其在预分配场景下,mmap 的参数逻辑如下:
- OSMEM: 对于映射系统管理的物理内存,只需在 mmap 系统调用里采用 MAP_POPULATE 标志即可, 其他参数任意
- RSVDMEM: 对于映射系统预留物理内存,需要通过 “/dev/mem” 文件或者设备驱动提供的特殊文件实现,mmap 系统调用无需采用 MAP_POPULATE 标志,由于是通过文件映射的方式,因此不能采用 MAP_ANONYMOUS 标志.
- MMIO: 对于映射 MMIO 区域,需要通过 “/dev/mem” 文件或者设备驱动提供的特殊文件实现,mmap 系统调用无需采用 MAP_POPULATE 标志,由于是通过文件映射的方式,因此不能采用 MAP_ANONYMOUS 标志.
- PMEM: 对于映射 PMEM 内存,需要采用 DAX 方式进行映射,因此需要借助特殊的文件系统和文件,mmap 系统调用需要采用 MAP_POPULATE 标志,另外由于通过文件映射的方式,因此不能采用 MAP_ANONYMOUS 标志.
- HUGELTB: 对于映射 HUGETLB 物理大页,可以通过匿名的方式映射 HUGETLB 大页,那么需要添加 MAP_ANONYMOUS 和 MAP_HUGETLB 标志; 反之如果通过 Hugetlbfs 文件系统的方式映射 HUGETLB 大页,那么需要打开指定的文件即可. 无论采用哪一种映射 HUGETLB 物理大页,如果采用 PreALLOC 方式分配内存时,需要采用 MAP_POPULATE 标志.
- THP: 对于映射透明大页,由于 THP 有很多中类型,这里以匿名透明大页为例进行说明,需要采用 MAP_ANONYMOUS 和 MAP_PRIVATE 标志,另外虚拟内存大小要超过 2MiB,并且如果指定虚拟地址,那么虚拟地址也要按 2MiB 对齐,最后就是采用 PreALLOC 方式映射就需要带上 MAP_POPULATE 标志.
- VMALLOC: VMALLOC 分配的内存可以与用户进程实现共享,那么用户进程需要基于私有的驱动模块来实现 VMALLOC 的映射,此时驱动模块会提供相应的文件,因此不能带有 MAP_ANONYMOUS 标志,另外也不需要带上 MAP_POPULATE 标志就可以实现预分配,这个操作有私有的驱动负责预分配的动作.
- DMA: DMA 内存的特点就是大块连续的物理内存,因此进程可以借助私有驱动程序将 DMA 内存映射到进程地址空间,并且驱动负责实现预分配的操作,因此 mmap 不能带 MAP_ANONYMOUS 和 MAP_POPULATE 标志.
- PAGECACHE: PAGECACHE 其实对应的是文件映射,由于文件映射的特点,因此不能待 MAP_ANONYMOUS 标志,文件映射为了实现预分配需要带上 MAP_POPULATE.
- SHMEM: 共享内存支持文件映射方式和匿名方式进行映射,当使用匿名方式时需要带上 MAP_SHARED 和 MAP_ANONYMOUS 标志,而对于文件映射方式则仅仅带上 MAP_SHARED 标志即可。共享内存为了实现预分配,那么只需带上 MAP_POPULATE 即可.