malloc 和 BSS 之间有任何限制或位置差异吗?

Any limitations or location differences between malloc and BSS?

(为了问题,请忽略全局变量的pros/cons)

全局变量是否与动态分配的内存位于同一内存中space?

例如,通常情况下,如果我有一个数兆字节的大数据结构,我希望在内存中可用,我 malloc() 我需要的数量并从那里使用结构指针。

我的问题是,这与将大型结构定义为全局结构之间在内存方面有什么不同吗?我知道未初始化的全局变量在对象 BSS 中定义了它们的大小,但实际上不会占用目标文件中的 space,所以从某种意义上说,它们是在运行时分配的。但是,按照 BSS 的定义,全局变量的大小是否有任何限制?它们是否以 malloc 以外的方式分配?

此答案适用于

静态对象没有分配 malloc(当然,您不能尝试 free)。当 execed 二进制文件以及程序的其余代码和数据时,内核会为它们分配内存。这通常称为“加载时间”而不是“运行 时间”,因为它发生在您的任何程序代码实际执行之前。唯一的区别是分配给 .bss 的内存没有填充从二进制文件加载的数据。与程序的其余部分一样,它通常是按需分页的,因此(清零的)物理内存只会在实际读取或写入 space 时分配,并且稍后可能会交换到磁盘。它或多或少等同于匿名 mmap.

(我在这里使用“静态”这个词是指存储持续时间,而不是作用域,它同样适用于全局变量以及 C 中的 static 变量。两者之间的内存放置存在一些差异这两个用于与位置无关的代码,我不会深入讨论,但它不会实质性地改变下面的信息。)

我假设您知道 malloc 通常是如何工作的,以及它如何从 OS 获取内存(通过 sbrk 或匿名 mmap,通常是后者对于大物体),所以我不会描述它。

您可能会遇到的主要区别是,在默认编译器设置下,静态数据(包括 bss)被限制为大约 2GB。 Linux 编译器工具链默认使用 "small" code model;所有代码和静态数据都使用 32 位带符号的 RIP 相对位移进行访问,因此任何两个这样的地址必须在 2 GB 以内,因此实际上该限制适用于所有代码和静态数据的总大小。如果超过此限制,您的程序将无法编译或 link。 (事实上​​,在某些测试中,如果您有幸将它放在数据的末尾,可能会有一个比这更大的对象,但这不太可能在实际程序中解决。)

您可以通过在编译时选择“中型”或“大型”代码模型来避免此限制,以便使用 64 位绝对地址,但代价是它们会导致更大且效率更低的代码,可能用于您的整个程序。因此,除非您有非常特殊的原因想要静态分配您的大对象,否则最好动态分配它。

对于非常大的分配,如果可移植性不是必需的,您可能更愿意直接使用匿名 mmap,而不是 malloc。这使您可以更好地控制何时 return 内存到 OS(使用 munmap),还可以让您利用 mprotect、[=23= 等功能]、huge pages等,用于更细粒度地控制MMU和分页机制。

然而,总的来说,记忆就是记忆,不管它是如何获得的。

Do global variables live in the same memory space as dynamically allocated memory?

是的,在大多数现代平台中,进程将内存视为单个平面内存 space(但它们通常无法访问所有实际内存;它是虚拟内存 space)。

I know uninitialized globals have their size defined in the object BSS but won't actually occupy space in the object file, so in a sense they are being allocated at runtime.

一切都在运行时“分配”,而不仅仅是 BSS 区域。程序不携带分配的内存,而是使用一种文件格式指定内存需要如何布局(包括内容、页面标志、对齐方式、位置、内容...),以及其他细节。

操作系统在加载程序时获取该信息并创建满足这些需求的进程,然后启动程序。

But is there any limit to the size of globals, as defined by BSS?

这取决于平台,但在大多数情况下您不会注意到限制,除非您想要请求不寻常的尺寸。有关 Linux x86_64.

的非常好的概述,请参阅 Nate 的回答

Are they being allocated in a way other than malloc?

操作系统使用程序中有关 BSS 的信息来映射一些填充零的内存,如上所述,甚至在程序启动之前 运行。

另一方面,malloc() 是一个标准的 C 库调用,它使用操作系统提供的任何功能来动态请求内存。也就是说,当程序已经是运行.

但两者最终都在程序所看到的相同内存中 space(在大多数现代平台中)。