操作系统内存管理

内存管理和虚拟内存

Posted by Ted on November 25, 2022

一、内存管理

操作系统的内存管理非常重要,主要负责下面这些事情:

  • 内存的分配与回收:对进程所需的内存进行分配和释放,malloc 函数:申请内存,free 函数:释放内存。
  • 地址转换:将程序中的虚拟地址转换成内存中的物理地址。
  • 内存扩充:当系统没有足够的内存时,利用虚拟内存技术或自动覆盖技术,从逻辑上扩充内存。
  • 内存映射:将一个文件直接映射到进程的进程空间中,这样可以通过内存指针用读写内存的办法直接存取文件内容,速度更快。
  • 内存优化:通过调整内存分配策略和回收算法来优化内存使用效率。
  • 内存安全:保证进程之间使用内存互不干扰,避免一些恶意程序通过修改内存来破坏系统的安全性。

1.1 malloc是如何分配内存的?

malloc 是 C 语言标准库中用于动态内存分配的函数。它从堆(heap)上分配指定大小的内存块,并返回一个指向该内存块的指针。如果分配失败,它返回 NULL。

基本工作原理

在使用 malloc 进行内存分配时,实际的处理过程涉及两个层面:首先是从进程的内存池中尝试分配内存,如果进程的内存池中没有足够的空间满足当前的请求,那么 malloc 会从操作系统那里请求更多的内存。

  • 从进程内存池中分配
    • 内存池管理:进程的内存池通常由 malloc 管理,它包含了一系列已经从操作系统获取并为进程预留的内存块。这些内存块可能是连续的或者是非连续的,取决于之前的分配和释放操作。
    • 内存分配尝试:当调用 malloc 请求内存时,malloc 首先检查其管理的内存池中是否有足够的空闲内存来满足请求。这涉及到查找合适大小的空闲块,可能需要根据内存分配算法(如首次适应、最佳适应等)来选择。
  • 从操作系统请求内存
    • 内存池不足:如果进程的内存池中没有足够的空闲内存块来满足当前的请求,malloc 需要从操作系统请求更多的内存。
    • 系统调用:malloc 通过系统调用(如 sbrk 或 mmap)向操作系统请求额外的内存。这些调用会将更多的内存区域分配给进程,从而扩展进程的堆空间。内存整合和返回
  • 内存整合:获取到新的内存后,malloc 可能会执行一些内存整合操作,如合并相邻的空闲块,以优化内存的使用和减少碎片。
  • 返回内存指针:完成内存分配后,malloc 将返回一个指向新分配内存块的指针。

需要注意的是,malloc函数只负责分配内存,并不会初始化内存块的内容。如果需要初始化内存块,可以使用memset等函数进行操作。

1.2 内存碎片

  • 内部内存碎片(Internal Memory Fragmentation,简称为内存碎片):已经分配给进程使用但未被使用的内存。导致内部内存碎片的主要原因是,当采用固定比例比如 2 的幂次方进行内存分配时,进程所分配的内存可能会比其实际所需要的大。举个例子,一个进程只需要 65 字节的内存,但为其分配了 128(2^7) 大小的内存,那 63 字节的内存就成为了内部内存碎片。
  • 外部内存碎片(External Memory Fragmentation,简称为外部碎片):由于未分配的连续内存区域太小,以至于不能满足任意进程所需要的内存分配请求,这些小片段且不连续的内存空间被称为外部碎片。也就是说,外部内存碎片指的是那些并未分配给进程但又不能使用的内存。我们后面介绍的分段机制就会导致外部内存碎片。

二、虚拟内存

虚拟内存(Virtual Memory) 是计算机系统内存管理非常重要的一个技术,本质上来说它只是逻辑存在的,是一个假想出来的内存空间,主要作用是作为进程访问主存(物理内存)的桥梁并简化内存管理。

img

虚拟地址空间构成虚拟内存。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片。还有部分暂时存储在外部磁盘存储器上(Swap),在需要时进行数据交换。

虚拟内存提供了以下能力

  1. 隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
  2. 提升物理内存利用率:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
  3. 简化内存管理:进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
  4. 多个进程共享物理内存:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
  5. 提高内存使用安全性:控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
  6. 提供更大的可使用内存空间:可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。

总的来说,虚拟内存技术通过将物理内存和磁盘空间结合起来,为每个进程提供了一个连续的、抽象的地址空间。它扩展了地址空间、提供了内存管理和保护机制,支持共享和隔离,提高了系统的性能和资源利用率,使得计算机系统更加灵活、稳定和安全。

虚拟内存的工作原理如下

  1. 地址空间划分:每个进程都有自己的虚拟地址空间,通常是一个连续的地址范围。这个地址空间被划分为多个固定大小的页面(通常是4KB),每个页面都有一个唯一的虚拟地址。
  2. 页面映射:虚拟内存管理器将虚拟页面映射到物理内存或磁盘上的页面框(通常也是4KB)。这个映射关系存储在页表中,页表记录了虚拟页面和物理页面之间的对应关系。
  3. 页面置换:当进程访问一个虚拟页面时,虚拟内存管理器首先检查该页面是否已经在物理内存中。如果在物理内存中,就直接访问;如果不在物理内存中,就发生了缺页中断。
  4. 缺页中断处理:当发生缺页中断时,操作系统会根据页表中的映射关系,将对应的页面从磁盘读取到物理内存中的一个空闲页面框中,并更新页表。然后,进程的执行可以继续,就好像该页面一直在物理内存中一样。
  5. 页面置换算法:当物理内存不足时,操作系统需要选择一个页面进行置换,将其写回磁盘并释放其物理内存。常用的页面置换算法有最近最久未使用(LRU)、先进先出(FIFO)和时钟(Clock)算法等。

虚拟内存缺点

  1. 性能开销:虚拟内存的使用涉及到页表查找和磁盘I/O操作,这些都可能导致性能下降。特别是当系统频繁进行换页操作时,会显著影响系统性能,这种现象称为“抖动”。
  2. 硬盘速度限制:虽然现代固态硬盘(SSD)的速度已经大幅提升,但相比于物理内存,硬盘的访问速度仍然较慢。频繁的访问硬盘来加载或存储页面会减慢程序的执行速度。
  3. 复杂的内存管理:虚拟内存系统需要复杂的硬件支持(如内存管理单元MMU)和操作系统级的支持,这增加了系统设计和维护的复杂性。
  4. 资源消耗:维护页表和相关数据结构需要额外的内存和CPU资源。

三、虚拟内存实现的三种机制

虚拟内存(Virtual Memory)是一种计算机系统技术,它使得程序可以使用比实际物理内存更大的地址空间。虚拟内存通过将虚拟地址映射到物理地址,实现了内存的高效管理和保护。虚拟内存的实现主要有三种机制:分页(Paging)、分段(Segmentation)和段页结合(Segmentation with Paging)。

1. 分页机制(Paging)

分页机制是虚拟内存实现中最常见的一种方式。它将虚拟地址空间和物理地址空间都划分为固定大小的块,分别称为页(Page)和页框(Frame)。虚拟地址由页号和页内偏移量组成,通过页表(Page Table)将页号映射到物理内存中的页框。

特点
  • 固定大小:页和页框大小固定,简化了内存管理。
  • 减少外部碎片:由于页大小固定,减少了外部碎片。
  • 内存保护:每个页可以有不同的访问权限。

2. 分段机制(Segmentation)

分段机制将虚拟地址空间划分为若干段,每个段有一个段基址和段长度。虚拟地址由段选择子和段内偏移量组成。段选择子用于选择段,段内偏移量用于指定段内的具体地址。

特点
  • 灵活性:可以根据需要动态调整段的大小。
  • 保护性:每个段可以有不同的访问权限,提供了内存保护。
  • 共享性:多个进程可以共享同一个段。

3. 段页结合机制(Segmentation with Paging)

段页结合机制结合了分段和分页的优点。内存首先被划分为段,每个段再划分为若干页。虚拟地址由段选择子、页号和页内偏移量组成。

特点
  • 灵活性和固定大小结合:段提供灵活性,页提供固定大小的管理。
  • 减少外部碎片:分页减少了外部碎片。
  • 内存保护和共享:段和页都可以有不同的访问权限,提供了更细粒度的内存保护和共享。

总结

  • 分段机制(Segmentation):分段机制将虚拟地址空间划分为多个段(segment),每个段具有不同的大小和属性。每个段都有一个基地址和长度,通过将段内的偏移量与基地址相加,可以得到物理地址。分段机制可以提供灵活的地址空间管理,但可能会导致外部碎片问题。
  • 分页机制(Paging):分页机制将虚拟地址空间和物理内存空间划分为固定大小的页(page),通常为4KB或者更大。虚拟地址被划分为页号和页内偏移量,通过页表(Page Table)来映射虚拟页号到物理页框号。分页机制可以提供更好的内存利用率和地址空间的连续性,但可能会导致内部碎片问题。
  • 段页机制(Segmentation with Paging):段页机制是分段机制和分页机制的结合,它将虚拟地址空间划分为多个段,每个段再划分为多个页。通过段表(Segment Table)和页表的组合,可以将虚拟地址映射到物理地址。段页机制综合了分段和分页的优点,提供了更灵活的地址空间管理和更好的内存利用率。