【发布时间】:2018-04-01 13:01:59
【问题描述】:
OSDev wiki 说:
将内核映射到每个用户进程中是传统的并且通常很好
为什么会这样?进程不能单独映射到内存吗?映射内核有什么好处,不会浪费空间吗?
另外,是否可以从用户空间访问内核空间,我为什么要这样做?
【问题讨论】:
标签: assembly memory-management linux-kernel x86 osdev
OSDev wiki 说:
将内核映射到每个用户进程中是传统的并且通常很好
为什么会这样?进程不能单独映射到内存吗?映射内核有什么好处,不会浪费空间吗?
另外,是否可以从用户空间访问内核空间,我为什么要这样做?
【问题讨论】:
标签: assembly memory-management linux-kernel x86 osdev
将内核映射到每个用户进程中是传统的并且通常很好
因此,当您进行系统调用时,内核不必更改页表来访问它自己的内存。例如,始终映射所有物理内存使得read 系统调用从页面缓存中的任何位置复制内容变得更便宜。
GDT 和 IDT 基地址是虚拟的 (lidt / lgdt),因此中断处理要求至少包含 IDT 的页面和中断-它指向的处理程序代码在用户空间执行时被映射。
但为了缓解 Intel CPU 上的 Meltdown user-space speculative reads can bypass the user/supervisor page-table permission bit,Linux 实际上会在用户空间执行时取消映射大部分内核。在跳转到常规入口点之前,它需要保留一个“蹦床”映射,用于交换页表以正确重新映射内核,以便中断处理程序和系统调用可以工作。
是否可以从用户空间访问内核空间,我为什么要这样做?
通常内核会禁用它。页表条目有一个user/supervisor bit,它控制它是否可以在不在内核模式下使用(即我认为是ring 3)。内核因此可以保留其内存映射,同时仍然保护它不被用户空间读/写。 (另见this for a diagram of nesting of page directories。)
CPU 具有支持此用例的性能特性:每个 PTE 中都有一个“全局”位(如果设置)意味着 CPU 可以将其缓存在 TLB 中,即使 CR3 更改(即跨上下文切换,当内核安装一个新的页表)。内核为它包含在每个进程中的内核映射设置这个。
顺便说一句,这些内核映射可能只有一个表的物理副本,每个不同的用户空间页表树的顶级页面映射级别 4 表 (PML4) 只是指向同一个内核 PDPTE结构(大多数/全部实际上是 1GiB 的大页面映射,而不是指向更多条目级别的指针)。请参阅上面链接的图表。
实际上内核允许用户空间读取(和执行)少量内存:内核将几个 4k 页 called the VDSO area 映射到每个进程的地址空间(在最顶部虚拟内存)。
对于一些简单但常见的系统调用,如gettimeofday() 和getpid(),用户空间可以在这些页面中使用call 函数(例如运行rdtsc 并通过内核导出的常量缩放结果)而不是使用syscall 进入内核模式并在那里做同样的事情。这可以为现代 x86 CPU 上的内核模式往返节省 50 到 100 个时钟周期,并且在调度到正确的系统调用之前不需要内核内的所有内容的所有保存/恢复。
是否可以在不映射内核的情况下将进程映射到内存中?
在 64 位内核上使用 32 位进程,整个 4GiB 虚拟地址空间可供用户空间使用。(3 左右的 4k VDSO 页面除外。)
否则(当用户空间虚拟地址与内核空间虚拟地址一样宽时)Linux 使用上半部分进行所有物理内存的内核映射(在 x86 上具有 1G 巨页)。
i386 Linux 有一个配置选项可以进行 1:3 拆分,IIRC,进一步限制了内核,但允许更多的虚拟地址空间用于用户空间进程。 IDK 如果这对于其他架构上的 32 位内核很常见,或者仅适用于 x86。
那不是浪费空间吗?
它占用了一些虚拟地址空间,但你应该拥有比物理内存更多的空间。如果你不这样做,你就不得不付出更频繁地重新映射内存的速度成本。
这就是为什么我们有 x86-64,所以虚拟地址空间是巨大的。 48 位是 256 TiB,所以其中一半是 128 TiB 的地址空间。如果有必要/有用,未来的 CPU 可以实现对更广泛虚拟地址的硬件支持。 (The page table format supports up to 52 bit physical addresses.)。对于提供比 DRAM 更高密度的内存映射存储的非易失性 DIMM,这可能会成为一个更大的问题,并成为大量使用这两种地址空间的原因。
如果您在单个进程中需要超过 2GiB 的虚拟地址空间,请使用 64 位系统。 (或者,如果您需要大量进程/线程,请至少使用 64 位内核。带有 PAE 的 32 位内核有时会遇到内存分配问题。请参阅一些https://serverfault.com/ 问题。)
某人reposted on their blog some of Linus Torvalds' comments about PAE (Physical Address Extensions) 允许在仅 32 位 x86 系统上拥有超过 4GB 的物理内存。总结:哎呀,即使有一个好的内核端实现,它肯定比 64 位内核慢。除了对英特尔工程师的更有趣的侮辱,他们认为这是一个好主意,可以解决 32 位操作系统的问题。
【讨论】: