【问题标题】:Can static memory be lazily allocated?静态内存可以延迟分配吗?
【发布时间】:2018-06-18 02:02:48
【问题描述】:

在 C 程序中有一个静态数组:

#define MAXN (1<<13)
void f() {
    static int X[MAXN];
    //...
}

Linux 内核能否选择在实际使用每个页面之前不将地址映射到物理内存?那么X怎么可能全是0,访问每一页时内存是否归零呢?这如何不影响程序的性能?

【问题讨论】:

  • 静态内存进入.data 部分,其初始内容存在于可执行文件中。加载器处理这个,而不是内核。
  • 听起来你想要的是mmap,在Linux中带有MAP_ANONYMOUS标志,它会延迟加载。它可能无法 100% 移植到各种 Unix 版本,但它基本上可以满足您的需求。请参阅this Linux memory FAQ 了解更多信息,尤其是“什么是匿名映射?”的答案,以及Linux mmap man page 或更友好的GNU Libc docs on mmap
  • @ignacio:没有初始化器的静态变量需要进行零初始化。在 Unix 系统上,这通常是通过将它们放入 .bss 段而不是数据段来完成的。 .bss 段的内容是隐含的,因此不是可执行文件的一部分。 (只有长度存在。)
  • 对,就是这个意思>_>
  • 如果您碰巧没有访问阵列的大部分,性能会受到良好的影响。

标签: c linux memory


【解决方案1】:

Linux 内核能否选择在实际使用每个页面之前不将地址映射到物理内存?

是的,它对所有内存都执行此操作(驱动程序和内核本身使用的特殊内存除外)。

那X怎么可能全是0,每页访问时内存是否归零?

你应该忽略这个细节。只要你访问的时候内存是全零的,我们就说它全是零。

这对程序的性能有何影响?

确实如此。

【讨论】:

  • “你应该忽略这个细节” 但这是问题的有趣部分,因为 C 标准规定所有具有静态存储持续时间的对象都必须在调用 main() 之前初始化。除此之外,C 没有任何限制。
  • @Lundin:两个词(或 3 个?):as-if 规则。
  • @Lundin 初始化意味着什么?
  • @immibis 在这种情况下设置为零。例如,假设我知道 .bss/.data 的整个假定内存布局,因此决定访问存储静态变量的内存区域,方法是执行类似 printf("%d", (int*)0x12345678); 的操作,其中 0x12345678 是 static int foo; 的地址。然后将发生的一切都是实现定义的,C 标准没有涵盖。但是,由于 C 标准确实保证在调用 main 之前初始化静态变量,因此假设上面的示例将打印 0 是很有意义的。
  • 所有的静态对象都会被初始化。我们知道这种情况已经发生,因为您可以随时读取它们并读取它们的初始值(或最后写入的值)。惰性内存分配实际上并没有进入它。
【解决方案2】:

Linux 内核能否选择在实际使用每个页面之前不将地址映射到物理内存?

是的,使用用户空间内存总是可以完成的。

那X怎么可能全是0,每页访问时内存是否归零?

内核维护一个全0的页面,当用户请求静态数组的新页面(静态因此在第一次使用前全为0),内核提供零页面,程序没有写入权限。写入数组会触发写时复制机制:发生页面错误,内核然后分配一个可写页面,映射它并从最后一条指令(由于页面而无法完成的指令)恢复程序过错)。请注意,预置零优化在这里改变了实现细节,但原理是一样的。

这对程序的性能有何影响?

程序不必在启动时(可能)将大量页面归零,内核实际上也不必拥有内存(只要您不这样做,就可以要求比系统获得更多的内存不要使用它)。程序执行过程中会产生页面错误,但可以将其最小化,参见mmap()madvise()MADV_SEQUENTIAL。请记住,Translation Lookaside Buffer 不是无限的,它可以维护很多条目。

来源:A linux memory FAQIntroduction to Memory Management in Linux by Alan Ott

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 2016-02-01
    相关资源
    最近更新 更多