Linux虚拟内存系统详解

本文章以Linux为例,讲解一下虚拟内存系统的工做原理,windows系统的原理也是大同小异,有兴趣的读者能够自行查阅相关资料。linux

linux内核以及它管理用户内存的机制,下面咱们以应用程序gonzo的内存示意图为例,进行详细说明。windows

  

Linux进程在内核中是以一个task_struct实例来实现的,称为进程描述符。task_structmm字段指向了内存描述符,即mm_struct,它是一份可执行程序的内存结构概要。如上图所示,它存储了内存各个内存端的起始位置和结束位置,进程使用的物理内存页的数量,进程使用的虚拟地址空间等信息。在内存描述符内部,还有两个内存管理的重要结构:virtual memory areaspage tables。下图就是Gonzo的内存区域示意图:数据结构

 

 

 

每个virtual memory areaVMA)都是一段连续的虚拟内存地址,这些内存区域毫不会重合。一个vm_area_struct描述一个内存区域,包括了它的起始地址和结束地址,内存访问权限标志位,以及一个vm_file字段(若是有该字段的话,用来指定哪一个文件映射到了该内存区域)。VMA不会映射匿名文件。进程内存布局中除了内存映射段外的每个内存段都对应一个VMA。这种方式尽管在X86机器上很常见,但这并非硬性要求。VMA们并不关心它们对应的是哪一个段。函数

 

一个程序的VMA们都是做为一个链表存在于内存描述符的mmap字段中的,而且按照虚拟地址进行了排序,而且是一个以mm_rb为根节点的红黑树。采用红黑树的数据结构是为了方便内核给定虚拟地址后快速查找对应的内存区域。当你读/proc/pid_of_process/maps这个文件时,内核就是简单的遍历进程的VMA链表并挨个打印。布局

 

Windows中的EPROCESS块就是task_structmm_struct的混合,它对应于VMA的是一个称为Virtual Address Descriptor(VAD)的数据结构,VAD们存储在一个AVL树中。有意思的是,Windowslinux的区别真的很小。post

 

4GB的虚拟内存地址空间被分红不少页。X86处理器在32位模式下支持4KB2MB以及4MB大小的页。LinuxWindows都是用的4KB的页来分割用户虚拟地址空间的。0-4096字节落在page 0,4096-8192字节落在page 1,以此类推。VMA的大小必定是page大小的倍数。下图就是用4KB的页分割的3GB用户虚拟地址空间示意:spa

 

 

 

处理器利用page tables来将虚拟内存地址转换为物理内存地址。每一个进程都有本身的page tables,不管进程切换什么时候发生,用户态的page table也会跟着切换。Linux在内存描述符中的pgd字段存储了一个指向该进程page tables的指针。每个虚拟内存页对应一个page table entry,一个X86的页的结构以下:翻译

 

 

Linux有函数来读取和设置PTE中的每个标志位。位P告诉处理器该虚拟页是否要在物理内存中呈现。若是设为0,访问该页时会触发一个页错误。R/W标志位表明了读写权限,若是为0则该页为只读。U/S标志位表明了普通用户和超级用户,若是设置为0,则该页只能被内核访问。这些标志位都是用来实现前面看到的只读内存和内核态地址空间的。设计

 

DA标志位表明了dirtyaccessed,一个脏页表示该页已经被写过,一个被访问过的页表示该页被读过或者写过。最后,PTE存储了其对应的物理内存地址的起始地址,4KB对齐。指针

 

内存保护是以页为单位进行的,由于每一个页都共用U/SR/W标志位。可是同一个物理内存页能够对应多个虚拟内存页,这些不一样的虚拟内存也可能有不一样的保护标志位,因此要记住:在VMA设置的权限标志位不必定真正的用到了物理内存的保护上。

 

虚拟内存不存储任何东西,它只是简单的将程序的地址空间映射到底层的物理内存空间,物理地址空间才是处理器真正操做的内存空间。物理地址空间也被分红了以页为单位的大小。每一个页是物理内存管理的最小单位。32LinuxWindows都是以4KB为大小划分页的。下图所示为一个2GB大小的RAM

 

 

咱们把virtual memory areaspage table entry以及page frame放在一块儿,理解一下它们是如何协做的,下图是一个用户堆的示例:

 

  

蓝色部分表明了VMA对应的地址范围,每一项都是一个page table entry,每一个箭头表明从PTE到物理page frame的映射,某些PTE没有箭头,表明这些PTEP标志位被清零了。这多是由于这些页历来没有被访问过或者页已经被换出了。不管哪一种状况,访问这些页都会致使缺页错误。

 

一个VMA就像一份你的程序和内核之间合约,你要求完成一些事情,好比内存分配、文件映射等,内核说没问题,而后它建立或者更新合适的VMA。可是为了效率,内核不会立马相应你的请求,直到第一次访问页产生缺页错误时才会去作,这也是虚拟内存的设计原则。

 

让咱们看下全部的这些数据结构联合起来是如何工做的,下图是一个内存分配的示例:

 

当程序经过brk()系统调用请求更多的内存时,内核简单的更新堆的VMA,这时并无page frame分配。

 

 

 

 

文章参考翻译自:https://manybutfinite.com/post/how-the-kernel-manages-your-memory/

相关文章
相关标签/搜索