是否可以在不映射内核的情况下将进程映射到内存中



OSDev wiki说:

在每个用户进程中映射内核是传统的,通常是好的

为什么会这样?进程不能单独映射到内存吗?映射内核的优点是什么?这不是在浪费空间吗?

此外,是否可以从用户空间访问内核空间?我为什么要这样做?

在每个用户进程中映射内核是传统的,而且通常很好

因此,当您进行系统调用时,内核不必更改页表来访问它自己的内存。例如,一直映射所有物理内存可以使read系统调用从页面缓存中的任何位置复制内容变得更便宜。

GDT和IDT基地址是虚拟的(lidt/lgdt),因此中断处理要求在执行用户空间时,至少映射包含IDT的页面及其指向的中断处理程序代码。

但是,作为英特尔CPU上Meltdown的缓解措施,用户空间推测读取可以绕过用户/主管页表权限位,Linux实际上在用户空间执行时取消映射内核的大部分。在跳转到常规入口点之前,它需要保持一个"蹦床"映射,交换页面表以重新映射内核,这样中断处理程序和系统调用就可以工作了。

是否可以从用户空间访问内核空间,我为什么要这样做?

通常内核会禁用此功能。页面表条目有一个用户/主管位,它控制在不处于内核模式时是否可以使用它(我认为是环3)。因此,内核可以保留其内存映射,同时仍然保护其不受用户空间的读/写影响。(有关页面目录嵌套的图表,请参见此。)

CPU有一个性能特性来支持这种用例:每个PTE中都有一个"全局"位(如果设置),这意味着即使CR3发生变化(即,当内核安装新的页表时,跨上下文切换),CPU也可以将其缓存在TLB中。内核为它包含在每个进程中的内核映射设置了这一点。

顺便说一句,这些内核映射的表可能只有一个物理副本,每个不同的用户空间页面表树的顶级页面映射级别4表(PML4)只是指向相同的内核PDPTE结构(大多数/所有这些实际上都是1GiB的hugepage映射,而不是指向其他级别条目的指针)。请参阅上面链接的图表。


实际上,内核允许用户空间读取(和执行)少量内存:内核将称为VDSO区域的几个4k页面映射到每个进程的地址空间(位于虚拟内存的最顶部)

对于一些简单但常见的系统调用,如gettimeofday()getpid(),用户空间可以在这些页面中使用call函数(例如,运行rdtsc并通过内核导出的常量缩放结果),而不是使用syscall进入内核模式并在那里做同样的事情。这可能为现代x86 CPU上的往返内核模式节省了50到100个时钟周期,而且在调度到正确的系统调用之前,不需要在内核内保存/恢复所有内容,从而节省了更多的时钟周期。


是否可以在不映射内核的情况下将进程映射到内存中?

使用64位内核上的32位进程,整个4GiB虚拟地址空间可用于用户空间(大约3个4k VDSO页面除外。)

否则(当用户空间虚拟地址与内核空间虚拟地址一样宽时),Linux使用上半部分来进行所有物理内存的内核映射(x86上有1G的大容量)。

i386Linux有一个配置选项,使拆分为1:3,IIRC,进一步限制了内核,但为用户空间进程提供了更多的虚拟地址空间。IDK,如果这在其他体系结构上的32位内核中很常见,或者只有x86。

这不是在浪费空间吗?

它占用了一些虚拟地址空间,但应该比物理内存占用更多。如果你不这样做,你就必须支付更频繁地重新映射内存的速度成本。

这就是为什么我们有x86-64,所以虚拟地址空间巨大。48位是256TiB,所以其中一半是128TiB的地址空间。如果必要/有用,未来的CPU可以实现对更广泛虚拟地址的硬件支持。(页表格式最多支持52位物理地址。)。非易失性DIMM提供的内存映射存储密度高于DRAM,这可能会成为一个更大的问题,也是使用大量这两种地址空间的原因。

如果您在单个进程中需要超过2GiB的虚拟地址空间,请使用64位系统。(或者,如果您需要无数个进程/线程,请至少使用64位内核。带有PAE的32位内核有时会遇到内存分配问题https://serverfault.com/问题。)

有人在他们的博客上转发了Linus Torvalds对PAE(物理地址扩展)的一些评论,PAE允许在32位的x86系统上拥有超过4GB的物理内存。总结:是的,即使有一个好的内核端实现,它也肯定比64位内核慢。除了对英特尔工程师更有趣的侮辱,他们认为这是一个好主意,可以解决32位操作系统的问题。

最新更新