在 32 位 linux 内核上使用 c++ 在堆上分配超过 2GB

Posted

技术标签:

【中文标题】在 32 位 linux 内核上使用 c++ 在堆上分配超过 2GB【英文标题】:Allocate more than 2GB on the heap using c++ on a 32bit linux kernel 【发布时间】:2013-01-24 02:37:09 【问题描述】:

这似乎是一个很常见的问题,但我仍然没有找到明确的答案。

我可以访问运行 linux 的服务器,该服务器具有 16 GB 的 RAM 和 16 核(64 位)CPU (/proc/cpuinfo 给出“Intel(R) Xeon(R) CPU E5520 @ 2.27GHz”)。然而,内核 是 32 位(uname -m 给出 i686)。当然,我没有root访问权限,所以我无法更改 那个。

我正在运行我编写的 C++ 程序,该程序执行一些需要大量内存的计算,所以我 我需要一个大堆——但每当我尝试分配超过 2GB 的空间时,我都会得到一个 badalloc, 虽然 ulimit 返回“无限”。 为简单起见,假设我的程序是这样的:

#include <iostream>
#include <vector>

int main() 
    int i = 0;
    std::vector<std::vector<int> > vv;
    for (;;) 
        ++i;
        vv.resize(vv.size() + 1);
        std::vector<int>* v = &(vv.at(vv.size() - 1));
        v->resize(1024 * 1024 * 128);
        std::cout << i * 512 << " MB.\n";
    
    return 0;

用g++(无flags)编译后,输出为:

512 MB.
1024 MB.
1536 MB.
2048 MB.
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted

据我了解,这是 32 位系统的限制,显然是因为 32 位指针 只能容纳 2^32 个不同的地址(我是否正确地假设如果我编译了相同的程序 在同一台服务器上,但该服务器运行 64 位内核,程序可以分配超过 2GB?)

这不是分配的内存是否连续的问题,如果我在 示例程序,出现同样的问题,而在我的实际程序中,没有大块的 内存,只是很多小的。

所以我的问题是: 有什么我可以做的吗?我无法更改操作系统,但我当然可以修改我的源代码, 使用不同的编译器选项等...... 但我确实需要超过 2GB 的内存,而且没有明显的方法可以进一步优化 程序使用的算法。

如果答案是明确的“否”,那么知道这一点仍然很好。

谢谢, 卢卡斯

【问题讨论】:

(1) 使用 64 位操作系统和本机应用程序。 (2) 在目标机器上启用Physical Address Extension 支持,如果它是32 位的。这应该会为您购买额外的 gB 以供使用。除此之外(您似乎被一些非常狭窄的可能性所束缚)自我管理的磁盘分页系统可能是您唯一的选择。 如果我在同一台服务器上编译相同的程序,但该服务器运行的是 64 位内核,那么我是否正确地假设该程序可以分配超过 2GB 的空间? -是的。您也许可以通过交换机等获得更多(可能高达 3.5Gb 左右)。基本上,尽管您遇到了可扩展性问题并且如果您真的找不到要按时间或进程拆分处理以避免达到限制,那么您将不得不移动到 64 位。 他能否通过让多个进程分别分配 2GB 来解决这个问题。然后发明一种IPC机制来根据需要将数据读/写到其他进程的内存块?还是只运行一个或多个 memcached 实例? @WhozCraig:PAE 不会增加用户空间虚拟地址空间的大小,或者 C 实现对单个对象大小的限制。 (在 GNU 系统中,它是 PTRDIFF_MAX,因此在 32 位系统上最大对象大小为 2GiB - 请参阅 What is the maximum size of an array in C?)。我假设在 2013 年,一台运行 32 位内核的 16GiB 服务器已经在使用 PAE。唯一的选择是使用 64 位系统。 (如果你真的无法更换操作系统,qemu 可以在 x86 上模拟 x86-64,但当然不会很快,必须分页到磁盘才能使用超过 3GiB。) 【参考方案1】:

如果您一次都需要内存,不,如果不更改为 64 位内核(是的,这将允许您在单个进程)

也就是说,如果您不需要一次全部使用内存,而只是通过快速访问,您可以随时将部分内存存储卸载到另一个进程。

例如,这可以通过将数据存储在另一个进程中来工作,并让您的进程在需要时将该进程中的共享内存临时映射到自己的内存空间中。它仍将存储在内存中,但在切换内存范围时会有一些开销。开销是否可接受取决于您的内存访问模式。

这不是一个非常直接的方法,但如果不更改内核来为您提供 64 位地址空间,听起来您有点束手无策。

编辑:您可以通过重新配置内核将限制提高到 2GB 以上,但这仅意味着您将在那里达到硬限制。此外,这将需要 root 访问权限。

【讨论】:

【参考方案2】:

答案是明确的“不”。如果您无法更改操作系统,则无法提高限制。该限制适用于整个进程,显然没有单个分配可以超过每个进程的限制。

根据用于编译内核的选项,限制可能高于 2GB,但肯定会低于 4GB。但我猜重新编译内核也算“改变操作系统”。

见this discussion

是的,在 64 位操作系统上,该限制将消失。

【讨论】:

以上是关于在 32 位 linux 内核上使用 c++ 在堆上分配超过 2GB的主要内容,如果未能解决你的问题,请参考以下文章

3G/1G vm split 有啥好处? 32位Linux内核

BFD_RELOC_64:使用 C++ 在 32 位 linux 上编译汇编器指令

Linux 内核中的数据类型

linux内核物理内存空间分布

linux磁盘分区

关于32位程序的内存