我使用的是ARM64系统(M1)。
$ uname -m
arm64
这是我用来查找系统上虚拟地址空间范围的 C 程序。
#include <stdlib.h>
#include <stdio.h>
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(void) {
printf("Page size = %d\n", getpagesize());
struct rlimit x;
getrlimit(RLIMIT_AS, &x);
printf("Current maximum size = %llx\n", x.rlim_cur);
printf("Limit on maximum size = %llx\n", x.rlim_max);
unsigned long long int value = 0;
value -= 1;
printf("Maximum float = %llx", value);
return 0;
}
输出:
Current maximum size = 7fffffffffffffff // 9223372036854775807
Limit on maximum size = 7fffffffffffffff // 9223372036854775807
Maximum float = ffffffffffffffff // 18446744073709551615
最大尺寸似乎是2^63-1。最后一位根本没有被使用。
为什么会这样?在64位系统上,虚拟内存地址范围应该到2^64-1对吧?
我知道地址只使用48位,但这似乎与地址空间的范围无关(为什么32位和64位地址有两种不同的长度?)
就arm64硬件而言,EL0和EL1共享一个转换机制,
TTBR0_EL1
和TTBR1_EL1
分别控制地址空间的下半部分和上半部分。通常下半部分用于用户态,上半部分用于内核。
但首先要注意的是,
rlimit
与硬件无关。这是关于操作系统的。
XNU 有这个(也被复制到 Apple 的 SDK 中):
#define RLIM_INFINITY (((__uint64_t)1 << 63) - 1) /* no limit */
所以这只是“无限制”值。
第二点,尽管有
RLIMIT_AS
的描述,但这与地址空间大小无关。这大约是流程中所有现有映射的总和。
arm64 XNU 允许您映射内存的实际最大地址是 0x00007ffffe000000。 该值只是针对 macOS 进行了硬编码。还有一个最小地址,在进程初始化时设置为进程中主二进制文件的基地址(通常为 0x100000000)加上 ASLR 幻灯片。
在 macOS 以外的 Apple 操作系统上,最大地址的规则更复杂,但以下是相关代码位:
osfmk/mach/arm/vm_param.h
:
#if defined(XNU_PLATFORM_MacOSX) || defined(XNU_PLATFORM_DriverKit)
#define MACH_VM_MAX_ADDRESS_RAW 0x00007FFFFE000000ULL
#else
#define MACH_VM_MAX_ADDRESS_RAW 0x0000000FC0000000ULL
#endif
osfmk/mach/shared_region.h
:
#define SHARED_REGION_BASE_ARM64 0x180000000ULL
#define SHARED_REGION_SIZE_ARM64 0x100000000ULL
osfmk/arm/pmap/pmap.c
:
/* end of shared region + 512MB for various purposes */
#define ARM64_MIN_MAX_ADDRESS (SHARED_REGION_BASE_ARM64 + SHARED_REGION_SIZE_ARM64 + 0x20000000)
// Max offset is 13.375GB for devices with "large" memory config
#define ARM64_MAX_OFFSET_DEVICE_LARGE (ARM64_MIN_MAX_ADDRESS + 0x138000000)
// Max offset is 9.375GB for devices with "small" memory config
#define ARM64_MAX_OFFSET_DEVICE_SMALL (ARM64_MIN_MAX_ADDRESS + 0x38000000)
vm_map_offset_t
pmap_max_64bit_offset(
__unused unsigned int option)
{
vm_map_offset_t max_offset_ret = 0;
#if defined(__arm64__)
const vm_map_offset_t min_max_offset = ARM64_MIN_MAX_ADDRESS; // end of shared region + 512MB for various purposes
if (option == ARM_PMAP_MAX_OFFSET_DEFAULT) {
max_offset_ret = arm64_pmap_max_offset_default;
} else if (option == ARM_PMAP_MAX_OFFSET_MIN) {
max_offset_ret = min_max_offset;
} else if (option == ARM_PMAP_MAX_OFFSET_MAX) {
max_offset_ret = MACH_VM_MAX_ADDRESS;
} else if (option == ARM_PMAP_MAX_OFFSET_DEVICE) {
if (arm64_pmap_max_offset_default) {
max_offset_ret = arm64_pmap_max_offset_default;
} else if (max_mem > 0xC0000000) {
// devices with > 3GB of memory
max_offset_ret = ARM64_MAX_OFFSET_DEVICE_LARGE;
} else if (max_mem > 0x40000000) {
// devices with > 1GB and <= 3GB of memory
max_offset_ret = ARM64_MAX_OFFSET_DEVICE_SMALL;
} else {
// devices with <= 1 GB of memory
max_offset_ret = min_max_offset;
}
} else if (option == ARM_PMAP_MAX_OFFSET_JUMBO) {
if (arm64_pmap_max_offset_default) {
// Allow the boot-arg to override jumbo size
max_offset_ret = arm64_pmap_max_offset_default;
} else {
max_offset_ret = MACH_VM_MAX_ADDRESS; // Max offset is 64GB for pmaps with special "jumbo" blessing
}
} else {
panic("pmap_max_64bit_offset illegal option 0x%x", option);
}
assert(max_offset_ret <= MACH_VM_MAX_ADDRESS);
assert(max_offset_ret >= min_max_offset);
#else
panic("Can't run pmap_max_64bit_offset on non-64bit architectures");
#endif
return max_offset_ret;
}
因此,在 iOS 和其他非 macOS 配置上,如果您有“巨型”地图(可以通过
com.apple.developer.kernel.extended-virtual-addressing
权利获得),则地址大小限制为 0x0000000fc0000000,或者在具有 com.apple.developer.kernel.increased-memory-limit
的 some设备上),否则为 0x00000002a0000000、0x00000002b8000000 或 0x00000003b8000000,具体取决于设备拥有多少物理内存。
请注意,后三个大小可能会发生变化,因为它们是根据共享缓存区域边界的大小计算得出的,而共享缓存区域边界本身也会发生变化。今天的“9.375GB”和“13.375GB”评论也是错误的,因为它们源于
SHARED_REGION_SIZE_ARM64
是0xa0000000
的时代。