当进程从Linux内核请求物理内存页时,内核会尽力提供在内存中物理连续的页面块。我想知道为什么这些页面在物理上连续是重要的;毕竟,内核可以通过简单地提供虚拟连续的页面来掩盖这一事实。
然而,内核当然最努力地提供物理上连续的页面,所以我试图弄清楚为什么物理连续性如此重要。我做了一些研究,并通过几个来源,发现了以下原因:
1)更好地利用缓存和放大器实现较低的平均内存访问时间(GigaQuantum:我不明白:怎么样?)
2)你必须摆弄内核页面表才能映射那些物理上不连续的页面(GigaQuantum:我不明白这一点:不是每个页面都是单独映射的吗?必须做些什么? ?)
3)映射不是物理上连续的页面导致更大的TLB颠簸(GigaQuantum:我不明白:怎么样?)
根据我插入的评论,我并不完全理解这三个原因。我的研究资料也没有充分解释/证明这三个原因。任何人都可以更详细地解释这些吗?
谢谢!将帮助我更好地理解内核...
答案 0 :(得分:6)
主要答案真正在于你的第二点。通常,当在内核中分配内存时,它不会在分配时映射 - 相反,内核使用简单的线性映射映射尽可能多的物理内存。在分配时,它只是为分配创造了一些内存 - 因为映射没有改变,它必须已经是连续的。
物理内存的大型线性映射是高效的:因为可以使用大页面(占用较少的页面表空间和较少的TLB条目),并且因为更改页表是一个缓慢的过程(所以你想避免在分配/解除分配时这样做。)
只能使用vmalloc()
界面而不是kmalloc()
来请求逻辑上线性的分配。
在64位系统上,内核的映射可以包含整个物理内存 - 在32位系统上(物理内存较少的系统除外),只有一部分物理内存直接映射。
答案 1 :(得分:5)
实际上,您描述的内存分配行为对于许多操作系统内核来说很常见,主要原因是内核物理页面分配器。通常,内核有一个物理页面分配器,用于为内核空间(包括DMA页面)和用户空间分配页面。在内核空间中,您需要连续内存,因为每次需要时,映射页面的代价很高(对于内核代码)。例如,在x86_64上,它完全没有价值,因为内核可以看到整个地址空间(在32位系统上有4G的虚拟地址空间限制,所以通常顶级1G专用于内核,底层3G专用于用户空间)。
Linux内核使用buddy算法进行页面分配,因此较大块的分配比较小块的分配需要更少的迭代(嗯,通过拆分更大的块获得更小的块)。此外,对内核空间和用户空间使用一个分配器允许内核减少碎片。想象一下,每次迭代为用户空间分配页面1页。如果用户空间需要N页,则进行N次迭代。如果内核想要一些连续内存会发生什么?如果从每个大块中偷走1页并将它们分配给用户空间,它如何构建足够大的连续块呢?
[更新]
实际上,内核为用户空间分配连续内存块的频率并不像您想象的那么频繁。当然,它在构建文件的ELF图像时分配它们,当它在用户进程读取文件时创建预读,它为IPC操作(管道,套接字缓冲区)创建它们或者当用户将MAP_POPULATE标志传递给mmap系统调用时。但通常内核使用“懒惰”页面加载方案。它为用户空间提供虚拟内存的连续空间(当用户第一次执行malloc或mmap时),但它不会填充物理页面的空间。它仅在发生页面错误时分配页面。当用户进程执行fork时也是如此。在这种情况下,子进程将具有“只读”地址空间。当子项修改某些数据时,会发生页面错误,并且内核会用新的页面替换子地址空间中的页面(以便父项和子项现在具有不同的页面)。通常,在这些情况下,内核只分配一个页面。
当然,内存碎片存在一个很大的问题。内核空间总是需要连续内存。如果内核会从“随机”物理位置为用户空间分配页面,那么在一段时间后(例如在一周的系统正常运行时间之后)在内核中获取大块连续内存要困难得多。在这种情况下,记忆力会过于分散。
要解决此问题,内核使用“readahead”方案。当某个进程的地址空间发生页面错误时,内核会分配并映射多个页面(因为进程可能会从下一页读取/写入数据)。当然,在这种情况下,它使用物理连续的内存块(如果可能)。只是为了减少潜在的碎片。
答案 2 :(得分:2)
我能想到的几个:
答案 3 :(得分:1)
来自内核的连续或非连续内存分配请求取决于您的应用程序。
E.g。连续内存分配:如果您需要执行DMA操作,那么您将通过kmalloc()调用请求连续内存,因为DMA操作需要一个物理上连续的内存,因为在DMA中您只提供起始地址内存块和其他设备将从该位置读取或写入。
某些操作不需要连续内存,因此您可以通过vmalloc()请求内存块,该内存块指向非传染性物理内存。
因此它完全依赖于请求内存的应用程序。
请记住,如果您要求连续内存而不是基于内核正在尝试最好地分配物理上连续的内存,那么这是一个很好的做法。好的kmalloc()和vmalloc()有他们的限制。
答案 4 :(得分:0)
放置我们将要阅读的东西在物理上非常接近,利用空间局部性,我们需要的东西更有可能被缓存。
不确定这个
我认为这意味着如果页面不连续,TLB必须做更多工作才能找到它们的所在位置。如果它们是连续的,我们可以将进程的所有页面表达为PAGES_START + PAGE_OFFSET。如果不是,我们需要为给定进程的所有页面存储单独的索引。由于TLB的大小有限,我们需要访问更多数据,这意味着我们将进行更多的交换。
答案 5 :(得分:-1)
内核不需要物理上连续的页面,实际上它只需要效率和稳定性。 单片内核往往有一个页表用于进程间共享的内核空间 并且不希望内核空间上的页面错误导致内核设计过于复杂
32位架构的通常实现总是3g / 1g分割为4g地址空间 对于1g内核空间,代码和数据的正常映射不应生成过于复杂而无法管理的递归页面错误: 你需要找到空的页面框架,在mmu上创建映射,并在每个内核页面错误上处理新的映射的tlb flush 内核已经忙于做用户端页面错误
此外,1:1线性映射可以具有更少的页表条目,因为它可以利用更大的页面单元大小(> 4kb) 较少的条目导致较少的丢失。
所以内核线性地址空间上的伙伴分配器总是提供物理上连续的页面帧 甚至大多数代码都不需要连续的帧 但许多需要连续页面框架的设备驱动程序已经相信通过通用内核分配器分配的缓冲区是物理上连续的