众所周知,在 32 位 Windows 操作系统下运行的程序只有 2GB 的可用虚拟内存。众所周知,其他 2GB 被保留为内核空间。但是那个内核空间实际上是什么?
我可以理解内核本身所需的储备,但为什么进程的 VAS 中有内核空间?谢谢。
众所周知,在 32 位 Windows 操作系统下运行的程序只有 2GB 的可用虚拟内存。众所周知,其他 2GB 被保留为内核空间。但是那个内核空间实际上是什么?
我可以理解内核本身所需的储备,但为什么进程的 VAS 中有内核空间?谢谢。
获取 Windows Internals book,它详细描述了这一点。不过,对于简短的总结,内核虚拟地址 (KVA) 空间中的一些内容:
1)内核和HAL
2) 设备驱动程序
3)内核模式堆(称为执行池,我总是觉得很有趣)
4)通过句柄导出到用户态的对象(进程、线程、事件、互斥体等对象)
5) 系统 PTE,将各种有趣的东西从肮脏的用户模式应用程序中映射出来(例如,线程在内核模式下运行时使用的执行堆栈)
6) 文件系统缓存
而且这个列表还在继续……就像我说的,阅读 Windows Internals。
-斯科特
内核内存映射到每个进程的虚拟地址空间的原因是上下文切换到内核模式时不必更改进程页表。当前权限级别仅提升到0
,这会立即使这些页面可访问。
只有在切换到不同的进程时才需要切换页表。由于这是一项昂贵的操作(例如,它需要 TLB 刷新),因此将其频率降至最低是一个胜利。
此外,如果您确实切换了特殊的内核页表,则您切换到了内核空间,您必须选择用户空间地址空间的一部分进行替换。这将使内核无法访问这些用户空间地址,这将需要反弹缓冲区或更多地址空间来处理何时将这些区域中的数据传输到内核或从内核传输。
在我看来,关于 2GB 边界还有一个鲜为人知的事实。许多应用程序通常使用大量指针算法(尤其是用 C、C++ 编写的应用程序...)。在这些应用程序中,向指针添加偏移量甚至减去指针是很常见的。
如果您的可用虚拟地址空间为 2GB,则可以保证减去两个指针始终介于 -2147483647 和 +2147483648 之间(这些是 32 位有符号值的限制)。
如果您的地址空间为 3GB,则可能的差异将大于可以用 32 位有符号值表示的任何值。
如果您知道您的应用程序是安全的,并且没有减去完全不相关的指针(并且您的数组小于 2GB!),您可以通过设置链接器标志 LARGEADDRESSAWARE (或使用 EDITBIN 实用程序设置)。
使用 XP(不太确定 Vista 和 W7),您可以启动到“内核空间”只有 1GB 的模式,并为应用程序留出 3GB 的虚拟地址空间。如果您的应用程序是 LARGEADDRESSAWARE,您将获得完整的 3GB。如果没有,您只能获得 2GB。
在 64 位 Windows 上,LARGEADDRESSAWARE 32 位应用程序甚至可以获得 4GB,因为 Windows 不需要 32 位区域中的大量地址空间(毕竟,它是 64 位操作系统)。
用 C++ 等高级语言编写的程序最终被翻译为 OS API 调用,具体来说,是用于 Windows OS 的 Windows API。许多 Windows API,如 CreateFile,实际上是在与内核模式驱动程序对话。进程地址空间中的内核空间用于为该进程分配内核资源。例如,驱动程序 IOCTL 调用包含输入输出缓冲区,在用户模式 API 和驱动程序之间传递。这样的缓冲区分配在进程内核空间中。
通常,内核空间包含由内核模式组件代表该进程分配的资源。
您的问题似乎是标题中提出的“为什么”而不是“什么”。
此外,您的问题假定为 32 位。64 位版本的 Windows 完全不同(例如,VAS 为 8TB,或在安腾系统上为 7TB)。
至于“为什么”,您将如何从不包含在进程的 VAS 中的内核函数(如驱动程序)传递指针?要弄清楚这一点,一本好书是Windows Internals。