虚拟内存

来源:互联网 发布:js监听按钮点击事件 编辑:程序博客网 时间:2024/06/14 04:33

小结:


虚拟内存 VM:是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互。


虚拟内存是对主存的一个抽象。支持虚拟内存的处理器通过使用一种叫做虚拟寻址的间接形式来引用主存。处理器产生一个虚拟地址,在呗发送到主存之前,会被翻译成一个物理地址,这个地址翻译需要硬件和软件结合。专门的硬件通过使用页表来翻译虚拟地址,而页表的内容是由操作系统提供的。


三个能力:

1、将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域、并根据需要在磁盘和主存之间来回传递数据

它在主存中自动缓存最近使用的存放在磁盘上的虚拟地址空间的内容。

2、简化内存管理,从而简化了链接和加载、代码和数据共享,以及应用程序的内存分配。。

3、通过在每条页表条目中加入保护位,从而简化了内存保护。


大多数页表条目位于L1高速缓存中,但一个称为TLB的页表条目的片上的高速缓存,通常会消除访问在L1上的页表条目的开销。


现代系统通过将虚拟内存片和磁盘的文佳片关联起来,来初始化虚拟内存片,这个过程称为内存映射。


寻址:


物理寻址:CPU直接读取内存


虚拟寻址:需要内存管理单元(MMU)来做内存翻译,将虚拟地址翻译为物理地址,再读取


地址空间:

允许每个数据对象有多个独立的地址,其中每个地址都有一个不同的地址空间。这就是虚拟内存的基本思想。

主存中的每字节都有一个选自虚拟地址空间的虚拟地址和选自物理地址空间的物理地址。



虚拟内存作为缓存的工具


每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。操作系统负责维护页表的内容,以及在磁盘与DRAM之间来回传送页。

页表是常驻在内存中(DRAM)


页命中:CPU读取虚拟内存中一个字时

1、虚拟地址作为一个索引,页表中这个索引位置上的有效位是1,说明已经在内存中。

2、那么对应页表中响应的物理地址,就可以去内存中找了。


不命中(缺页):CPU读取虚拟内存中一个字时

1、虚拟地址作为一个索引,页表中这个索引位置上的有效位是0,说明没在内存中。触发缺页异常。

2、选择一个牺牲页(可以理解为 在内存中选择一个字节,放回硬盘中,然后把这个字节改为当前搜索的字。)

3、页表也要修改,当前有效位设为1,虚拟内存指向物理内存;而被放回硬盘的字节的有效位设为0,指向硬盘中的虚拟内存。


分配页面:程序调用malloc

VP5(虚拟页面5)的分配过程是在硬盘上创建空间并更新PTE5(有效位与指针),使它指向硬盘上这个新创建的页面。


虚拟内存作为内存管理的工具


操作系统为每个进程提供了一个独立的页表,因而也就是一个独立的虚拟地址空间。

注意:多个虚拟页面可以映射到同一个共享物理页面上,比如说调用公用的函数。


VM可以简化链接和加载、代码和数据共享,以及应用程序的内存分配。


简化内存分配:在分配内存时,操作系统分配k个连续的虚拟内存页面,并将他们映射到物理内存中任意位置的k个任意的物理页面。

页面可以随机分散在物理内存中。


虚拟内存作为内存保护的工具


通过在PTE(页表)上添加一些额外的许可位来控制对一个虚拟页面内容的访问。



地址翻译


地址翻译过程

虚拟地址中包含虚拟页号(VPN)和虚拟页偏移量(VPO)

MMU利用VPN来选择适当的PTE。

将页表条目(PTE)中的物理页号和虚拟地址中的VPO串联起来,就得到响应的物理地址。


当页面命中时,CPU硬件执行步骤

1、处理器生成一个虚拟地址,并把它传给MMU

2、MMU生成PTE地址,并从高速缓存/主存请求得到它(这一步是因为页表是在内存中)

3、高速缓存/主存向MMU返回PTE

4、MMU构造物理地址,并把它传送给高速缓存/主存

5、高速缓存/主存返回所请求的数据给处理器


缺页要求硬盘和操作系统内核协作完成

1、处理器生成一个虚拟地址,并把它传给MMU

2、MMU生成PTE地址,并从高速缓存/主存请求得到它(这一步是因为页表是在内存中)

3、高速缓存/主存向MMU返回PTE

4、PTE有效位是0,MMU出发一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。

5、缺页异常处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到硬盘。

6、缺页异常处理程序页面调入新的页面,并更新内存中的PTE。

7、缺页异常处理程序返回到原来的进程中,再次执行指令,这次就回命中。


高速缓存可以和虚拟内存结合起来,主要思路是地址翻译发生在高速缓存查找之前。


利用TLB加速地址翻译:TLB是小的、虚拟寻址的缓存,每一行都保存着一个由单个PTE组成的块。

1、CPU产生一个虚拟地址

2、3、MMU从TLB中取出相应PTE

4、MMU将这个虚拟地址翻译成物理地址,并发送到高速缓存/主存。

5、高速缓存/主存返回所请求的数据给处理器

注:当TLB不命中,MMU必须从L1缓存中读取相应的PTE。



多级页表

用来压缩页表的常用方法是使用层次结构的页表。

这种方法从两个方面减少了内存的要求:

1、如果一级页表中的一个PTE为空,那么对应的二级页表就不存在。

2、只有一级页表才需要总是存在主存中。



内存映射


内存映射:Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容


虚拟内存区域可以映射到两种类型的对象中的一种:

1、linux文件系统中的普通文件(比如可执行目标文件):文件区被分为页大小的片,每一片包含一个虚拟页面的初始内容。因为按需进行页面调度,所以这些虚拟页面没有实际交换进入物理内存,直到CPU第一次引用到页面。


2、匿名文件(内核创建,全是二进制零):CPU第一次引用这样一个区域内的虚拟页面时,内核在物理内存找一个牺牲页,如果该页面被修改过,就换出来,用二进制零覆盖,将这个页面标记为驻留在内存中的。


一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件(交换空间)之间换来换去。


内存映射概念来源:如果虚拟内存系统可以集成到传统的文件系统中,那么就能提供一种简单而高效的把程序和数据加载到内存的方法!


共享对象

一个对象可以被映射到虚拟内存的一个区域,要么共享对象,要么私有对象。

共享对象就是说不同进程的虚拟内存指向同一个物理内存(这样就能节省内存空间),就是共享这个对象。


共享对象的修改对于其他进程都是可见的。映射到共享对象的虚拟内存区域叫做共享区域。类似还有私有区域。



私有对象

使用一种叫 写时复制 的技术被映射到虚拟内存中。意思就是,只有读的时候,物理内存中只有一份,而有一个进程要写的时候,就在物理内存中创建这个页面一个副本来修改,更新的进程页表也指向新的这个副本,充分使用内存。



动态内存分配:


动态内存分配器 维护一个进程的虚拟内存区域(堆heap)堆里面是块(虚拟内存片)的集合,要么是已分配的,要么是空闲的。


为何用动态内存分配:直到程序运行时,才知道某些数据结构的大小。


显示分配器:程序员手动分配,手动回收

void *malloc(size_t size);   成功返回已分配块的指针


碎片:

堆利用率很低的主要原因,当虽然有未使用的内存但不能用来满足分配请求时。


内部碎片:已分配块比有效载荷大时发生的。

外部碎片:空闲内存合计够,但是不是单独的空闲块。


垃圾收集:


垃圾收集器是一种动态内存分配器,自动释放程序不再需要的已分配块(垃圾)。


垃圾收集器将内存视为一张 有向可达图,不可到达的堆节点为垃圾,自动释放。



C程序中常见内存相关错误


1、间接引用坏指针:


scanf("%d", val);   错!


2、读未初始化内存


3、允许栈缓冲区溢出


4、假设指针和它们指向的对象是相同大小的


5、造成位错误


6、引用指针,而不是它所指向的对象












原创粉丝点击