众所周知,Windows应用程序通常在32位系统上拥有2Gb的专用地址空间。 这个空间可以通过/ 3Gb开关扩展到3Gb。
操作系统保留其余的4Gb。
我的问题是为什么?
以内核模式(即设备驱动程序代码)运行的代码有自己的地址空间。 为什么在独占的4Gb地址空间之上,操作系统仍然要保留2Gb的每个用户模式进程?
我认为原因是用户模式和内核模式调用之间的转换。 例如,对NtWriteFile
的调用将需要一个内核调度例程的地址(因此为什么系统在每个应用程序中预留2Gb)。 但是,使用SYSENTER
,是不是足够的内核模式代码知道哪个函数/服务被调用的系统服务号?
如果您可以向我澄清,为什么操作系统在每个用户模式进程中占用2Gb(或1Gb)是如此重要。
两个不同的用户进程有不同的虚拟地址空间。 由于虚拟物理地址映射不同,当从一个用户进程切换到另一个用户进程时, TLB缓存失效。 这是非常昂贵的,因为没有已经在TLB中缓存的地址,任何存储器访问都将导致PTE的故障和散步。
系统调用涉及两个上下文切换:用户→内核,然后内核→用户。 为了加快速度,通常为内核使用最高1GB或2GB的虚拟地址空间。 由于虚拟地址空间不会跨这些上下文切换发生变化,因此不需要TLB刷新。 这由每个PTE中的用户/管理员位来启用,这确保内核内存仅在内核空间中可访问; 即使页面表相同,用户空间也无法访问。
如果有两个单独的TLB的硬件支持,一个专门用于内核使用,那么这个优化将不再有用。 但是,如果你有足够的空间来奉献,那么制作一个更大的TLB可能是更值得的。
x86上的Linux曾经支持一种称为“4G / 4G拆分”的模式。 在这种模式下,用户空间可以完全访问整个4GB虚拟地址空间,并且内核也有一个完整的4GB虚拟地址空间。 如上所述,成本是每个系统调用都需要一个TLB刷新,以及更复杂的例程来在用户和内核内存之间复制数据。 这已经被测量到施加高达30%的性能损失。
自从这个问题最初被问到和回答以来,时代已经发生了变化:64位操作系统现在更为普遍。 在x86-64的当前操作系统中,用户程序允许从0到2 47 -1(0-128TB)的虚拟地址,而内核永久驻留在从2 47 ×(2 17 -1)到2 64 -1 (或从-2 47到-1,如果你把地址当作有符号整数)。
如果在64位Windows上运行32位可执行文件,会发生什么情况? 您可能会认为从0到2 32 (0-4GB)的所有虚拟地址都可以轻松使用,但是为了避免暴露现有程序中的错误,32位可执行文件仍然被限制在0-2GB,除非它们被重新编译为/LARGEADDRESSAWARE
。 对于那些,他们可以访问0-4GB。 (这不是一个新的标志,同样适用于使用/3GB
交换机运行的32位Windows内核,它将默认的2G / 2G用户/内核分区更改为3G / 1G,虽然3-4GB仍然会出现的范围。)
什么样的错误可能会有? 作为一个例子,假设你正在实现快速排序并有两个指针, a
和b
指向数组的开始和结尾。 如果用(a+b)/2
选择中间位置作为支点,只要两个地址都小于2GB就可以工作,但是如果两者都在上面,那么加法会遇到整数溢出,结果会是阵列之外。 (正确的表达是a+(ba)/2
)
另外,具有默认的3G / 1G用户/内核拆分的32位Linux历史上运行的程序堆栈位于2-3GB范围内,所以任何这样的编程错误都可能被快速刷新。 64位Linux使32位程序可以访问0-4GB。
Raymond Chen 在这个话题上有一堆文章 。
Windows(像任何操作系统)比内核+驱动程序更多。
您的应用程序依赖于许多不仅存在于内核空间中的OS服务。 有很多缓冲区,句柄和各种可以映射到进程自己的地址空间的资源。 无论何时调用一个返回窗口句柄或画笔的Win32 API函数,这些东西都必须在你的过程中的某个地方分配。 所以Windows的一部分运行在内核中,是的,其他部分运行在他们自己的用户模式进程中,有些应用程序需要直接访问的部分映射到您的地址空间。 这部分难以避免,但是一个重要的附加因素是性能。 如果每个 Win32调用都需要上下文切换,那么这将是一个重大的性能问题。 如果它们中的一些可以在用户模式下处理,因为它们所依赖的数据已经映射到您的地址空间,则避免了上下文切换,并且可以节省相当多的CPU周期。
所以任何操作系统都需要一定数量的地址空间。 我相信Linux默认情况下只能为操作系统设置1GB。
有一次,雷蒙德陈的博客解释了微软用Windows解决2GB的原因。 我没有链接,我不记得细节,但做出的决定是因为Windows NT最初是针对Alpha处理器的,Alpha上也有一些很好的理由去做50/50分裂。 ;)
这与Alpha对32位以及64位代码的支持有关。 🙂
以内核模式(即设备驱动程序代码)运行的代码拥有自己的地址空间。
不,不是的。 它必须与x86处理器上的进程的用户模式部分共享该地址空间。 这就是为什么内核必须预留足够的空间和有限的地址空间。
我相信最好的答案是,操作系统设计师认为,当你不得不在意的时候,人们会使用64位的Windows。
但是这里有一个更好的讨论 。
部分答案是关于微处理器架构的历史。 以下是我所知道的一些,其他人可以提供更多的最新细节。
英特尔8086处理器具有内存段偏移架构,可提供20位内存地址,因此总共可寻址1MB的物理内存。
与Zilog Z80不同的竞争处理器不同,Intel 8086只有一个地址空间 ,不仅能够容纳电子存储器,还能与键盘,串行端口,打印机端口和视频显示器等外围设备进行所有输入/输出通信。 (为了便于比较,Zilog Z80具有独立的输入/输出地址空间,并带有专用的装配操作码以供访问)
为了扩大周边扩展空间,需要将地址空间分割成0-640K的电子存储空间,“其他的东西”(输入/输出,ROMS,显存等)从640K到1MB。
随着x86线路的发展和演进,以及个人电脑的发展,已经使用了类似的方案,以4G地址空间的今天的2G / 2G拆分结束。