CSAPP——第九章 虚拟内存

几个概念

  1. 程序
    存储在磁盘上的文件,在执行的时候加载如内存
  2. 内存
    分为DRAM,和SRAM
    DRAM:可以就看成我们买的内存条
    SRAM:可以就看是CPU中的一二三级缓存

虚拟内存

好处:

  1. 将主存(DRAM)进行抽象,通过分页机制,实现:主存中保存活动的区域,并根据需要(缺页异常和正常存取)在主存之间传送数据。高效的使用了主存
  2. 为进城提供一直的地址空间,简化内存管理,和程序的链接
  3. 保护每个进城的地址空间不被其他进程破坏。

程序和DRAM

DRAM可以看做是一个M个连续字节大小的数组,每个字节都有唯一的地址:物理地址。
CPU直接访问DRAM使用物理地址的方式成为:物理寻址。
早起程序使用物理寻址,但是目前大多使用虚拟寻址。目前仍有一些特殊的机器使用物理寻址。

虚拟寻址

指的是:CPU访问DRAM的地址不是物理地址,称为虚拟地址。需要由CPU中的内存管理单元(MMU)将虚拟地址转换为物理地址。然后MMU利用转换得到的物理地址发给DRAM,DRAM将数据直接送给CPU。

CSAPP——第九章 虚拟内存_第1张图片
虚拟寻址主存

虚拟内存VM

在物理内存上面抽象出的一层成为虚拟内存,主要是用来管理物理内存的。
虚拟内存对应一个虚拟地址空间,物理内存对应一个物理地址空间。也就是两个M字节的连续数组。分别两种寻址方式。
其中虚拟内存中的某个地址对应物理内存中的某个地址,可以使一对多的关系。

虚拟页

VM将内存又划分为很多的小块,每块大4K,不固定,可变,以后用P表示一个页的大小,每个单位成为虚拟页
同时物理内存页被分页,每个单位成为页帧
虚拟内存通过分页机制达到将将程序的活动部分加载进内存。

可以理解为,将程序也按照每块P大小分割,并不全部将程序加载进内存,而是在需要那一页的时候,将这一页加载进内存。(这里涉及到缺页异常)

每个页可能有如下三种不想交的状态,每个时刻只能有一种:

  1. 未分配
    指的是,VM中的一页,并没有被使用。所以这一虚拟页没有对应的页帧(物理内存的一页)。
  2. 未缓存
    指的是,虚拟页被内存使用,相当于一个变量被声明。但是还没有定义。所以,只是在VM中占用,但是还没有分配页帧。(没有分配页帧的意思是,对应的值没有加载进物理内存)
  3. 以缓存
    值得是,VM中的虚拟页,已经连接到页帧,同时也从磁盘中读取数据存储在对应的页帧。
CSAPP——第九章 虚拟内存_第2张图片
内存做缓存

图上说是虚拟页存储在磁盘上?大哥,主存是缓存,虚拟页怎么可能比缓存慢。

页表条目(Page Table Entry)维护了虚拟页到页帧的转换,由操作系统维护。
页表条目存储在物理内存中。
页表条目每一项由:有效位,和物理页号组成。
有效位指的是:1该虚拟页已经指向页帧(以缓存),0该虚拟页没有被使用或者已使用但是未指向页帧(未分配和未缓存)。
物理页号,指的是虚拟页指向的页帧地址。

CSAPP——第九章 虚拟内存_第3张图片
虚拟页->页帧

指向物理内存的是以缓存,指向VP的是未缓存,NULL的代表未分配。

除了有效位以外,页表中还可以存储其他的信息,比如是否可读之类的。

CSAPP——第九章 虚拟内存_第4张图片

页命中

命中可以理解成,当CPU取数据的时候,能够直接主存中获取,成为命中。如果数据还没有存进主存成为未命中。
在虚拟内存中,DRAM缓存不命中成为缺页。

当CPU读取数据的时候回发生两种情况:

1. 虚拟地址指向的表项PTE中有效位为0

可能是未缓存或者是未分配,这时候触发一个缺页异常!
linux下,MMU试图翻译一个虚拟地址A时,触发一个缺页。这个异常导致控制转移到内核的缺页处理程序::

  1. 确认A地址是合法。
    缺页处理程序搜索区域结构的链表,把A和每个区域结构中的vm_startvm_end做比较。如果指令不合法,发生一个段错误segmatation fault,结束程序。
    因为A地址可能是个函数,或是堆上或是栈上数据,所有要每个都搜索。
  2. 地址合法的情况下,确认是否有权限
    也就是,进城是否又读写或执行该区域内页帧的权限。
  3. 权限合法的情况下,加载进主存
    当物理内存未满的情况下,可能是直接找个空闲的地方加载页帧。(不知道啊。书上都没写。可能是这样)
    否则,缺页处理程序在物理内存中选择一个牺牲页,然后,如果该牺牲也被修改过了,那么将页帧内容写会磁盘。然后将新的磁盘内容加载进页帧。同时更新页表。
    当缺页处理程序返回的时候,CPU重新启动引起缺页的指令,这时候A已经是以缓存的。正常读取。
    选择牺牲页,应该是又算法的,局部性原理。

其中在第一部搜索接受的时候,结构是这样的:

每个线程都有的结构

缺页之前:

CSAPP——第九章 虚拟内存_第5张图片
image.png

缺页处理程序返回之后:

CSAPP——第九章 虚拟内存_第6张图片
image.png

当我工作集超出了物理内存的大小,会产生抖动,页面将不断的换进换出。

虚拟页与共享库

当多个程序调用同一个共享库时,程序没必要为每一个程序加载一次共享库。
而是不同程序的虚拟页指向同一页帧。
产生了多对一的情形:
不同程序的虚拟页对应同一页帧

CSAPP——第九章 虚拟内存_第7张图片
共享页面

页面调度和虚拟地址空间的结合有点:

  1. 简化程序链接
    每个程序可以采用相同基本格式,从同一虚拟地址开始。
  2. 简化加载
    当程序加载进内存的时候,只需要分配虚拟页,而不必加载进页帧。当需要的时候,发生缺页异常,然后加载。按需加载。
  3. 简化共享
    方便共享库的加载。
  4. 简化内存分配
    在虚拟地址空间中连续的地址,在物理地址空间中不一定连续。
    也就是程序在堆栈中分配的地址,在物理空间中可能并不是连续的。

地址翻译过程

  1. 处理器生成虚拟地址传送给MMU
  2. MMU生成PTE地址,并从主存或高速缓存请求,去访问的是页表条目。应该是将整一项返回给MMU
  3. 高速缓存(查找页表条目)以后返回一项给MMU

命中情况下

  1. MMU根据返回的PTE(此时PTE的有效位为1)构造物理地址,再次发送给主存
  2. 主存直接读取数据,发送给CPU
CSAPP——第九章 虚拟内存_第8张图片
页面命中

缺页

  1. PTE有效位为0,MMU触发缺页异常。CPU中的控制转移到内核中的缺一处理程序
  2. 确定牺牲页,替换。缺页处理程序更新PTE
  3. 确立处理程序返回,CPU再次执行导致缺页的程序

TLB缓存

CPU每次请求,MMU都去查找一次过于浪费。
因此MMU中有一个翻译后背缓冲器(Tanslation Lookaside Buffer)
在上面的第2部时。变为:MMU去查找TLB,如果没查找到,再去主存中查找页表条目。返回后跟新TLB。

CSAPP——第九章 虚拟内存_第9张图片
TLB

页表分级

CSAPP——第九章 虚拟内存_第10张图片
image.png

暂时不是很懂。不过有点理解吧

二. linux虚拟内存系统

execve函数

调用execve函数以后执行以下步骤

  1. 删除已存在的用户区域
    删除当前进程虚拟地址中用户部分已存在的结构,.text,.data等
  2. 映射私有区域
    为因程序的代码,数据,bss和站区域创建新的结构。
    这些区域都是私有的,代码区和数据区映射对应文件的.text,.data。而.bss映射到匿名文件,其大小保存在要执行的文件中。
  3. 映射共享区域
    如果要执行的程序调用了共享库之类的,那么需要将这些库映射到用户虚拟空间中的共享区域。
  4. 设置程序计数器(PC)
    最后一件事情是设置进程上下文中的程序计数器,使其指向代码区域的入口点。

CSAPP——第九章 虚拟内存_第11张图片
加载器执行程序

图中请求二进制0的地方,为映射到匿名文件 mmap()

动态内存分配

分配器指的是new,delete,malloc,free这些。
分为显示分配器,如上面的四个,也就是需要用户手动释放
隐式分配器也就是垃圾收集器,比如java等语言。

malloc实现

#include 
void *malloc(size_t size);

返回一个void *指针,可以隐式转化为任意类型。
其分配的块总是对其的。32下为8的倍数,64位下是16的倍数。
malloc并不初始化其分配的内存,calloc初始化。
malloc的实现,基于mmap()sbrk()

#include 
void *sbrk(intptr_t incr);

以后补

你可能感兴趣的:(CSAPP——第九章 虚拟内存)