如何在程序中找到 MMAPable 虚拟地址的范围?

问题描述

背景信息:

  1. 我在 x86 系统上使用 64 位 Arch。

  2. 我没有使用 libc 或任何依赖于 libc 的语言。我正在使用专有的研究语言。我正在通过内联汇编进行系统调用

  3. 我正在为一个研究项目编写一个实验性的自定义分配器,因此便携式解决方案是一个不错的选择,但不是必需的。

  4. 我的程序是静态链接的,我愿意并且能够重写我正在使用的库来解释给定的解决方案。

根据这篇 SO 帖子:Where is the stack memory allocated from for a Linux process? 程序的虚拟地址空间的组织方式如下:

------------------ <--- Top of the process address space
Stack (grows down)
v v v v v v v v v
------------------

(unmapped)

------------------ <--- Maximum stack size.


(unmapped)


-------------------
mmap
-------------------


(unmapped)


-------------------
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
brk (grows up)
-------------------
BSS
-------------------
Data
-------------------
Text
-------------------

------------------- <--- Bottom or process address space.

假设这是正确的,我正在尝试找到 brk 和堆栈之间的 MMAPable 地址范围。

通过从 sbrk(0) 获取 ptr,页面向上对齐 ptr,然后确保永远不会再次调用 brksbrk 来找到下限是否足够?

>

通过将 ptr 移到堆栈上的某个位置、从 getrlimit 获取堆栈的最大大小、从 ptr 中减去该大小、将 ptr 向下对齐页面以及然后确保堆栈的大小永远不会被setrlimit改变?

在这里不需要一个确切的范围,但我确实需要一个大范围的连续地址,这些地址可以保证是安全的 MMAPable(当然,假设我自己不会破坏它们)。在我的研究中,我遇到了这个: https://www.ibm.com/docs/en/aix/7.2?topic=memory-understanding-mapping ,它说:

对于 64 位进程,有两组地址范围和进程地址空间可用于 mmap 或 shmat 映射。第一个由单个范围 0x07000000_00000000-0x07FFFFFF_FFFFFFFF 组成,可用于固定位置和可变位置映射。第二组地址范围仅适用于固定位置映射,包括范围 0x30000000-0xCFFFFFFF、0xE0000000-0xEFFFFFFF 和 0x10_00000000-0x06FFFFFF_FFFFFFFF。该集合的最后一个范围由 0x10_00000000-0x06FFFFFF_FFFFFFFF 组成,也可用于系统加载程序以保存程序文本、数据和堆,因此只有该范围的未使用部分可用于固定位置映射。

这正是我需要的信息类型,但它用于错误的操作系统。如果有人能帮我找到有关 Linux 的相同信息,特别是 Arch,那将是一个很大的帮助。

解决方法

经过大量测试后,我发现我在问题中提出的解决方案确实有效。我一直在使用 cat /proc/<pid>/maps 检查我的自定义分配器,它的行为符合我的预期。重申解决方案:

  1. 要找到下限,请使用 sbrk(0),确保 ptr 页面对齐,然后确保永远不会再次调用 brksbrk

  2. 为了安全地逼近上限,使用 getrlimit 找到堆栈大小,从 ptr 中减去它进入堆栈,页面对齐 ptr,然后永远不要使用 setrlimit 更改堆栈大小.

如果您可能需要触摸 brksbrksetrlimit,那么您还可以在下限中添加一些填充并从上限中减去一些填充。您可以通过使用 /proc/meminfo 查找系统有多少内存来动态计算安全的填充量,或者如果您不需要通用解决方案,您可以根据您的情况过度估计需要多少正在做。