C语言实验-为什么我不能分配和使用2GB的内存?

wnavrhmk  于 2023-01-04  发布在  其他
关注(0)|答案(3)|浏览(183)

我继续用C做实验。我有这个程序,可以让你决定你想吃多少RAM。

char * eatRAM()
{
    unsigned long long toEat;
    unsigned long long i = 0;
    float input;
    char * pMemory = NULL;
    int megaByte = 1048576;

    puts("How much RAM do you want to eat? (in Mega Bytes)");
    puts("NOTE: If you want to eat more RAM than you have available\nin your system, the program will crash");
    printf("\n>> MB: ");
    scanf("%f", &input);

    toEat = (unsigned long long)(input * megaByte);
    pMemory = malloc(toEat);

    printf("\n\nEating in total: %llu Bytes\n", toEat);
    puts("Check your task manager!\n");

    if(pMemory != NULL)
    {
        printf("\n\nEating in total: %llu Bytes\n", toEat);
        puts("Check your task manager!\n");

        for(i; i < toEat; i++)
        {
            pMemory[i] = 'x';
        }
    }
    else
    {
        puts("\nSeems like that amount of memory couldn't be allocated :( \n");
    }
    return pMemory;
}

更新问题:
问题是...如果我输入例如1024MB它工作,我可以在任务管理器中看到它正在使用1GB的RAM。即使我输入1500MB它也工作。
但如果我输入2048MB,它会显示
似乎无法分配该内存量:(
甚至输入1756MB
请记住,我是C新手,也许我遗漏了一些重要的东西,与操作系统如何允许我访问内存有关,它可能是什么?

x8goxv8g

x8goxv8g1#

默认情况下,Windows上的32位进程有2GB的地址空间。整个pow(2,32)地址空间的下半部分,即最上面的2GB由操作系统使用。由于几乎没有人再使用32位操作系统,所以当您将程序与/LARGEADDRESSAWARE链接时,可以获得4GB。
这2 GB的虚拟机空间需要由代码和数据共享。你的程序通常在0x 00400000加载,你使用的任何操作系统DLL(如kernel32.dll和ntdll.dll)都有高负载地址(超过0x 7 F000000)。至少启动线程的堆栈和默认进程堆是在程序开始运行之前创建的,它们的地址通常是不可预测的。
你的程序在大多数操作系统安装中都会受到收缩 Package 的病毒攻击,你会被注入提供反恶意软件和云存储等“服务”的DLL。这些DLL的加载地址是不可预测的。还有你与自己链接的任何DLL,当你的程序启动时都会被隐式加载。很少有程序员会注意到他们首选的基址,并将其保留为默认值。0x 1000000。您可以从调试器的“模块”窗口中看到这些DLL。此类DLL通常有自己的CRT,并倾向于创建自己的堆。
您自己进行的分配,特别是那些不是来自低碎片堆的非常大的分配,需要在现有代码和数据分配之间留下的漏洞中找到地址空间。如果您得到1500 MB,则您的VM相当干净。一般来说,超过650 MB,您将遇到麻烦。当程序运行一段时间后,虚拟机空间变得碎片化,分配失败几乎总是因为操作系统找不到足够大的漏洞而导致的,这并不是因为你没有足够的虚拟机了。漏洞的总和可能比你失败的分配请求要大得多。
这些细节正在迅速成为民间传说,几乎没有理由仍然以x86为目标。在未来20年内,以x64为目标和地址空间碎片将不会成为问题,很难对8 TB的虚拟机进行碎片化。有很大的空间可以扩展。
所以你不能得到2048 MB,你不能得到所有的,这应该是显而易见的。从SysInternals的VMMap utility中获得进一步的见解,它向你展示了虚拟机是如何划分的。Mark Russinovich的blog post和书给予很多背景知识。

qvtsj1bj

qvtsj1bj2#

这是一个OS限制,而不是C限制。
要处理超过4Gb的系统范围,您需要运行64位操作系统,而要处理超过4Gb的单个进程,必须将其构建为64位应用程序。Win32的每个进程内存限制为2Gb。5Gb的物理RAM在很大程度上是无关紧要的,因为内存是虚拟化的。
除了32位和64位系统和应用程序的理论限制外,操作系统仍然可能施加限制。例如,Windows的不同版本(Home、Pro、Server等)出于商业原因施加特定限制。
在您的情况下,具体的答案需要关于您的系统、工具链和构建选项的信息。如果您使用Windows和VC++,则需要考虑/LARGEADDRESAWARE选项;在32位编译器中,默认情况下不启用physical address extension,但除非启用physical address extension,否则Win32在任何情况下都有2Gb的默认限制。
我相信在Win 64上运行的32位进程可以寻址全部4Gb 32位地址空间,但在这种情况下,您肯定需要使用/LARGEADDRESAWARE进行构建。即使这样,也不是所有的空间都可用于堆,并且任何单个分配都必须是连续的,因此可能会受到以前分配和堆碎片的限制。

vktxenjb

vktxenjb3#

如果剩余的可用内存量小于您尝试分配的内存量,则分配将永远不会起作用。
另外,在这一行之后:

pMemory = (char *) malloc(toEat);

增加以下内容:

if (!pMemory){
  printf("Can't allocate memory\n");
  return NULL;
}

这样,您将看到一条"无法分配内存"消息,而不是接收"分段错误"相关消息,并且您的函数将返回NULL。
确保在调用eatRam函数的函数中执行类似的值检查,否则您将收到"分段错误"消息。此外,使用gdb之类的调试器。

相关问题