虚拟内存
虚拟内存(Virtual Memory)
是计算机系统内存管理的一种技术,它使得应用程序认为它们拥有连续的可用内存,而实际上,这些内存可能是分散在物理内存和硬盘上的
。虚拟内存技术将物理内存抽象为一系列连续的虚拟地址(Virtual Addresses),这些地址由操作系统和硬件共同管理。虚拟内存的主要目的是简化内存管理,提高内存利用率和保护应用程序之间的隔离。
在Linux操作系统中,它使用虚拟内存这项技术管理物理内存,为每个进程提供一个独立的、连续的虚拟地址空间。虚拟内存分为若干个固定大小的页(Page),每个页对应物理内存中的一个页帧(Page Frame)。
虚拟内存的主要概念和组件包括:
-
虚拟地址空间(Virtual Address Space):
对于每个进程,操作系统都会提供一个独立的虚拟地址空间
。虚拟地址空间的大小取决于硬件架构和操作系统。例如,在32位系统中,虚拟地址空间的大小为4GB(2^32字节);在64位系统中,虚拟地址空间的大小可以达到数TB或更大。 -
页(Page):虚拟内存被划分为固定大小的单元,称为页。页的大小取决于硬件架构,通常为4KB。物理内存也被划分为同样大小的单元,称为页帧(Page Frame),
每个虚拟内存中的页会相应的对应物理内存中的一个页帧
。 -
页表(Page Table):页表是一种数据结构,用于存储虚拟地址到物理地址的映射关系,它实际上是存在于物理内存中的,操作系统负责管理和维护页表。当程序访问虚拟地址时,硬件和操作系统会根据页表将其转换为相应的物理地址。
- 虽然页表存储在物理内存中,但操作系统在处理页表时,通常会使用虚拟地址来访问页表。为了实现这一点,
操作系统会将页表的部分或全部内容映射到虚拟地址空间中。这样一来,操作系统可以通过虚拟地址方便地访问和管理页表
。 - 此外,现代CPU中通常集成了
内存管理单元
(Memory Management Unit,MMU),它可以加速虚拟地址到物理地址的转换过程。MMU中包含一个称为Translation Lookaside Buffer(TLB)的高速缓存,用于存储最近使用的虚拟地址到物理地址的映射关系。当进行地址转换时,MMU首先会检查TLB中是否有相应的映射关系,如果没有,再查询物理内存中的页表。这样可以避免频繁访问物理内存中的页表,提高地址转换效率。
- 虽然页表存储在物理内存中,但操作系统在处理页表时,通常会使用虚拟地址来访问页表。为了实现这一点,
-
分页(Paging):
分页是一种内存管理技术
,当程序访问的虚拟地址对应的物理内存页不在内存中(例如,被换出到硬盘)时,会触发分页异常。这时,操作系统需要将所需的物理页从硬盘加载到内存中,然后更新页表以反映新的映射关系。这个过程称为分页或页面调度(Page Swapping)。 -
缺页中断(Page Fault):
缺页中断是一种特定的中断类型
,当程序访问的虚拟地址对应的物理内存页不在内存中时,会触发缺页中断。此时,操作系统需要处理缺页中断,将所需的物理页从硬盘加载到内存中,并更新页表。
虚拟内存的优点:
-
简化内存管理:虚拟内存使得程序可以使用连续的内存空间,而无需关心实际的物理内存布局。这简化了内存分配和管理的过程。
-
提高内存利用率:通过将不常用的内存页换出到硬盘,虚拟内存技术可以有效地利用有限的物理内存资源。这使得系统能够运行更多的进程和更大的数据集。
-
内存隔离:虚拟内存为每个进程提供独立的虚拟地址空间,从而确保进程之间不会互相干扰。这有助于提高系统的稳定性和安全性。
-
内存共享:虚拟内存技术允许多个进程共享相同的物理内存页,从而实现进程间通信和资源共享。例如,动态链接库和内存映射文件就依赖于虚拟内存的共享机制。
虽然虚拟内存技术带来了许多优点,但它也有一些缺点和限制:
-
性能开销:虚拟地址到物理地址的转换和页面调度过程会产生一定的性能开销。为了降低这种开销,现代硬件通常包含一个称为转换后援缓冲(Translation Lookaside Buffer,TLB)的高速缓存,用于存储最近使用过的虚拟地址到物理地址的映射关系。
-
外部碎片化:虚拟内存技术可能导致硬盘上的外部碎片化。当物理内存页被频繁换入换出时,硬盘上的空闲空间可能变得零散,从而降低磁盘操作的性能。为了解决这个问题,操作系统通常会使用一种称为交换空间(Swap Space)的连续磁盘区域来存储换出的内存页。
总之,虚拟内存是一种内存管理技术
,它为应用程序提供了连续的内存空间,并实现了内存的高效利用和进程间隔离。虽然虚拟内存技术带来了一定的性能开销,但通过使用高速缓存和优化算法,现代操作系统和硬件可以将这种开销降到最低。
分页 与 缺页中断 的区别
分页和缺页中断在实际使用中常常会被混淆,但它们在概念上确实是有所区别的。
-
分页(Paging)是一种内存管理技术
,它将虚拟内存划分为固定大小的单位(页),并将这些页映射到物理内存中。分页技术使得操作系统可以按需加载和卸载物理内存中的数据,从而更有效地利用有限的内存资源。当操作系统需要将物理内存中的某个页换出到硬盘时,就会发生分页或页面调度(Page Swapping)。 -
缺页中断(Page Fault)是一种特定的中断类型
,它在程序访问虚拟地址对应的物理内存页不在内存中时触发。缺页中断的原因可能是:- 该物理页尚未加载到内存中。例如,程序刚启动时需要的代码和数据还没有被加载到内存。
- 该物理页曾经被加载到内存中,但由于内存资源不足,被换出到硬盘。
-
当发生缺页中断时,操作系统需要将所需的物理页从硬盘加载到内存中,并更新页表以反映新的映射关系。加载完成后,进程从中断状态恢复,继续访问所需的数据。
举一个例子:
-
当您的程序(进程)要访问某个文件或内存数据时,首先会在虚拟内存中查找对应的虚拟地址。虚拟内存中的每个地址都映射到物理内存中的一个物理页。然而,并不是虚拟内存中的所有内容都实时加载到物理内存中。这是因为物理内存有限,操作系统需要根据程序的实际需求有效地管理内存资源。
-
当程序试图访问一个尚未加载到物理内存中的虚拟地址时(例如,该地址对应的物理页被换出到硬盘,或者文件内容尚未加载到内存中),就会触发分页异常。这时,操作系统需要将所需的物理页从硬盘加载到内存中,并更新页表以反映新的映射关系。这个过程称为分页或页面调度。
分页技术使得操作系统可以有效地利用有限的物理内存资源,实现内存的动态分配和回收。当某个进程需要更多内存时,操作系统可以将不活跃的物理页换出到硬盘,从而为新的内存需求腾出空间。同时,分页技术还有助于保护进程之间的内存隔离,防止一个进程访问其他进程的内存空间。
总之,分页是一种内存管理技术,而缺页中断是在程序访问不在内存中的数据时触发的一种中断。虽然它们在实际操作中密切相关,但它们在概念上是有区别的。
磁盘文件如何被加载到内存空间?
情景假设:
- 假设有一个文件a.txt ,它存储在磁盘上的一块被记作b的空间上。a.txt这个文件中存储的一段文字叫做“hello,world”。
- 现在有一个程序C,这个程序的功能就是修改文字。我们现在想要运行程序C,去修改a.txt中的文字,从原来的"hello,world"改为"hello,new day!"
回答问题:
-
当a.txt被加载到内存空间时,磁盘上的一块被记作b的空间上的a.txt文件仍然存在。在这个过程中,操作系统会将a.txt的数据复制到物理内存中,而不是移除它。所以,在内存中的数据和磁盘上的数据实际上是两个独立的副本。
-
当a.txt被修改后并写回磁盘时,操作系统通常会尝试将修改后的数据写回到原来的位置(即b空间),但这并非绝对。
-
在某些情况下,例如当文件大小增加以至于原来的位置无法容纳时,操作系统可能会将修改后的a.txt数据写入磁盘的另一个空闲区域,并更新文件系统的元数据以反映这个变化。如果操作系统将修改后的数据写回到原来的位置,那么在磁盘上的a.txt仍然位于b空间。如果将数据写入到另一个空闲区域,那么在磁盘上的a.txt将位于新的位置。
小总结
我的分析
-
当文件被加载到内存空间的时候,实际上相当于内存复制了文件的一块内容,或者是复制了整个文件的内容到内存空间上去,
我们称内存空间上关于这个文件的内容是磁盘上文件内容的一个副本,磁盘文件才是这个文件真正的母本
。 -
然后操作系统会将复制的文件中的数据按页(通常为4KB)划分,然后将这些页加载到物理内存的空闲页框中。在加载过程中,操作系统会为这些页在虚拟地址空间中分配对应的虚拟地址,并在页表中建立虚拟地址到物理地址的映射关系。这样,进程就可以通过虚拟地址访问文件中的数据了。
-
当我们(进程)对这个文件做了一系列的修改后,我们首先会尝试把这些修改后的数据,重新写回磁盘中文件的母本中。
- 假如,修改后文件的大小没有发生变化,ok,那就直接对文件母本进行覆写,也就是写回原来的位置。
- 假如修改后,文件变小了,首先我们会对母本的内容进行一次覆写,然后缩小母本在磁盘上占的大小,文件在磁盘上的位置不会发生改变。
- 假如修改后,文件变大了,但原来的位置可以容纳下它,那我们还是会对母本的内容进行一次覆写,然后扩大母本的磁盘空间,但是文件母本在磁盘上的位置不会发生改变。
- 假如修改后,文件变大了,以至于原来的位置无法容纳时,我们会可能会将文件副本重新写入到磁盘空间的另一个位置(空闲区域),并更新文件系统的元数据以反映这个变化。那么此时文件母本会存在在磁盘空间上的一个新的地方。
G老师给我的回复
-
你的分析基本上是正确的。当一个文件被加载到内存时,操作系统确实会创建一个该文件的副本,并将其存储在内存空间中。进程通过访问虚拟地址来操作文件数据,这些虚拟地址通过页表映射到物理内存地址。当进程对文件进行修改时,操作系统需要将这些修改写回磁盘上的原始文件。根据文件修改后的大小以及磁盘上的可用空间,这个过程可能涉及覆写、缩小或扩大原始文件的磁盘空间,甚至可能需要将文件移动到磁盘上的另一个位置。
-
需要注意的是,操作系统在处理文件数据时,会采取一定的策略来优化性能。例如,操作系统可能会延迟将内存中修改后的数据写回磁盘,直到有足够的数据需要写入,或者在一段时间后自动执行写回操作。这种策略可以提高I/O性能,但也可能导致在突然断电的情况下丢失尚未写回磁盘的数据。
-
总之,你的分析对于操作系统如何处理文件加载、修改和写回的过程是正确的。
为什么会产生“外部碎片化”
外部碎片化主要是由于磁盘上的空闲空间被分割成很多较小、不连续的部分导致的。
当文件被创建、删除或调整大小时,会在磁盘上产生空闲空间。如果这些空闲空间没有得到合理的利用,它们就会导致外部碎片化
。
假设你的磁盘上有以下几个音乐文件:
songA.mp3 (50KB)
songB.mp3 (100KB)
songC.mp3 (150KB)
-
现在,你决定删除songB.mp3,这会在songA.mp3和songC.mp3之间产生100KB的空闲空间。接下来,你下载了一个新的音乐文件,songD.mp3,大小为120KB。由于之前空闲的100KB空间不足以容纳songD.mp3,操作系统需要在磁盘上找到一个足够大的连续空间来存储这个新文件。因此,songD.mp3被存储在磁盘上的其他位置,而原来的100KB空闲空间仍然保持空闲。
-
此后,你又下载了一个新的音乐文件,songE.mp3,大小为80KB。由于原来的100KB空闲空间足够容纳songE.mp3,操作系统将其存储在这个空闲空间中。然而,这会导致原本的100KB空闲空间减小到20KB。
现在,磁盘上的空闲空间被分割成了一个较小的、不连续的部分。当需要在磁盘上创建或调整大小的文件时,这些零散的空闲空间可能会导致性能下降,因为操作系统需要在磁盘上寻找足够大的连续空间来满足需求。这种情况就是所谓的外部碎片化。
为了解决外部碎片化问题,操作系统通常会进行磁盘碎片整理,将磁盘上的空闲空间重新组织,使其更加连续。此外,操作系统还使用交换空间来管理物理内存和磁盘之间的数据交换,以减少外部碎片化的影响。
虚拟地址空间
虚拟地址空间是一种内存管理技术,它允许程序以一种抽象的方式访问内存资源,从而实现对物理内存的高效管理。虚拟地址空间将物理内存抽象为一个连续的地址空间,使得每个进程都拥有自己独立的虚拟地址空间。操作系统负责管理虚拟地址到物理地址的映射。
在Linux中,虚拟地址空间通常分为两个区域:用户区域和内核区域。
- 用户区域: 用户区域包含了用户程序的代码、数据和堆栈等信息。具体包括以下部分:
- 程序代码:存放编译后的可执行二进制代码,这部分内存区域是只读的,防止程序意外或恶意修改代码。
- 数据段:存放程序的全局变量和静态变量。
- 堆:用于存储程序运行时动态分配的内存。堆从低地址向高地址增长。
- 文件映射区:存储映射到内存中的文件数据,如共享库、内存映射文件等。
- 栈:存放程序的局部变量、函数调用的参数和返回地址等信息。栈从高地址向低地址增长。
- 环境变量和命令行参数:存放传递给程序的环境变量和命令行参数。
- 内核区域:内核区域是操作系统内核代码和数据的存储区域。用户进程无法直接访问这部分内存,只能通过系统调用与内核进行交互。内核空间包括以下部分:
- 内核代码:操作系统内核的代码。
- 内核数据:操作系统的全局数据结构,如进程表、文件系统缓存等。
- 内核栈:为内核模式下的函数调用提供栈空间。
- 物理内存映射:将物理内存映射到内核虚拟地址空间,便于内核访问。
- 高速缓存:缓存一些常用的内核数据结构,提高访问速度。
用户空间和内核空间的主要区别在于访问权限和功能。用户空间主要用于存储用户程序的代码和数据,而内核空间则存放操作系统内核的代码和数据。用户空间的程序无法直接访问内核空间,只能通过系统调用与内核进行交互。这种设计有助于保护内核数据结构的完整性,避免用户程序意外或恶意破坏操作系统。
当计算机程序运行时,它需要访问一些内存,例如使用的变量或代码等。虚拟地址空间是一种使程序可以直接访问这些内存而不必真正了解这些内存在物理上所处的位置的技术。虚拟地址由处理器内存管理单元映射到实际物理地址。每个进程都有自己的独立虚拟地址空间,同时操作系统负责将每个进程的虚拟地址映射到主存中的物理内存地址。
虚拟地址空间为计算机提供了多个好处:
- 进程之间不会干扰彼此的虚拟地址空间,因此它们可以相互隔离。
- 虚拟地址空间使每个进程都有最大4GB(32位系统)或16EB(64位系统)可用的虚拟内存空间,尽管实际物理内存少于该数量,这样允许处理器利用虚拟内存空间存储更多数据。
虚拟内存把许多分散的小块整合成一个连续的地址范围, 优化了内存访问的速度。
虚拟地址空间允许各进程共享某些资源并提升系统的效率。
总的来说,虚拟地址空间是一种重要的内存管理技术,它允许计算机以更高效、可靠和安全的方式管理内存。
结合着Linux系统来再来理解一下这个概念
在32位Linux系统中,虚拟地址空间大小为4GB,分为内核空间和用户空间。以下是一个简要概述:
-
内核空间(Kernel Space):
内核空间位于高地址段,通常在3GB到4GB之间
。这个区域存放内核代码和数据结构,如内存管理、进程管理、设备驱动等。这部分内存只能由内核模式下的代码访问。
-
用户空间(User Space):
-
用户空间位于低地址段,通常在0到3GB之间
。这个区域存放用户程序和相关数据结构。用户空间从高地址到低地址包含以下部分:- 环境变量和命令行参数:位于用户空间的高地址部分。
- 栈空间(Stack):位于环境变量和命令行参数之下,用于存放函数调用栈、局部变量和返回地址等。
- 动态链接库(Shared Libraries):位于栈空间下方,存放动态链接的共享库。内存映射文件也会被加载到这个区域。
- 堆空间(Heap):位于动态链接库下方,用于存放动态分配的内存。堆空间在程序运行期间可以根据需要增长或缩小。
- 未初始化数据段(BSS)、初始化数据段(Data)和代码段(Text):位于堆空间下方,存放程序的全局变量、静态变量和程序代码。
-
内存映射文件和动态链接库通常位于用户空间的共享库部分
。需要注意的是,不同的Linux发行版和架构可能会对虚拟地址空间的布局有所调整,但大致结构保持一致。