Linux的用户内存上限

首先,提个问题:64bit x86 Linux中用户空间最大可以使用多少物理内存?根据Kernel的定义,这个值是64T,Redhat官方给的是12T,但含糊的说了64T的限制。那64bit x86的寻址位宽是多少?答:48bit,理论上应该是支持2<<48=256T才是,Linux kernel为什么会强制限定了一个64T的最大内存?

避免复杂化,本文一律不谈大页内存;一律只针对当下主流48bit内存空间和IA32e地址扩展的x86_64 CPU。

还是先从逼死强迫症的48bit内存地址位宽说起吧。按道理讲,64bit的CPU,内存地址就应该是64bit/8byte。但这48bit地址是一个x86平台上很有名的历史包袱之一,其中的0~47位为物理内存地址,48~63这16位必须是与第47位保持一致。在编程的层面上表现为所有的内存指针的前16位都是0。X86_64本身并不是一个完全意义上的64位地址空间,考虑向下兼容,内存采用了IA32的扩展方式IA32e,这诡异的设定源自于IA32的多级页表。

先开始讲页表跳转。Intel SDM vol3 figure 4-8 给了一张图。 (4-9,4-10分别是3层转换的2M大页和两层转换的1G大页)

当前内存的管理是通过页表转换的模式实现的,64bit系统当前是4级(跳)页表。48bit带宽的前36bit分为4段,每段9bit页表,后12bit对应的正是一个内存页(即4K)大小。

又一个逼死强迫症的8bit+1bit,又是32bit时代的遗传病。这9bit类似于指针操作,不断地指向的下一级内存地址。亲身经历,为了调试一个底层的bug,当时一个会议室15个人的组了hackthon,在完全解读了转换算法的情况下,一个小时里愣是没有一个人通过人肉的计算成功的完成地址页表转换,可见算法之反人类。

摘自《Understanding the Linux Kernel, 3rd edition》Daniel P. Bovet, Marco Cesati

几十年前比尔盖兹大叔说过一句:“PC只要640K的内存就够了!”成为了他一生的黑点。当时的8088CPU只支持20bit的寻址0xFFFFF,正巧是一个8位和一个12位的组合。这48bit的强迫症种子从这个时候就埋下了。

等一下!20bit不明明是1M吗,为什是640K内存?——2位保留地址!8088将20bit中的两个bit作为保留地址,当时用来映射BIOS和外设。当时这两个bit带来的仅仅只是被这384K的划分的不连续空间,当这种历史包袱遗传到了现在的48bit,保留的地址空间范围一样存在。同样,48bit空间也要满足“两头顶格”的习惯,整个可用地址范围变成了0~0x7FFF FFFF FFFF和0x8000 0000 0000~0xFFFF FFFF FFFF两个不连续的地址空间上的的几个更加离散的小岛。以首位区分或者理解为正负符号,Linux Kernel使用“1”作为系统地址空间,使用“0”作为用户地址空间(小于47bit可分配给用户空间)。贴图来自SDM vol3 fuigure 3-3局部,实模式即8088模式也可以在此找到。

另一方面就是Linux kernel是用移位的方式确定用户可用内存地址空间范围,相比x86,Linux表现出了必须是2的n次方强迫症逻辑——既然已经注定凑不齐47bit(128T),甚至目前连64T内存的主机都没有,keep easy!那就索性再扣除一个bit,46bit = 64T。 mm.txt如是说:

Current X86-64 implementations support up to 46 bits of address space (64 TB), which is our current limit. This expands into MBZ space in the page tables.

–Andi Kleen(Intel.)

眼看着64T内存的主机也越来越近了,再加一级页表,48bit变57bit是一种最偷懒的办法(0b111001,比48还迫害强迫症患者),但如果考虑到云计算的普及,虚拟主机的应用越来越多的成为了主流,页表转换到了VM场景下还要增加额外的虚拟页表跳转,更多的开销已经无法接受了。看来日后只有大页才是趋势。

“向下兼容”这四个字让x86这个平台充斥着各种不可理解;Linux,每一行代码都有一个故事……

推荐阅读:
经常会通过一些通用的测试工具测
接到一个黑盒的case:一套双
去年的DCDC,我主要介绍了基

发表评论

电子邮件地址不会被公开。 必填项已用*标注

请补全下列算式: *

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据