系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理

简介: 虚拟内存的主要作用是提供更大的地址空间,使得每个进程都可以拥有大量的虚拟内存,而不受物理内存大小的限制。此外,虚拟内存还可以提供内存保护和共享的机制,保护每个进程的内存空间不被其他进程非法访问,并允许多个进程共享同一份物理内存数据,提高了系统的资源利用率。虚拟内存的实现方式有分段和分页两种,其中分页机制更为常用和灵活。分页机制将虚拟内存划分为固定大小的页,将每个进程的虚拟地址空间映射到物理内存的页框中。为了减少页表的大小和访问时间,采用了多级页表的方式,将大的页表划分为多个小的页表,只加载需要的页表项,节约了内存空间。

虚拟内存

虚拟内存是一种操作系统提供的机制,用于将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上。通过使用虚拟内存,操作系统可以有效地解决多个应用程序直接操作物理内存可能引发的冲突问题。

在使用虚拟内存的情况下,每个进程都有自己的独立的虚拟地址空间,它们不能直接访问物理内存地址。当程序访问虚拟内存地址时,操作系统会进行地址转换,将虚拟地址映射到物理地址上,这样不同的进程运行时,写入的是不同的物理地址,避免了互相覆盖指针的问题。

虚拟内存的使用使得每个进程都可以拥有相同的虚拟地址空间,而不用担心与其他进程的地址冲突。操作系统负责管理虚拟地址和物理地址之间的映射关系,并在需要时进行地址转换。这样,进程可以以一种透明的方式访问内存,无需关心内存的实际物理位置。

image

通过虚拟内存机制,操作系统能够更好地管理系统内存资源,提供更高的安全性和稳定性。它可以为每个进程提供独立的地址空间,保护进程间的数据隔离,同时也可以有效地利用物理内存,将不常用的数据交换到磁盘上(交换区),以提供更大的可用内存空间。

内存分段

在分段机制下,虚拟地址由两部分组成:段选择子和段内偏移量。段选择子是一个索引,用于指定要访问的段的起始地址和长度。段内偏移量则表示在该段内的具体位置。

操作系统会维护一个段表,其中包含了每个段的起始地址和长度信息。当程序访问一个虚拟地址时,操作系统会通过段选择子从段表中找到对应的段描述符,然后根据段描述符中的信息计算出物理地址。

具体的映射过程如下:

  1. 程序访问虚拟地址,通过段选择子找到对应的段描述符。
  2. 根据段描述符中的基址和长度信息,计算出段的起始物理地址。
  3. 将段的起始物理地址与段内偏移量相加,得到最终的物理地址。

image

不过,需要注意的是,分段机制可能会导致内存碎片的问题,因为不同段的大小可能不同,导致一些碎片化的空间无法被利用。当不够内存分配的时候,会选择使用内存交换,先把一块正在使用的内存移到磁盘中,然后再移回来把中间留的内存缝隙全用上,虽然解决了内存碎片的问题,但是这个交换操作很慢,效率低,看下图示:

image

虚拟内存、分段和内存交换似乎解决了同时运行多个程序的问题,但仍存在性能瓶颈。由于硬盘访问速度较慢,每次内存交换都需要将大段连续的内存数据写入硬盘。因此,如果交换的是占用大量内存空间的程序,整个系统会变得卡顿。

为了解决内存分段的碎片和提高内存交换效率,引入了内存分页机制。

内存分页

内存分页是将整个虚拟和物理内存空间划分为固定大小的连续内存块,称为页(Page)。在Linux下,每一页的大小通常为4KB。虚拟地址与物理地址之间通过页表进行映射,页表存储在CPU的内存管理单元(MMU)中,从而CPU可以直接通过MMU找到实际访问的物理内存地址。

虚拟地址与物理地址之间通过页表来映射,如下图:

image

由于内存空间事先划分为固定大小的页,不会像分段机制那样产生碎片。当释放内存时,以页为单位进行释放,避免了无法利用的小内存块。

如果内存空间不足,操作系统会将其他正在运行的进程中的"最近未使用"的内存页面暂时存储到硬盘上,称为换出(Swap Out)。当需要时,再将页面加载回内存,称为换入(Swap In)。因此,每次写入硬盘的是少量的一页或几页,不会花费太多时间,从而提高了内存交换的效率。

image

简单分页

简单分页存在空间上的缺陷。在操作系统可以同时运行大量进程的情况下,页表会变得非常庞大。在32位环境下,虚拟地址空间为4GB,假设页的大小为4KB,就需要大约100万个页。每个页表项需要4字节来存储,所以整个4GB空间的映射需要4MB的内存来存储页表。

尽管4MB的页表看起来并不算太大,但要注意每个进程都有自己的虚拟地址空间,也就是说每个进程都有自己的页表。如果有100个进程,就需要400MB的内存来存储页表,这对于内存来说是相当大的开销,更不用说64位环境下了。

多级页表

要解决上述问题,我们可以采用一种叫做多级页表(Multi-Level Page Table)的解决方案。在之前我们已经了解到,在32位环境下,页大小为4KB的情况下,一个进程的页表需要存储100多万个页表项,每个项占用4字节的空间,因此一个页表需要4MB的内存空间。

为了节省内存空间,我们可以将单级页表进行分页,将一个页表(一级页表)分为1024个页表(二级页表),每个二级页表包含1024个页表项,形成二级分页结构。这样一级页表覆盖整个4GB的虚拟地址空间,而对于未使用的页表项,不会创建对应的二级页表,只在需要时才创建。如下图所示:

image

换个角度来看,大多数程序未使用到整个4GB的虚拟地址空间,因此部分页表项是空的,没有分配实际的内存空间。在物理内存紧张的情况下,操作系统会将最近一段时间未访问的页表换出到硬盘,从而释放物理内存。使用二级分页,一级页表只需要覆盖整个4GB的虚拟地址空间,而未使用的页表项不需要创建对应的二级页表。假设只有20%的一级页表项被使用,那么页表占用的内存空间只有0.804MB,相比于单级页表的4MB,内存节约非常巨大。

为什么不分级的页表无法实现这样的内存节约呢?从页表的性质来看,页表保存在内存中,其主要作用是将虚拟地址翻译为物理地址。如果在页表中找不到对应的页表项,计算机系统将无法正常工作。因此,页表必须覆盖整个虚拟地址空间。而不分级的页表需要100多万个页表项进行映射,而二级分页只需要1024个页表项(一级页表覆盖整个虚拟地址空间,二级页表在需要时创建)。

页表缓存TLB(Translation Lookaside Buffer)

TLB(Translation Lookaside Buffer)是一个位于CPU芯片中的缓存,用于存储程序中最常访问的页表项,以加快虚拟地址到物理地址的转换速度。多级页表虽然解决了空间上的问题,但是增加了转换的工序,导致时间上的开销。然而,由于程序的局部性原理,程序执行期间通常仅限于某一部分,访问的存储空间也局限于某个内存区域。因此,通过将最常访问的页表项存储到TLB这个硬件缓存中,可以更快地进行地址转换。

在CPU芯片中,内存管理单元(Memory Management Unit)芯片负责处理地址转换和TLB的访问与交互。当CPU进行寻址时,首先会查找TLB,如果找到了对应的页表项,就可以直接进行物理地址的访问,避免了继续查找常规页表的开销。

由于TLB中存储的是程序最常访问的几个页表项,所以TLB的命中率通常是很高的。这是因为程序执行过程中,访问的页表项相对固定。通过利用TLB,可以大大提高地址转换的速度,加快程序的执行效率。

Linux内存管理

image

Linux内存管理涉及逻辑地址和线性地址的转换。逻辑地址是程序使用的地址,而线性地址是通过段式内存管理映射的地址,也称为虚拟地址。

Linux的虚拟地址空间分为内核空间和用户空间两部分。32位系统中,内核空间占用1G,剩下的3G是用户空间;64位系统中,内核空间和用户空间都是128T,分别占据内存空间的最高和最低处。如下所示:

image

进程在用户态时只能访问用户空间内存,进入内核态后才能访问内核空间内存。虽然每个进程都有独立的虚拟内存,但虚拟内存中的内核地址关联的是相同的物理内存,这样进程切换到内核态后就可以方便地访问内核空间内存。

总结

虚拟内存是操作系统提供的一种机制,通过将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上,解决了多个应用程序直接操作物理内存可能引发的冲突问题。虚拟内存的使用使得每个进程都可以拥有相同的虚拟地址空间,而不用担心与其他进程的地址冲突。通过虚拟内存机制,操作系统能够更好地管理系统内存资源,提供更高的安全性和稳定性。虚拟内存的实现方式有分段和分页,其中分页机制更为常用,采用多级页表的方式节约了内存空间。页表缓存TLB能够加快虚拟地址到物理地址的转换速度。Linux的内存管理涉及逻辑地址和线性地址的转换,将虚拟地址空间分为内核空间和用户空间,方便进程访问内核空间内存。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
缓存 并行计算 PyTorch
PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制
本文深入探讨了PyTorch中GPU内存管理的核心机制,特别是CUDA缓存分配器的作用与优化策略。文章分析了常见的“CUDA out of memory”问题及其成因,并通过实际案例(如Llama 1B模型训练)展示了内存分配模式。PyTorch的缓存分配器通过内存池化、延迟释放和碎片化优化等技术,显著提升了内存使用效率,减少了系统调用开销。此外,文章还介绍了高级优化方法,包括混合精度训练、梯度检查点技术及自定义内存分配器配置。这些策略有助于开发者在有限硬件资源下实现更高性能的深度学习模型训练与推理。
2313 0
|
9月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2392 10
|
9月前
|
缓存 监控 Linux
CentOS系统如何查看当前内存容量。
以上方法都不需要特殊软件或者复杂配置即可执行,在CentOS或其他Linux发行版中都适合运行,并且它们各自透露出不同角度对待问题解答方式:从简单快速到深入详尽;从用户态到核心态;从操作层数到硬件层数;满足不同用户需求与偏好。
721 8
|
10月前
|
存储 缓存 监控
手动清除Ubuntu系统中的内存缓存的步骤
此外,只有系统管理员或具有适当权限的用户才能执行这些命令,因为这涉及到系统级的操作。普通用户尝试执行这些操作会因权限不足而失败。
1898 22
|
9月前
|
缓存 监控 Ubuntu
Ubuntu操作系统下清除系统缓存与无用文件的方法
通过上述步骤断行综合性地对Ubuntu进行优化与整洁可显著改善其性能表现及响应速度。然而,请注意在执行某些操作前确保充分了解其潜在影响;例如,在移除旧内核之前确认新内核稳定运行无问题;而对于关键配置更改则需确保备份好相关设置以便恢复原状态。
2387 0
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
1487 78
|
监控 Linux Python
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
457 27
|
缓存 Java Linux
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
1428 59
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
3095 58
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
1140 38

热门文章

最新文章