如何在C中分配大内存

时间:2017-06-01 16:25:40

标签: c linux gcc memory

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

获得内存的起始地址后,在我有限的理解中,如果我想初始化它,只需使用memset()来实现。但事实是,一段时间后会触发段错误

我尝试使用gdb查找导致它的原因,最后发现如果我执行一些内存操作超过128 MB 会导致此错误。

是否有任何规则限制程序只能访问小于128 MB的内存?或者我用错误的方式分配和初始化它?

如果需要其他信息,请告诉我。

任何建议都将受到赞赏。

[平台]

  • Linux 4.10.1 with gcc 5.4.0
  • 使用gcc test.c -o test
  • 构建程序
  • CPU:Intel i7-6700
  • RAM: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)

3 个答案:

答案 0 :(得分:2)

这里有两个可能的问题。首先,您没有正确使用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*:%lun", 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;
}

答案 1 :(得分:-1)

神圣,我发现问题 - 指针类型出错了。

这是完整的代码

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;

感谢那些给我建议或意见的人:)

答案 2 :(得分:-2)

进程需要如此大量的连续内存是非常罕见的,是的,内核确实强加了这样的内存限制。在处理大于128 Kb的内存请求时,您可能应该知道malloc()它在窗帘后面调用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 > /proc/sys/vm/overcommit_memory,您可以将内核的默认行为更改为永不过量使用。您可以查看手册页以获取有关此内容的更多信息。

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

来源:LSFMM: Improving the out-of-memory killer掌握嵌入式Linux编程 Chris Simmonds