19 memory windows virtual virtual-address-space memory-address
众所周知,Windows应用程序通常在32位系统上具有2Gb的专用地址空间.使用/ 3Gb开关可将此空间扩展至3Gb.
操作系统保留了4Gb的剩余部分.
我的问题是为什么?
在内核模式下运行的代码(即设备驱动程序代码)具有自己的地址空间.为什么在独有的4Gb地址空间之上,操作系统仍然希望保留每个用户模式进程的2Gb?
我认为原因是用户模式和内核模式调用之间的转换.例如,调用NtWriteFile将需要内核调度例程的地址(因此系统为什么在每个应用程序中保留2Gb).但是,使用SYSENTER,是不是系统服务号足以让内核模式代码知道正在调用哪个函数/服务?
如果你能向我澄清为什么操作系统对每个用户模式进程采用2Gb(或1Gb)非常重要.
eph*_*ent 22
两个不同的用户进程具有不同的虚拟地址空间 由于虚拟物理地址映射不同,因此在将上下文从一个用户进程切换到另一个用户进程时,TLB高速缓存无效.这是非常昂贵的,因为没有已经缓存在TLB中的地址,任何内存访问都将导致故障和PTE的步行.
Syscalls涉及两个上下文切换:用户→内核,然后是内核→用户.为了加快速度,通常会为内核使用保留最高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+(b-a)/2.)
另外,32位Linux具有默认的3G/1G用户/内核分割,历史上运行的程序堆栈位于2-3GB范围内,因此任何此类编程错误都可能很快被刷新.64位Linux使32位程序可以访问0-4GB.
| 归档时间: |
|
| 查看次数: |
4108 次 |
| 最近记录: |