出于教育目的,我正在计算我可以在Linux上分配多少虚拟内存.在x86_64上,它分配了128TB的虚拟内存,如documentation所示.但在ARM64上,我只分配了170TB的虚拟内存,尽管documentation 上的虚拟内存是256TB.我想了解是什么阻止我分配256TB的虚拟内存.

所以我写了一个程序

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

int main() {
    char *chars;
    size_t nbytes;

    while(chars != MAP_FAILED) {
      nbytes += 0x10000000000; // 1TB
      chars = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
      );

      munmap(chars, nbytes);
    }

    printf("Allocated %ld total TB\n", nbytes/1024/1024/1024/1024);
    exit(EXIT_FAILURE);
}

设置超量使用的参数:

echo 1 > /proc/sys/vm/overcommit_memory

并得到了结果:

Allocated 171 total TB

我试着增加参数:

sysctl -w vm.max_map_count=655300000
ulimit -l unlimited

但没有什么是有帮助的.

我的内核参数:

# grep CONFIG_ARM64_VA_BITS /boot/config-$(uname -r)
# CONFIG_ARM64_VA_BITS_39 is not set
CONFIG_ARM64_VA_BITS_48=y
CONFIG_ARM64_VA_BITS=48

# grep CONFIG_ARM64_PA_BITS /boot/config-$(uname -r)
CONFIG_ARM64_PA_BITS_48=y
CONFIG_ARM64_PA_BITS=48

我的系统:

ARM Cortex A53 (ARMv8) - 1GB RAM
5.15.0-1034-raspi #37-Ubuntu SMP PREEMPT
# free -m
               total        used        free      shared  buff/cache   available
Mem:             905         198         346           3         360         616

推荐答案

You're allocating a contiguous chunk of virtual memory, and that's the biggest hole in the address space.
(Needless to say, the address space of your program is 48 bits, i.e. 256TiB.)

当您的进程运行时,您可以通过查看/proc/self/maps(或/proc/<pid>/maps)来看到这一点.下面是一个非常懒惰的测试用例,它直接调用system():

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

int main(void)
{
    char *chars = NULL;
    size_t nbytes = 0;

    char *prev_chars = NULL;
    size_t prev_bytes = 0;

    while(1)
    {
        prev_chars = chars;
        prev_bytes = nbytes;

        nbytes += 0x10000000000; // 1TB
        chars = mmap(
            NULL,
            nbytes,
            PROT_READ | PROT_WRITE,
            MAP_SHARED | MAP_ANONYMOUS,
            -1,
            0
        );

        if(chars == MAP_FAILED)
        {
            break;
        }

        munmap(chars, nbytes);
    }

    printf("Largest successful allocation: %p %zx\n", prev_chars, prev_bytes);
    printf("/proc/self/maps:\n");
    char buf[32];
    snprintf(buf, sizeof(buf), "cat /proc/%u/maps", getpid());
    system(buf);

    return EXIT_FAILURE;
}

如果我用cc -o t t.c -Wall -O3编译它并运行它,那么我会得到以下输出:

Largest successful allocation: 0xaad16a0000 aa0000000000
/proc/self/maps:
aaaad16a0000-aaaad16a1000 r-xp 00000000 103:02 391637                    /tmp/t
aaaad16bf000-aaaad16c0000 r--p 0000f000 103:02 391637                    /tmp/t
aaaad16c0000-aaaad16c1000 rw-p 00010000 103:02 391637                    /tmp/t
aaaae67e1000-aaaae6802000 rw-p 00000000 00:00 0                          [heap]
ffff88150000-ffff882d7000 r-xp 00000000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882d7000-ffff882ec000 ---p 00187000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882ec000-ffff882f0000 r--p 0018c000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882f0000-ffff882f2000 rw-p 00190000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff882f2000-ffff882ff000 rw-p 00000000 00:00 0 
ffff88317000-ffff8833e000 r-xp 00000000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff88350000-ffff88352000 rw-p 00000000 00:00 0 
ffff88352000-ffff88354000 r--p 00000000 00:00 0                          [vvar]
ffff88354000-ffff88355000 r-xp 00000000 00:00 0                          [vdso]
ffff88355000-ffff88357000 r--p 0002e000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff88357000-ffff88359000 rw-p 00030000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fffff69e0000-fffff6a01000 rw-p 00000000 00:00 0                          [stack]

作为参考,我在MacOS主机下的arm64 Debian VM上运行这段代码,但这在这里几乎无关紧要.

您在这里可以看到,正在使用的最低地址是0xaaaad16a0000,其中映射了进程本身的主要二进制文件的段.这是地址0x0的170到171TiB之间的某个地方.紧随其后的是一些堆内存,然后是另一个很大的缺口,直到0xffff88150000,其中映射了libc.这一差距构成了另外85至86TiB的差距.

如果您分别请求170TiB和85TiB的两个单独分配,则这两个调用都应该成功,从而使您的总分配达到255TiB.但是您不能获得连续的映射,因为地址空间的中间有东西.

当然,这是ASLR的错.如果您运行上面的代码,地址在不同的运行中会有所不同,但是映射主二进制的地址应该始终以0xaaaa开头,然后再跟8位数字.为了获得一致的结果,您可以通过使用setarch $(uname -m) -R ./[binary_name]调用ASLR来关闭测试二进制文件的ASLR.那么您的二进制代码应该始终映射到0xaaaaaaaa0000.

但它仍然会位于地址空间的中间.如果你想改变这一点,你需要用-no-pie来编译.如果对上面的代码执行该操作并运行它,您将看到现在可以获得更大的连续分配:

Largest successful allocation: 0xff93880000 ff0000000000
/proc/self/maps:
00400000-00401000 r-xp 00000000 103:02 391637                            /tmp/t
0041f000-00420000 r--p 0000f000 103:02 391637                            /tmp/t
00420000-00421000 rw-p 00010000 103:02 391637                            /tmp/t
0a425000-0a446000 rw-p 00000000 00:00 0                                  [heap]
ffff93880000-ffff93a07000 r-xp 00000000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a07000-ffff93a1c000 ---p 00187000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a1c000-ffff93a20000 r--p 0018c000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a20000-ffff93a22000 rw-p 00190000 103:02 132374                    /usr/lib/aarch64-linux-gnu/libc.so.6
ffff93a22000-ffff93a2f000 rw-p 00000000 00:00 0 
ffff93a53000-ffff93a7a000 r-xp 00000000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff93a8c000-ffff93a8e000 rw-p 00000000 00:00 0 
ffff93a8e000-ffff93a90000 r--p 00000000 00:00 0                          [vvar]
ffff93a90000-ffff93a91000 r-xp 00000000 00:00 0                          [vdso]
ffff93a91000-ffff93a93000 r--p 0002e000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffff93a93000-ffff93a95000 rw-p 00030000 103:02 132368                    /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fffff6806000-fffff6827000 rw-p 00000000 00:00 0                          [stack]

其中0xff0000000000是255TiB.您可以在此基础上再次添加setarch $(uname -m) -R以防止堆地址随机化,但在这一点上,这几乎无关紧要.

当然,这些东西取决于内核和使用的工具链的任意缺省值,所以很可能这篇文章中没有一个是guaranteed的--但在这个时间点上,它是这样工作的.

Linux相关问答推荐

C++17:G++8.5版似乎无法正确生成无符号64位伪随机整数

Linux 的 __fastfail 替代方案?

奇怪的 Bash 脚本行为

Linux:用户态线程在执行系统调用时是否有更高的优先级?

可以用openmp并行化内存分配删除吗? (c++)

在 Ubuntu 中,如何设置 Tomcat 9 以使用 Java 17?

为什么要命令; ls -l file_doesnot_exists > /dev/null 2>&1 正在工作; ls -l 2>&1 file_doesnot_exists > /dev/null 不是

使用带有 gnome-keyring 的 Git 凭证助手作为 Sudo 时出错

MessageBox 的 GTK 实现

如何使用多个版本的 GCC

C.UTF-8 和 en_US.UTF-8 语言环境有什么区别?

将以前忽略的目录添加到 Git 存储库

如何在python中找到文件或目录的所有者

Docker Bash 提示不显示 colored颜色 输出

从文本文件中删除奇数行或偶数行

我可以打开一个套接字并将其传递给 Linux 中的另一个进程吗

如何从目录复制内容而不是符号链接?

如何使用 sed 通过灵活的键和值更改我的配置文件?

在类 Unix 系统中上次运行的 cron 作业(job)的详细信息?

通过 linux x86-64 函数调用保留了哪些寄存器