【问题标题】:Why is the kernel concerned about issuing PHYSICALLY contiguous pages?为什么内核关心发布物理上连续的页面?
【发布时间】:2011-11-14 15:35:17
【问题描述】:

当一个进程向 Linux 内核请求物理内存页面时,内核会尽最大努力提供一个在内存中物理上连续的页面块。我想知道为什么页面物理上是连续的很重要;毕竟,内核可以通过简单地提供虚拟连续的页面来掩盖这一事实。

然而,内核确实尽最大努力提供物理上连续的页面,所以我试图弄清楚为什么物理上的连续性如此重要。我做了一些研究,并通过几个来源发现了以下原因:

1) 更好地利用缓存并实现更低的平均内存访问时间(GigaQuantum:我不明白:如何?)

2)您必须摆弄内核页表才能映射物理上不连续的页面(GigaQuantum:我不明白这一点:不是每个页面都单独映射吗?必须做些什么摆弄?)

3) 物理上不连续的映射页面会导致更大的 TLB 抖动(GigaQuantum:我不明白:如何?)

根据我插入的 cmets,我不太了解这 3 个原因。我的任何研究资料也没有充分解释/证明这三个原因。谁能更详细地解释一下这些?

谢谢!将帮助我更好地理解内核...

【问题讨论】:

  • 你使用哪个函数向内核请求物理内存页?

标签: linux-kernel


【解决方案1】:

主要答案确实在于您的第二点。通常,当内存在内核中分配时,它不会在分配时进行映射 - 相反,内核使用简单的线性映射预先映射尽可能多的物理内存。在分配时,它只是为分配分配了一些内存 - 因为映射没有改变,它必须已经是连续的。

物理内存的大型线性映射是高效的:既因为可以使用大页面(它占用更少的页表条目空间和更少的 TLB 条目),也因为更改页表是一个缓慢的过程(所以你想避免在分配/解除分配时这样做)。

只能使用vmalloc() 接口而不是kmalloc() 来请求仅逻辑线性的分配

在 64 位系统上,内核的映射可以包含整个物理内存 - 在 32 位系统上(除了那些具有少量物理内存的系统),只有一部分物理内存被直接映射。

【讨论】:

    【解决方案2】:

    实际上,您描述的内存分配行为对于许多操作系统内核来说都很常见,主要原因是内核物理页面分配器。通常,内核有一个物理页面分配器,用于为内核空间(包括用于 DMA 的页面)和用户空间分配页面。在内核空间中,您需要连续的内存,因为每次需要它们时都映射页面是昂贵的(对于内核代码)。例如,在 x86_64 上,它完全没有价值,因为内核可以看到整个地址空间(在 32 位系统上,虚拟地址空间有 4G 的限制,所以通常顶部 1G 专用于内核,底部 3G 专用于用户空间)。

    Linux 内核使用buddy 算法进行页面分配,因此分配较大块的迭代次数少于分配较小块的迭代次数(嗯,较小的块是通过拆分较大的块获得的)。此外,对内核空间和用户空间使用一个分配器允许内核减少碎片。假设您每次迭代为用户空间分配 1 页。如果用户空间需要 N 页,则进行 N 次迭代。如果内核想要一些连续的内存会发生什么?如果你从每个大块中窃取 1 页并将它们提供给用户空间,它如何构建足够大的连续块?

    [更新]

    实际上,内核为用户空间分配连续的内存块并不像您想象的那么频繁。当然,它在构建文件的 ELF 映像时分配它们,当它在用户进程读取文件时创建预读时,它为 IPC 操作(管道、套接字缓冲区)或当用户将 MAP_POPULATE 标志传递给 mmap 系统调用时创建它们。但通常内核使用“惰性”页面加载方案。它将 virtual 内存的连续空间提供给用户空间(当用户第一次执行 malloc 或执行 mmap 时),但它不会用物理页面填充空间。它仅在发生页面错误时分配页面。当用户进程执行分叉时也是如此。在这种情况下,子进程将具有“只读”地址空间。当孩子修改一些数据时,会发生页面错误,内核将子地址空间中的页面替换为新的页面(这样父子地址现在有不同的页面)。在这些情况下,通常内核只分配一页。

    当然,内存碎片是个大问题。内核空间总是需要连续的内存。如果内核会从“随机”物理位置为用户空间分配页面,那么在一段时间后(例如在系统正常运行一周之后)在内核中获得大块连续内存会更加困难。在这种情况下,内存会过于碎片化。

    为了解决这个问题,内核使用“预读”方案。当某个进程的地址空间发生缺页时,内核会分配和映射多个页面(因为进程可能会从下一页读取/写入数据)。当然,在这种情况下,它使用物理上连续的内存块(如果可能的话)。只是为了减少潜在的碎片化。

    【讨论】:

      【解决方案3】:

      我能想到的几个:

      • DMA 硬件通常根据物理地址访问内存。如果您有多个页面的数据要从硬件传输,那么您将需要一块连续的物理内存来执行此操作。一些较旧的 DMA 控制器甚至要求内存位于较低的物理地址。
      • 它允许操作系统利用大页面。一些内存管理单元允许您在页表条目中使用更大的页面大小。这允许您使用更少的页表条目(和 TLB 插槽)来访问相同数量的虚拟内存。这降低了 TLB 未命中的可能性。当然,如果要分配 4MB 的页面,则需要 4MB 的连续物理内存来支持它。
      • 内存映射 I/O。某些设备可以映射到需要跨越多个帧的连续内存范围的 I/O 范围。

      【讨论】:

        【解决方案4】:

        来自内核的连续或非连续内存分配请求取决于您的应用程序。

        例如连续内存分配:如果您需要执行 DMA 操作,那么您将通过 kmalloc() 调用请求连续内存,因为 DMA 操作需要一个物理上也是连续的内存,因为在 DMA 中您将只提供起始地址内存块和其他设备将从该位置读取或写入。

        某些操作不需要连续内存,因此您可以通过 vmalloc() 请求一个内存块,它提供指向非传染性物理内存的指针。

        所以它完全取决于请求内存的应用程序。

        请记住,如果您请求的是连续的内存,那么这是一个很好的做法,它应该基于内核正在尽力分配物理上连续的内存。好吧 kmalloc() 和 vmalloc() 有它们的也有限制。

        【讨论】:

          【解决方案5】:
          1. 将我们要经常阅读的内容放在物理上靠得很近可以利用空间局部性,我们需要的内容更有可能被缓存。

          2. 不确定这个

          3. 我相信这意味着如果页面不连续,TLB 必须做更多的工作来找出它们都在哪里。如果它们是连续的,我们可以将一个进程的所有页面表示为 PAGES_START + PAGE_OFFSET。如果不是,我们需要为给定进程的所有页面存储一个单独的索引。因为 TLB 的大小是有限的,我们需要访问更多的数据,这意味着我们将进行更多的换入和换出。

          【讨论】:

          • 利用空间局部性”你能解释一下吗?
          【解决方案6】:

          内核实际上不需要物理上连续的页面,它只需要效率和稳定性。 单片内核往往有一个页表用于在进程之间共享内核空间 并且不希望内核空间出现页面错误导致内核设计过于复杂

          所以在 32 位架构上通常的实现总是 3g/1g 拆分为 4g 地址空间 对于 1g 内核空间,代码和数据的正常映射不应生成过于复杂而无法管理的递归页面错误: 您需要找到空页框,在 mmu 上创建映射,并在每个内核端页面错误上处理 tlb 刷新以获取新映射 内核已经忙于处理用户端页面错误

          此外,1:1 线性映射可以使用更少的页表条目,因为它可以利用更大的页面单元 (>4kb) 更少的条目导致更少的 tlb 未命中。

          所以内核线性地址空间上的伙伴分配器总是提供物理上连续的页框 即使大多数代码也不需要连续的帧 但是许多需要连续页框的设备驱动程序已经相信通过通用内核分配器分配的缓冲区在物理上是连续的

          【讨论】:

            猜你喜欢
            • 2021-09-10
            • 2010-10-24
            • 2020-06-24
            • 1970-01-01
            • 2014-10-27
            • 1970-01-01
            • 2012-01-08
            • 1970-01-01
            • 2014-03-28
            相关资源
            最近更新 更多