【问题标题】:How to allocate a large memory in C [closed]如何在 C 中分配大内存 [关闭]
【发布时间】:2017-11-02 21:48:00
【问题描述】:

我正在尝试编写一个程序,可以通过malloc(1024*1024*1024)从系统获取1GB内存。

得到内存的起始地址后,以我有限的理解,如果我想初始化它,只需使用memset()即可实现。但事实是一段时间后会触发 segfault

我尝试使用gdb查找原因,最后发现如果我对内存进行一些操作超过128 MB会导致此故障。

是否有任何规则限制程序只能访问小于 128 MB 的内存?还是我分配和初始化的方式不对?

如果需要更多信息,请告诉我。

任何建议将不胜感激。

[平台]

  • Linux 4.10.1 和 gcc 5.4.0
  • 使用gcc test.c -o test 构建程序
  • CPU:英特尔 i7-6700
  • 内存:16GB

[代码]

    size_t mem_size = 1024 * 1024 * 1024;
    ...
    void *based = malloc(mem_size);  //mem_size = 1024^3
    int stage = 65536;
    int initialized = 0;
    if (based) {
            printf("Allocated %zu Bytes from %lx to %lx\n", mem_size, based, based + mem_size);
    } else {
            printf("Error in allocation.\n");
            return 1;
    }
    int n = 0;
    while (initialized < mem_size) {  //initialize it in batches
            printf("%6d %lx-%lx\n", n++, based+initialized, based+initialized+stage);
            memset(based + initialized, '$', stage);
            initialized += stage;
    }

[结果]

  Allocated 1073741824 Bytes from 7f74c9e66010 to 7f76c9e66010
  ...
  2045 7f7509ce6010-7f7509d66010
  2046 7f7509d66010-7f7509de6010
  2047 7f7509de6010-7f7509e66010
  2048 7f7509e66010-7f7509ee6010  //2048*65536(B)=128(MB)
  Segmentation fault (core dumped)

【问题讨论】:

  • 向我们展示您的代码
  • 这可能是代码问题,而不是内存系统问题。即使是 32 位 Linux 系统也应该支持 4GB 的虚拟内存空间。
  • 检查 malloc 是否在分配失败时返回 NULL。您可能正在设置空指针,这可能会导致 SIGSEGV
  • @David 你需要 1GB 连续的虚拟内存。
  • @KChi 您应该将您正在使用的 exact 代码发布为 complete 程序而不是 sn-p。你甚至不应该问一个问题而不这样做。

标签: c linux gcc memory


【解决方案1】:

这里有两个可能的问题。首先是您没有正确使用malloc()。您需要检查它是否返回NULL,或非NULL 值。

另一个问题可能是操作系统过度使用内存,而内存不足 (OOM) 杀手正在终止您的进程。 You can disable over-committing of memory and getting dumps to detect via these instructions.

编辑

两大问题:

  • 不要在日志语句中执行带有副作用的操作(即:n++)。非常糟糕的做法,因为在大型项目中,日志调用通常在编译时被删除,现在程序的行为有所不同。
  • based 转换为(char *)

这应该有助于解决您的问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    size_t mem_size = 1024 * 1024 * 1024;
    printf("MEMSIZE: %lu\n", mem_size);
    printf("SIZE OF: void*:%lu\n", sizeof(void*));
    printf("SIZE OF: char*:%lu\n", sizeof(char*));
    void *based = malloc(mem_size);  //mem_size = 1024^3
    int stage = 65536;
    int initialized = 0;
    if (based) {
        printf("Allocated %zu Bytes from %p to %p\n", mem_size, based, based + mem_size);
    } else {
        printf("Error in allocation.\n");
        return 1;
    }
    int n = 0;
    while (initialized < mem_size) {  //initialize it in batches
        //printf("%6d %p-%p\n", n, based+initialized, based+initialized+stage);
        n++;
        memset((char *)based + initialized, '$', stage);
        initialized += stage;
    }

    free(based);

    return 0;
}

【讨论】:

  • 谢谢,但是我确定我得到了一个非空指针(我更新了上面的示例代码),根据来自 htop 的信息,我的计算机中应该有足够的可用内存
  • @KChi 运行以下命令时会发生什么:gcc -O0 -ggdb test.c &amp;&amp; gdb -ex run ./a.outgdb 是否来自 error in allocation?我在 OOMK 上提供的提示有帮助吗?
【解决方案2】:

天哪,我发现了问题——指针类型出错了。

这是完整的代码

int main(int argc, char *argv[]) {

    /*Allocate 1GB memory*/
    size_t mem_size = 1024 * 1024 * 1024;
    // the problem is here, I used to use pointer as long long type
    char* based = malloc(mem_size);
    // and it misleading system to calculate incorrect offset
    if (based) {
        printf("Allocated %zu Bytes from %lx to %lx\n", mem_size, based, based + mem_size);
    } else {
        printf("Allocation Error.\n");
        return 1;
    }

    /*Initialize the memory in batches*/
    size_t stage = 65536;
    size_t initialized = 0;
    while (initialized < mem_size) {
        memset(based + initialized, '$', stage);
        initialized += stage;
    }

    /*And then I set the breakpoint, check the memory content with gdb*/
    ...
    return 0;

感谢给我建议或cmets的人:)

【讨论】:

  • 所以,让我们明确一点:在您写的问题中:void *based,但在实际代码中您实际上有long long *based,对吗?
【解决方案3】:

一个进程需要如此大的连续内存是非常不寻常的,是的,内核确实施加了这样的内存限制。您可能应该知道malloc() 在处理大于 128 Kb 的内存请求时,它在幕后调用mmap()。您应该尝试直接使用它。

您还应该知道内核在分配时的默认策略是分配比它拥有的更多的内存。 逻辑是大多数分配的内存都没有实际使用,因此允许超过系统实际内存的分配相对安全。

编辑: 正如某些人所指出的,当您的进程确实开始使用内核成功分配的内存时,它将被 OOM Killer 杀死。此代码产生了以下输出:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int arc, char**argv)
{
  char *c = malloc(sizeof(char) * 1024 * 1024 * 1024 * 5);

  if(c)
  {
    printf("allocated memory\n");
    memset(c, 1, sizeof(char) * 1024 * 1024 * 1024 * 5);  
    free(c);
  }
  else
  {
    printf("Out of memory\n");
  }
  return 0;
}

输出:

 $ ./a.out 
    allocated memory
    Killed

但是在你改变了系统的限制之后:

# echo 2 > /proc/sys/vm/overcommit_memory 
# exit
exit
$ ./a.out 
Out of memory

如您所见,系统上内存分配成功,内存使用后才出现问题 :编辑

内核对您可以分配多少内存施加了限制,您可以使用以下命令检查它们:

grep -e MemTotal -e CommitLimit -e Committed_AS /proc/meminfo
ulimit -a

第一个命令将打印总内存,第二个命令将显示内核对分配施加的限制 (CommitLimit)。该限制基于您的系统内存和系统上定义的过量使用率,您可以使用此命令cat /proc/sys/vm/overcommit_ratio 进行检查。

Committed_AS 是此时已经分配给系统的内存。您会注意到这可以超过总内存而不会导致崩溃。

您可以通过编写echo 2 &gt; /proc/sys/vm/overcommit_memory 将内核的默认行为更改为永不过度使用。您可以查看手册页以获取更多信息。

我建议检查系统的限制,然后禁用内核的默认过度使用行为。然后通过检查malloc()mmap() 分配时是否失败来尝试查看您的系统是否真的可以分配那么多内存。

来源:LSFMM: Improving the out-of-memory killerChris Simmonds

掌握嵌入式 Linux 编程

【讨论】:

  • "如果所有进程实际上都试图使用内核分配给它们的所有内存,则会产生分段错误。" - 我不相信那是真的。相反,IIRC,进程被 SIGKILL 信号杀死,内核记录一条消息(通常可以通过dmesg 命令查看)。
  • 据我所知,如果您的系统有内存泄漏并且您有内存不足的情况,就会发生这种情况,这是不同的。
  • “如果您的系统发生内存泄漏并且您遇到内存不足的情况”与尝试访问已通过过度分配分配的内存的进程完全相同。它没有不同。
猜你喜欢
  • 2015-02-11
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 2019-02-03
  • 2018-10-24
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多