两个不同的用户进程具有不同的虚拟地址空间。由于虚拟↔物理地址映射不同,TLB http://en.wikipedia.org/wiki/Translation_lookaside_buffer当上下文从一个用户进程切换到另一个用户进程时,缓存会失效。这是非常昂贵的,因为如果没有缓存在 TLB 中的地址,任何内存访问都将导致错误和遍历PTE http://en.wikipedia.org/wiki/Page_tables.
系统调用涉及两个上下文切换:用户→内核,然后内核→用户。为了加快速度,通常会保留顶部 1GB 或 2GB 的虚拟地址空间供内核使用。由于虚拟地址空间在这些上下文切换中不会发生变化,因此不需要 TLB 刷新。这是由每个 PTE 中的用户/管理员位启用的,这确保内核内存只能在内核空间中访问;即使页表相同,用户空间也无权访问。
如果硬件支持两个独立的 TLB,其中一个专门供内核使用,那么这种优化将不再有用。但是,如果您有足够的专用空间,那么制作一个更大的 TLB 可能更值得。
Linux on x86 曾经支持一种称为“4G/4G split”的模式。在这种模式下,用户空间可以完全访问整个4GB虚拟地址空间,内核也拥有完整的4GB虚拟地址空间。如上所述,成本是every系统调用需要 TLB 刷新,以及更复杂的例程来在用户内存和内核内存之间复制数据。经测量,这会造成高达 30% 的性能损失。
Times have changed since this question was originally asked and answered: 64-bit operating systems are now much more prevalent. In current OSes on x86-64, virtual addresses from 0 to 247-1 (0-128TB) are allowed for user programs while the kernel permanently resides within virtual addresses from 247×(217-1) to 264-1 (or from -247 to -1, if you treat addresses as signed integers).
What happens if you run a 32-bit executable on 64-bit Windows? You would think that all virtual addresses from 0 to 232 (0-4GB) would easily be available, but in order to avoid exposing bugs in existing programs, 32-bit executables are still limited to 0-2GB unless they are recompiled with /LARGEADDRESSAWARE
. For those that are, they get access to 0-4GB. (This is not a new flag; the same applied in 32-bit Windows kernels running with the /3GB
switch, which changed the default 2G/2G user/kernel split to 3G/1G, although of course 3-4GB would still be out of range.)
可能存在哪些类型的错误?举个例子,假设您正在实现快速排序并有两个指针,a
and b
指向数组的开头和结尾。如果您选择中间作为枢轴(a+b)/2
,只要两个地址都在2GB以下就可以,但如果都在2GB以上,那么加法会遇到整数溢出,结果会超出数组。 (正确的表达是a+(b-a)/2
.)
顺便说一句,32 位 Linux 具有默认的 3G/1G 用户/内核分割,历史上运行的程序的堆栈位于 2-3GB 范围内,因此任何此类编程错误都可能很快被清除。 64 位 Linux 允许 32 位程序访问 0-4GB。