linux内核如何管理不到1GB的物理内存?

Posted

技术标签:

【中文标题】linux内核如何管理不到1GB的物理内存?【英文标题】:How does the linux kernel manage less than 1GB physical memory? 【发布时间】:2011-05-30 12:55:05 【问题描述】:

我正在学习 linux 内核内部结构,在阅读“理解 Linux 内核”时,我遇到了很多与内存相关的问题。其中之一是,如果我的系统上安装了 512 MB 的物理内存,Linux 内核如何处理内存映射。

据我所知,内核将 0(或 16)MB-896MB 物理 RAM 映射到 0xC0000000 线性地址并可以直接寻址。所以,在上述我只有 512 MB 的情况下:

内核如何从 512 MB 映射到 896 MB?在所描述的方案中,内核进行了设置,以便每个进程的页表将虚拟地址从 0xC0000000 直接映射到 0xFFFFFFFF (1GB) 到物理地址从 0x00000000 到 0x3FFFFFFF (1GB)。但是,当我只有 512 MB 物理 RAM 时,如何将虚拟地址从 0xC0000000-0xFFFFFFFF 映射到物理 0x00000000-0x3FFFFFFF ?重点是我的物理范围只有 0x00000000-0x20000000。

在这种情况下如何处理用户模式进程?

每篇文章都只解释这种情况,当您安装了 4 GB 内存并且内核将 1 GB 映射到内核空间并且用户进程使用剩余的 RAM 量时。

如果能帮助我加深理解,我将不胜感激。

谢谢..!

【问题讨论】:

这对 unix.stackexchange.com 来说是个好问题 虚拟地址内核内存映射到不一定是0xC0000000。 0xC0000000 是 x86 的默认地址 osgx,感谢您的 cmets。绝对有用。需要理解您提供的指针。如果有问题,会回来。非常感谢。 TheLoneJoker,我还可以向您推荐 Robert Love 的《Linux 内核开发》一书。它由文本组成,而不是由代码和 cmets 组成,就像 ULK 一样 【参考方案1】:

并非所有虚拟(线性)地址都必须映射到任何东西。如果代码访问未映射的页面,则会引发页面错误。

物理页面可以同时映射到多个虚拟地址。

在 4 GB 虚拟内存中有 2 个部分:0x0... 0xbfffffff - 是进程虚拟内存,0xc0000000 .. 0xffffffff 是内核虚拟内存。

内核如何从 512 MB 映射到 896 MB?

它映射到 896 MB。因此,如果您只有 512 个,则映射的只有 512 MB。

如果您的物理内存位于 0x00000000 到 0x20000000 中,它将被映射以供内核直接访问虚拟地址 0xC0000000 到 0xE0000000(线性映射)。

在这种情况下用户模式进程如何?

用户进程的物理内存将被映射(不是顺序的,而是随机的页到页映射)到虚拟地址 0x0 .... 0xc0000000。此映射将是 0..896MB 页面的第二个映射。这些页面将从免费页面列表中获取。

物理 RAM 中的用户模式进程在哪里?

任何地方。

每篇文章都只解释了当您安装了 4 GB 内存和的情况

没有。每篇文章都解释了如何映射 4 Gb 的虚拟地址空间。虚拟内存的大小始终为 4 GB(对于没有内存扩展的 32 位机器,如 x86 的 PAE/PSE/etc)

正如 Robert Love 的书 Linux Kernel Development(我使用第三版)的 8.1.3. Memory Zones 中所述,物理内存有几个区域:

ZONE_DMA - 包含小于 16 MB 的内存页帧 ZONE_NORMAL - 包含大于等于 16 MB 小于 896 MB 的内存页框 ZONE_HIGHMEM - 包含 896 MB 及以上的内存页框

因此,如果您有 512 MB,您的 ZONE_HIGHMEM 将为空,而 ZONE_NORMAL 将映射 496 MB 的物理内存。

另外,请查看本书的2.5.5.2. Final kernel Page Table when RAM size is less than 896 MB 部分。这是关于内存小于 896 MB 的情况。

另外,对于 ARM,还有一些关于虚拟内存布局的描述:http://www.mjmwired.net/kernel/Documentation/arm/memory.txt

第63行PAGE_OFFSET high_memory-1是内存的直接映射部分

【讨论】:

osgx,根据您提到的内容,“所以,如果您有 512 MB,您的 ZONE_NORMAL 将映射 496 MB 的物理内存。”如果是这种情况,则物理内存中将不会有任何用户空间页面的位置。除非,可以换出 ZONE_NORMAL 页面?期待你的想法。 @TheLoneJoker,内核可以为用户空间进程分配 phys 页面。区域 ZONE_NORMAL 和 ZONE_HIGHMEM。 (例如linux-mm.org/HighMemory 2G 系统:“内核从两个区域分配进程和页面缓存内存很重要”)如果 ZONE_HIGHMEM 为空,内核将从 ZONE_NORMAL 分配页面。 @TheLoneJoker,交换是虚拟页面上的一个过程,或者,您可以将其命名为“从物理页面取消映射虚拟页面”。因此,虚拟地址空间中的任何用户页面都可以取消映射并换出。 @TheLoneJoker,HIGHMEM 只是内核的问题,对于用户进程,highmem 和法线映射页面之间没有区别。【参考方案2】:

硬件提供Memory Management Unit。它是一个能够拦截和更改任何内存访问的电路。每当处理器访问 RAM 时,例如要读取下一条要执行的指令,或者作为由指令触发的数据访问,它会在某个 address 处执行此操作,大致来说,这是一个 32 位值。一个 32 位字可以有超过 40 亿个不同的值,所以有一个 4 GB 的地址空间:这是可以有一个唯一地址的字节数。

因此处理器将请求发送到其内存子系统,如“获取地址 x 处的字节并将其返回给我”。请求通过 MMU,MMU 决定如何处理请求。 MMU 将 4 GB 空间虚拟分割成 ;页面大小取决于您使用的硬件,但典型大小为 4 和 8 kB。 MMU 使用表格告诉它如何处理每个页面的访问:要么授予访问重写地址(页面条目说:“是的,包含地址 x 的页面存在,它是在物理 RAM 地址 y") 或被拒绝,此时内核被调用以进一步处理事情。内核可能决定杀死有问题的进程,或者做一些工作并改变 MMU 表,以便可以再次尝试访问,这次成功。

这是虚拟内存的基础:从这个角度来看,进程有一些RAM,但是内核已经把它移到了硬盘,在“交换空间”中。相应的表在 MMU 表中被标记为“不存在”。当进程访问他的数据时,MMU 调用内核,内核从交换中获取数据,将其放回物理 RAM 中的某个空闲空间,并更改 MMU 表以指向该空间。然后内核跳回到进程代码,就在触发整个事情的指令处。进程代码看不到整个业务,除了内存访问花费了相当长的时间。

MMU 还处理访问权限,防止进程读取或写入属于其他进程或内核的数据。每个进程都有自己的一组 MMU 表,内核管理这些表。因此,每个进程都有自己的地址空间,就好像它独自在一台有 4 GB RAM 的机器上一样——除了该进程最好不要访问它没有从内核正确分配的内存,因为相应的页面被标记缺席或禁止。

当某个进程通过系统调用调用内核时,内核代码必须在该进程的地址空间内运行;因此内核代码必须位于每个进程的地址空间中的某个位置(但受到保护:MMU 表阻止非特权用户代码访问内核内存)。由于代码可以包含硬编码地址,内核最好在所有进程的相同地址;通常,在 Linux 中,该地址是 0xC0000000。每个进程的 MMU 表将地址空间的一部分映射到内核在启动时实际加载的任何 物理 RAM 块。请注意,内核内存永远不会被换出(如果可以从交换空间读回数据的代码本身被换出,事情会很快变糟)。

在 PC 上,事情可能会稍微复杂一些,因为有 32 位和 64 位模式、段寄存器和 PAE(它充当一种具有大页面的二级 MMU)。基本概念保持不变:每个进程都有自己的虚拟 4 GB 地址空间视图,内核使用 MMU 将每个虚拟页面映射到 RAM 中的适当物理位置,或者根本不映射。

【讨论】:

Thomas,内核是在 0xc0000000 的开头还是稍晚一点(几百 MB)? 显然,内核与从 0xC0000000 或不远(例如 0xC0008000)开始的代码链接,至少在 ARM 和 x86 上是这样。然而,内核也使用这 1GB 区域来分配自己的数据,包括动态块 (kmalloc()...) 和内核内线程的堆栈,我不知道这些地址离高地址有多远...... 0xC0000000- 的前 896 MB 用于映射物理。页?那么,内核存储在非常低的物理地址? 内核必须存储在物理RAM中(内核包含可以从交换区读回东西的代码,所以它不能自己换出,否则谁会从交换空间读回来?)。它在启动期间存储在 RAM 中,此时整个 RAM 不一定可用或完全检测到(这取决于架构;但引导加载程序通常非常原始)。所以内核倾向于去低物理地址。 内核在引导加载程序对其执行 jmp 时存储在低位。但是内核的引导(函数 main 和/或之前)包括移动到更高的地址(在激活 32/64 位模式并检测到内存之后),因为最小的 RAM 用于类似 ISA 的 DMA。内核包含代码和数据;并且可以换出一些数据。原则上,一些代码也可以交换(不是处理页面错误的代码)。 handle_mm_fault 中有一个单独的分支用于内核的虚拟地址。【参考方案3】:

osgx 有一个很好的答案,但我看到有人仍然不明白的评论。

每篇文章仅说明安装 4 GB 时的情况 内存和内核将 1 GB 映射到内核空间和用户 进程使用剩余的 RAM。

这里有很多混乱。有虚拟内存,也有物理内存。每个 32 位 CPU 都有 4GB 的虚拟内存。 Linux 内核的传统划分是 3G/1G 用于用户内存和内核内存,但较新的选项允许不同的分区。

为什么要区分内核空间和用户空间? - 我自己的问题

当任务交换时,必须更新 MMU。对于所有进程,内核 MMU 空间应该保持不变。内核必须随时处理中断和故障请求。

虚拟到物理映射如何工作? - 我自己的问题。

虚拟内存有多种排列方式。

到物理 RAM 页面的单个私有映射。 到单个物理页面的重复虚拟映射。 会引发 SIGBUS 或其他错误的映射。 由磁盘/交换支持的映射。

从上面的列表中,很容易看出为什么您可能拥有比物理内存更多的虚拟地址空间。事实上,故障处理程序通常会检查进程内存信息,以查看页面是否映射(我的意思是为进程分配),而不是在内存中。在这种情况下,故障处理程序将调用 I/O 子系统来读取页面。当页面被读取并更新 MMU 表以将虚拟地址指向新的物理地址时,导致错误的进程恢复。

如果您了解上述内容,就会清楚为什么您希望拥有比物理内存更大的虚拟映射。这就是支持内存交换的方式。

还有其他用途。例如,两个进程可能使用相同的代码库。由于链接,它们可能位于进程空间中的不同虚拟地址。在这种情况下,您可以将不同的虚拟地址映射到同一个物理页面,以节省物理内存。这对于新分配很常见;它们都指向物理“零页”。当您触摸/写入内存时,会复制零页并分配新的物理页(COW 或写入时复制)。

有时也可以为虚拟页面设置别名,其中一个为 cached,另一个为 non-cached。可以检查这两个页面以查看哪些数据已缓存,哪些未缓存。

主要是virtualphysical不一样!说起来容易,但在查看 Linux VMM 代码时经常会感到困惑。

【讨论】:

【参考方案4】:

-

您好,实际上我不是在 x86 硬件平台上工作,所以我的帖子中可能存在一些技术错误。

据我所知,特别列出了 0(或 16)MB - 896MB 之间的范围,而您的 RAM 多于该数字,例如,您的主板上有 1GB 物理 RAM,这称为“低内存”。如果您的主板上的物理 RAM 超过 896MB,那么其余的物理 RAM 称为 highmem。

说到你的问题,你的板上有 512MiBytes 物理 RAM,所以实际上,没有 896,没有 highmem。

内核能看也能映射的总RAM是512MB。

'因为物理内存和内核虚拟地址是一一对应的,所以内核有512MiBytes的虚拟地址空间。我真的不确定前面的句子是否正确,但这是我的想法。

我的意思是,如果有 512MBytes,那么内核可以管理的物理 RAM 量也是 512MiBytes,而且内核不能创建超过 512MBytes 的这么大的地址空间。

参考用户空间,有一点不同,用户应用程序的页面可以换出到硬盘,但内核的页面不能。

所以,对于用户空间,借助页表等相关模块,似乎还有4GBytes的地址空间。 当然,这是虚拟地址空间,而不是物理 RAM 空间。

这是我的理解。

谢谢。

【讨论】:

【参考方案5】:

如果物理内存小于 896 MB,那么 linux 内核会线性映射到该物理地址。

详情见此..http://learnlinuxconcepts.blogspot.in/2014/02/linux-addressing.html

【讨论】:

以上是关于linux内核如何管理不到1GB的物理内存?的主要内容,如果未能解决你的问题,请参考以下文章

内存管理(上)

Linux内存管理-高端内存

转载linux内核笔记之高端内存映射

linux内核 - 如何获取物理地址(内存管理)?

术语“用户空间”和“内核空间”是指物理内存吗?

操作系统——Linux内核完全注释011c-3.0