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

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

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

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

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

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

4GB的虚拟内存地址空间被分成很多页。X86处理器在32位模式下支持4KB、2MB以及4MB大小的页。Linux和Windows都是用的4KB的页来分割用户虚拟地址空间的。0-4096字节落在page 0,4096-8192字节落在page 1,以此类推。VMA的大小一定是page大小的倍数。下图就是用4KB的页分割的3GB用户虚拟地址空间示意:

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

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

D和A标志位代表了dirty和accessed,一个脏页表示该页已经被写过,一个被访问过的页表示该页被读过或者写过。最后,PTE存储了其对应的物理内存地址的起始地址,4KB对齐。

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

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

我们把virtual memory areas,page table entry以及page frame放在一起,理解一下它们是如何协作的,下图是一个用户堆的示例:

蓝色部分代表了VMA对应的地址范围,每一项都是一个page table entry,每个箭头代表从PTE到物理page frame的映射,某些PTE没有箭头,代表这些PTE的P标志位被清零了。这可能是因为这些页从来没有被访问过或者页已经被换出了。无论哪种情况,访问这些页都会导致缺页错误。

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

让我们看下所有的这些数据结构联合起来是如何工作的,下图是一个内存分配的示例:

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

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

Linux虚拟内存系统详解的更多相关文章

  1. Linux /dev目录详解和Linux系统各个目录的作用

    Linux /dev目录详解(转http://blog.csdn.net/maopig/article/details/7195048) 在linux下,/dev目录是很重要的,各种设备都在下面.下面 ...

  2. syslog之一:Linux syslog日志系统详解

    目录: <syslog之一:Linux syslog日志系统详解> <syslog之二:syslog协议及rsyslog服务全解析> <syslog之三:建立Window ...

  3. Linux 内存机制详解宝典

    Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...

  4. Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)

    启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...

  5. Linux启动过程详解

    Linux启动过程详解 附上两张图,加深记忆 图1: 图2: 第一张图比较简洁明了,下面对第一张图的步骤进行详解: 加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的 ...

  6. Linux netstat命令详解

    Linux netstat命令详解 一  简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多 ...

  7. java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET

    java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了!      社区福利快来领取免费参加MDCC大会机会哦    Tag功能介绍—我们 ...

  8. Linux crontab 命令详解

    在 Linux 中,任务可以被配置在指定的时间段.指定的日期.或系统平均载量低于指定的数量时自动运行.红帽企业 Linux 预配置了对重要系统任务的运行,以便使系统能够时时被更新.譬如,被 locat ...

  9. linux curl用法详解

    linux ‍‍curl用法详解 ‍‍curl的应用方式,一是可以直接通过命令行工具,另一种是利用libcurl库做上层的开发.本篇主要总结一下命令行工具的http相关的应用, 尤其是http下载方面 ...

随机推荐

  1. curl下载目录

    http://ftp.loongnix.org/os/loongnix/1.0/SRPMS/ wget wget -c -r -np -k -L -p

  2. Invalid App Store Icon. The App Store Icon in the asset catalog in 'xxx.app' can’t be transparent nor contain an alpha channel.

    1.向appstore上传应用的时候,报了这样一个错误 ERROR ITMS-90717: "Invalid App Store Icon. The App Store Icon in th ...

  3. Weekly Contest 117

    965. Univalued Binary Tree A binary tree is univalued if every node in the tree has the same value. ...

  4. HDU6299-2018ACM暑假多校联合训练1002-Balanced Sequence

    这个题的题意是给你n个字符串,认定()是一种平衡的串,两个以上连续的()()也是一种平衡的串,如果一对括号里面包含一个平衡的串,这个括号也被算在这个平衡的串之内, 如(()(()))是一个长度为8的平 ...

  5. Python爬取招聘信息,并且存储到MySQL数据库中

    前面一篇文章主要讲述,如何通过Python爬取招聘信息,且爬取的日期为前一天的,同时将爬取的内容保存到数据库中:这篇文章主要讲述如何将python文件压缩成exe可执行文件,供后面的操作. 这系列文章 ...

  6. GitHub 十大 CI 工具

    简评:GitHub 上最受欢迎的 CI 工具. 持续集成(Continuous integration)指的是,频繁地(一天多次)将代码集成到主干. 持续集成工具让产品可以快速迭代,同时还能保持高质量 ...

  7. 使用原生实现jquery中的css方法

    由于jquery放在mobile页面上,有时候还是显得有点大,所以今天尝试使用原生来开发,但是习惯了jquery之后,转用原生开发之后,发现原生中,找不到可以替代jquery的css方法,于是对原生的 ...

  8. 【问题记录】element is not attached to the page document

    遇到ui脚本报错:element is not attached to the page document 解决办法,再次定位即可

  9. 性能检测参考SQL语句

    /****** Object: StoredProcedure [dbo].[SP_CPU] Script Date: 12/09/2018 19:01:24 ******/ SET ANSI_NUL ...

  10. 洛谷 P1579 哥德巴赫猜想(升级版)

    嗯... 这或许也算一道数论题吧... 题目链接:https://www.luogu.org/problemnew/show/P1579 这道题的说明好像只会扰乱人的思路....然后就是这道题的细节比 ...