浅谈内存管理
仅供个人学习参考
虚拟寻址
计算机的最基本体系结构就是CPU加上内存,CPU负责计算,内存负责存储临时的计算数据,当然实际的模型要复杂得多,那么,问题来了,CPU如何准确获取内存上的数据呢?
我们先将内存假设成一个字节数组的模型
很容易可以得出一个简单粗暴的方法——物理寻址。就是照着内存的地址直接找。显然,这种寻址方式会导致许多问题,如不支持多进程同时进行(会导致不同进程之间的越级访问,互相篡改数据啥的)。所以我们现在采取另外一种寻址方式——虚拟寻址。
虚拟寻址涉及到另外一个抽象概念——虚拟地址空间。虚拟内存由虚拟地址组织起来,它使得每个进程都单独的使用整个主存(从进程的角度来看),并且每个进程所看到的内存都是一模一样的,该内存空间称为虚拟地址空间
举个栗子
如图
其中,DRAM就是我们平时所说的内存(比如某台笔记本的内存为32GB),这个内存是实实在在的物理内存,但是对于每个进程来说,他们所看到的是虚拟内存(对于32位操作系统,其大小为4GB)至于为什么是4GB请自行百度
负责虚拟地址到物理地址的转换的是一个叫做MMU( memory manage unit , 虚拟内存管理单元) 的硬件。每当CPU需要访问物理内存时,都会产生一个虚拟地址,这个虚拟地址被 MMU 翻译成物理内存地址。
内存分段
早期人们采用的方法是内存分段,即把一段与程序所需空间大小相等的虚拟内存映射到等大小的物理内存上,如图。
但是这样子做效率低下,倘若内存不够了,需要先将原先内存里暂时不用的段拿出来放到磁盘(因为是对照一整个程序的段,所以这个段会很大,读写费时间)。于是我们采取新的方式————内存分页。
内存分页
为了方便地址的管理和调用,Linux采取内存分页的方式。即将物理内存划分为固定大小的页(物理页【Physical Page】),通常是4KB或者2MB。每个进程的虚拟地址空间也被划分为相同大小的页(虚拟页【Vistural Page】)。通过页表(Page Table)来记录虚拟页和物理页的映射关系。当进程访问虚拟地址时,操作系统会根据页表将虚拟页映射到物理页上,从而实现地址转换
页表(提一嘴)
页表是一个结构为PTE(Page Table Entry) 的数组。页表中的每一个表项都存储了一个虚拟页到物理页的映射。
根据虚拟页是否分配了物理页和是否缓存的角度来区分,虚拟内存页有三种:
(1).未分配物理页
(2).已分配物理页但未缓存
(3).已分配物理页且已缓存
(常用的页会放在内存中,不常用的会写入磁盘毕竟内存是有限的嘛)为了区分这三种情况,页表中有一个有效标志位,和一个指向物理内存的地址。 如果地址为空,表示没有为虚拟页分配对应的物理页(1)。而有效标志位,用于表明是否缓存在DRAM中。如果有效位为1,则表示物理内存页已分配且已缓存(3),此时地址指向DRAM中物理内存页的地址。 如果有效位为0,则表示物理内存页已分配但缓存未命中(2)。
上述页表一般是缓存在L1 cache 中的,而MMU到 L1 cache 所需的指令周期也很长,所以MMU自己也做了一个小缓存,叫做翻译后备缓冲器TLB((translation Lookaside buffer)就是放一些比较常用的在身边方便找。当MMU需要将虚拟内存地址转换为DRAM中的内存地址时,此时先查TLB,如果缓存命中直接就得到了DRAM中的地址,否则就需要到页表中去查。
缺页异常
MMU查询页表,发现该虚拟页在DRAN中没有缓存,此时会触发缺页异常。此时程序的控制权转移给一个缺页异常程序,它所作的事情,简单来说就是从DRAM里面挑出一个暂时不用的页,把它写入磁盘,同时把我们所需要的页从磁盘中复制到DRAM里面。
由于程序的局部性原理,进程总是趋向于在一个较小的活动页面集合上工作,这个工作集合或者说常驻集合,在第一次访问的时候被 cache 到 DRAM 中之后,后续都会命中缓存。说白了就是DRAM命中率很高,很少会触发缺页异常,所以不用担心程序的效率问题。
页表(再提一嘴)
前面说到不同进程之间的越级访问问题,那么页表是如何解决这个问题的呢?
比如下图图中页表添加了3个许可位。SUP表示进程是否必须运行在内核模式才能访问该页。SUP为1 的页在用户态是无法访问的。同时还有读权限和写权限。当某个指令进行越权访问时,CPU就会触发一个段错误。
一般来说,页表可以实现以下的功能:
1.每个进程的代码段所在页是不可修改的
2.内核的代码和数据结构所在页也是不可修改的
3.进程不能读写其他进程的私有内存页
4.进程间通信可以通过设置进程间共享页来实现。即允许多个进程对某一页进行读写。
多级页表 (进一步优化)
我们做个计算,页表有多大,一页4KB,虚拟内存空间是4GB,也就是有4GB/4KB = 10^6 个页,假设一个 PTE 大小是4个字节,页表的大小也达到了 4GB/4KB* 4Byte = 4MB。由于页表是缓存在 L1 里的,4MB可不是个小数目。而且如果是64 位系统,虚拟内存空间是128 TB ,页表的大小将指数增长。
为了节省内存空间,页表可以采用多级结构。多级页表将整个虚拟地址空间划分为多个层级,每个层级都有自己的页表。通过多级页表,可以减少页表的大小,提高内存利用率。
写在最后
大概先这些,后续有补充再更新
图源自网络,如侵删