虚拟内存笔记
阅读「深入理解操作系统」的虚拟存储器一章做个笔记。
在早期的操作计算机操作系统中,操作主存是CPU直接访问的物理地址。后面演进为通过VA(vatual address)来管理访问,这样做几个好处:
- 将主存(就是我们说的内存条)看作是磁盘的一高速缓存
- 为每个进程提供一致的访问空间,简化应用程序
- 每个进程独立的空间而不被其它进程破坏
下面这张从wiki上摘取的,从这张图可以看出VM屏蔽了处理了细节,给上层一个统一的,连续的地址空间。
cpu通过虚拟地址怎样访问到主存数据的呢?如下图,先把虚拟地址(va)翻译成物理地址(pa),这个工作是由MMU(memory management unit)完成,它是cpu芯片上的专用硬件,但是这个工作要和OS紧密配合才能完成,因为要在OS管理的Page Table来动态查询并翻译虚拟地址。后面会讲到详细的翻译过程。
页,页表?
页(page)是怎么来的?虚拟内存的本质设计思路就是把内存当作磁盘的高速缓存,每个进程都是独立的地址空间,可以分配2**64(64位操作系统)大小的虚拟空间,但是这些数据并不是长期驻留在内存中的,如果内存紧张时,某个活动的进程需求内存但是未分配缓存时,会牺牲其它某个进程的内存,把这些当前闲置的内存数据交换出(swapping或者paging)到磁盘上。当被交换出的数据在下次需求的时候,又从磁盘换入到内存中。这时不难理解,程序运行的过程中内存数据是有换入换出的,那么性能问题就会显现出来了。大家都知道读写磁盘数据的开销在哪里,就是寻道定位(特别是之前的旋转磁盘)读取第一个字节数据。所以就会以块为单位进行读取,可以理解成批量的意思,就是page。那页表(page table)又是什么呢?可以把页表理解成一个hasmap结构的数据体,key值就是虚拟地址,而value保存的信息是该虚拟地址当前的动态信息,包括是否进行分配,还是分配了但数据在磁盘上,或者数据已在内存上。这样MMU就可以结合这个页表数据把虚拟地址翻译成物理地址。可以查看前面wiki的那张图理解。
fork对内存操作
fork就是创建一个新的进程,子进程也会把父进程的地址空间全部复制一份过来。但是使用的是COW(copy on write)技术,只有有写操作时,当前被影响的内存页才会复制,只读的时候其实物理内存上的数据还是只有一份。可以思考下redis的RDB持久方式,就是通过fork一个子进程来同时进行save操作,如果在save的整个过程中数据没有写变,那么其实不会占用double的内存的。
mmap是什么?
我的浅显理解就是把一个fd(文件描述符,一般就是通过open函数打的一个本地磁盘本地文件)映射到当前进程的一段虚拟地址空间中,这样我们可以像操作内存一样操作文件。这样可以用来进行进程的内存共享通信,因为两个进程可以映射同一个文件,然后各自操作相应的虚拟地址空间。