尽管有无限的ulimit,为什么非常大的堆栈分配会失败?

时间:2013-03-16 10:59:18

标签: c linux memory-management

以下静态分配给出了分段错误

double U[100][2048][2048];

但是下面的动态分配很顺利

double ***U = (double ***)malloc(100 * sizeof(double **));

for(i=0;i<100;i++)
{
    U[i] = (double **)malloc(2048 * sizeof(double *));
    for(j=0;j<2048;j++)
    {
        U[i][j] = (double *)malloc(2048*sizeof(double));
    }
}

linux中的ulimit设置为无限制。

有人能给我一些关于发生什么的暗示吗?

4 个答案:

答案 0 :(得分:5)

当您说ulimit设置为无限制时,您使用的是-s选项吗?否则,这不会改变堆栈限制,只会改变文件大小限制。

但是,无论如何都会出现堆栈限制。我可以分配:

double *u = malloc(200*2048*2048*(sizeof(double)));  // 6gb contiguous memory

运行我得到的二进制文件:

VmData:  6553660 kB

但是,如果我在堆栈上分配,那就是:

double u[200][2048][2048];

VmStk:   2359308 kB

这显然不正确(暗示溢出)。使用原始分配,两者给出相同的结果:

Array:  VmStk:   3276820 kB
malloc: VmData:  3276860 kB

但是,运行堆栈版本,无论数组的大小如何,我都无法生成段错误 - 即使它超过系统上实际的总内存,如果设置了-s unlimited

编辑:

我在循环中使用malloc进行了测试,直到失败为止:

VmData: 137435723384 kB  // my system doesn't quite have 131068gb RAM

然而,堆栈使用量永远不会超过4gb。

答案 1 :(得分:3)

假设你的机器实际上有足够的可用内存来分配3.125 GiB的数据,差异很可能在于静态分配需要所有这些内存是连续的(它实际上是一个三维数组),而动态分配只需要大约2048 * 8 = 16 KiB的连续块(​​它是指向非常小的实际数组的指针数组的指针数组)。

当操作系统用完时,操作系统也可能使用交换文件作为堆内存,而堆栈内存则不然。

答案 2 :(得分:2)

对Linux内存管理 - 特别是堆栈 - 有一个非常好的讨论:9.7 Stack overflow,值得一读。

您可以使用此命令查找当前堆栈soft limit

的内容
ulimit -s

在Mac OS X上,硬限制为64MB,请参阅How to change the stack size using ulimit or per process on Mac OS X for a C or Ruby program?

您可以在程序中修改运行时的堆栈限制,请参阅Change stack size for a C++ application in Linux during compilation with GNU compiler

我将您的代码与示例相结合,这是一个有效的程序

#include <stdio.h>
#include <sys/resource.h>

unsigned myrand() {
    static unsigned x = 1;
    return (x = x * 1664525 + 1013904223);
}

void increase_stack( rlim_t stack_size )
{
    rlim_t MIN_STACK = 1024 * 1024;

    stack_size += MIN_STACK;

    struct rlimit rl;
    int result;

    result = getrlimit(RLIMIT_STACK, &rl);
    if (result == 0)
    {
       if (rl.rlim_cur < stack_size)
       {
           rl.rlim_cur = stack_size;
           result = setrlimit(RLIMIT_STACK, &rl);
           if (result != 0)
           {
              fprintf(stderr, "setrlimit returned result = %d\n", result);
           }
       }
   }    
}

void my_func() {
   double U[100][2048][2048];
   int i,j,k;
   for(i=0;i<100;++i)
    for(j=0;j<2048;++j)
        for(k=0;k<2048;++k)
            U[i][j][k] = myrand();
   double sum = 0;
   int n;
   for(n=0;n<1000;++n)
       sum += U[myrand()%100][myrand()%2048][myrand()%2048];
   printf("sum=%g\n",sum);
}

int main() {
   increase_stack( sizeof(double) * 100 * 2048 * 2048 );

   my_func();

   return 0;
}

答案 3 :(得分:-3)

你正在达到堆栈的限制。默认情况下,在Windows上,堆栈为1M,但如果有足够的内存,则可以增长更多。

在许多* nix系统上,默认堆栈大小为512K。

您正在尝试分配2048 * 2048 * 100 * 8字节,超过2 ^ 25(堆叠超过2G)。如果您有大量可用的虚拟内存且仍希望在堆栈上分配此内存,请在链接应用程序时使用不同的堆栈限制。

Linux中: How to increase the gcc executable stack size? Change stack size for a C++ application in Linux during compilation with GNU compiler

视窗: http://msdn.microsoft.com/en-us/library/tdkhxaks%28v=vs.110%29.aspx