VM as a tool for caching
CMU213-CSAPP-Virtual-Memory-Concepts | GreenHatHGのBlog
理解这个概念,就是说,VM是看作是独立与CPU和主存之外的disk,然后主存看成是这个虚拟地址数列的cache (DRAM就是物理的主存)
重新用自己的话说一下概念,虚拟内存是长度为N的连续数组,保存在disk上
数组上的内容on disk 缓存在 physical memory (DRAM cache)上
那么disk和主存之间的传输单元是 一块一块的,虚拟内存叫虚拟页virtual page, 物理内存叫physical page.
虚拟内存有三种情况
unallocated (不占用disk的空间)
cached
uncached
概念
页表page table:就是一个字典,key是 PTE (page table entry, 就是虚拟地址页的index), value是物理地址页的index
CPU执行move, call, jump. ret 任何控制转移指令,都会生成一个虚拟地址,MMU内存管理单元会在页表中查找
页命中page hit: 指的是VM word在DRAM cahe hit
缺页/页缺失page fault: 指的是VM word不在物理内存上DRAM cache miss
遇到这种情况,硬件会触发异常(exception),然后操作系统 会转移控制权到内核的page fault 处理的代码块,从物理内存选择一个page被替换,这个叫做select a victim to be evicted, 这个被替换的叫做牺牲页
disk和内存传递块的过程 交换(swapping)或者页面调度(paging)
页面调入DRAM
页面调出DRAM
当有不命中发生时,才换入页面的策略叫做 按需页面调度(demanding paging)
CSAPP解释了,当时虚拟内存的概念提出来,高速缓存SRAM还没被提出来,因此很多概念是相似接近的。
当C语言执行malloc,实际发生了什么?实际上是在页表上,找到未分配的条目(页 的index),然后修改PTE5的状态由 unallocated改为uncached, 并没有搬到主存上
一般来说,程序具有良好的局部性,这套虚拟内存机制都可以工作得很好,但一旦你的工作需要的数据量大于内存可以放下的大小,就会出现抖动,不断驱逐的情况 thrashing 抖动,页面将不断地换进换出。
每个process都有自己的虚拟内存空间,简化了内存管理
内存分配有一套mapping方案,每个virtual page 可以map到物理页上
每个进程都有一个非常相似的虚拟地址空间,code和data都从相同的位置开始,
在物理内存的同一位置可以share给不同的进程,这就是动态共享库的原理,libc.so只需要在物理内存加载一次
理解虚拟内存后,回过头来看看链接和加载
- 链接:共享库很好理解,不同的进程有不同的虚拟地址空间,可以映射到通过物理地址,这就是共享啦
- 加载:假设我们要加载一个prog, 这个prog有代码和数据(.text .data) 加载到一个进程中。实际操作是这样的,操作系统的加载器未.text 和.data分配了各自的虚拟页(virtual page),把它们标记未无效的(就是未被缓存的 uncached), 因此,实际上加载器 不从磁盘到实际内存复制认为你和数据,在每个页被初次使用时,这个CPU指令用到这个内存位置,虚拟内存会自动按需自动调入相应的数据页.
Loading: execve allocate virtual page for .text and .data section, and create PTE maked as invalid. The .text and .data sections are copied page by page, on the demand of virtual memory system
对内存的保护,就是我们要访问一个地址,通过MMU查找虚拟地址的PTE,设置3个许可位,SUP内核(超级用户)才可以访问页,读写权限等,如果违反,CPU会触发保护故障,将控制转移传递给内存的异常处理程序,linux shell中,将这种异常报告称为段错误 segmentation fault
在 x86-64 系统上,指针和地址都是 64 位的,但是虚拟地址空间则是 48 位的,超过 48 位的 bit 要么全是 0 要么全是 1,这是 intel 制定的规则,高位全为 1 的地址为内核保留(地址指向内核中的代码或者数据),高位全为 0 的地址为用户代码保留。