《操作系统概念精要》之内存篇(四)-虚拟内存

经过以前的学习,了解到了内存的管理策略。不管是分页,还是分段,或者是页表的管理策略。这些都有一个共同的目标:将多个进程都保存在内存中,这一操作,都是为了保证在进程启动时,进程处于内存中。
但是虚拟内存技术允许执行进程不必完全处于内存。这就使得程序可以完全大于物理内存,从而在开发的过程中,程序员不用担心内存的限制。

基本概念

虚拟内存:虚拟内存可以是一种逻辑上扩充物理内存的技术。它会把进程的逻辑地址空间进行扩展。(书中说的虚拟内存,可以看做是假象的逻辑内存,虽然在平常使用top 命令看到了虚拟内存表示一些具体的物理实体,但是而书上一直用虚拟内存这个词个人认为不是很好 )。
虚拟地址空间: 其实这里的虚拟地址空间 感觉还是可以用逻辑地址空间的概念来代替。它表示进程在内存中的逻辑地址范围。

请求调页

我们在把进程加载到内存的过程中, 并不是把进程的所有执行代码都加载到内存,而是在需要执行的时候才加载进内存,这种技术叫请求调页,常常用于虚拟内存系统。
对于请求页的逻辑内存,页面只有在程序执行期间被请求时才被加载。因此,从未访问的那些页从不加载到物理内存中。而这种交换的方式也被称为 惰性交换

基本执行过程:当换入进程时,调页程序会猜测在该进程被再次换出之前,会用到哪些页。调页程序不是调入整个进程,而是把哪些要使用的页调入内存。从而减少交换时间,而且还能避免物理内存空间的浪费。
具体实现:在使用这种方案的时候,需要一定的硬件支持。以区分内存的页面和磁盘的页面。通常在页面上会有一个保护位,它的最后一位就是用于提供这个功能的。这个位是有效-无效位
当这个位被置为有效位时, 相关的页面是合法的,并且在内存中。
当这个位被置为无效位时,页面无效或者是有效但是在磁盘上。

有效-无效位的页表.png

当进程在执行过程中的时候,如果访问内存驻留的页面时,会一切顺利。但是当访问到尚未调入的页面的时候,对,标记为无效的页面会产生缺页错误。分页硬件在通过页表转换地址时,会发现无效位被设置,从而陷入操作系统。
一般缺页错误的处理很简单:

  1. 检查这个进程的内部表,以确认该引用是有效的还是无效的内存访问。
  2. 如果引用无效,那么终止进程。如果引用有效但是没有调入内存,那么现在就调入。
  3. 找到一个空闲帧。
  4. 调度一个磁盘操作,以将所需页面读到刚分配的帧。
  5. 磁盘读取完成,修改进程内部表和页表。以指示该页现在处于内存中。
  6. 重新启动被陷入中断的指令。该进程能访问所需要的页面,就好像原来他就在内存中一样。
缺页中断处理.png

交换空间

在进程执行过程中,需要一个辅助设备,用于保存不在内存中的那么页面。这种外存通常为高速硬盘,称为交换设备,用于交换的这部分磁盘叫做交换空间
请求调页的中使用的交换空间的磁盘I/O 通常要快于文件系统的。交换空间的文件系统更快,因为它是按更大的块来分配的。并且不采用文件查找和间接分配方法。
因此,系统可以在进程启动时,将整个文件映像复制到交换空间,然后从交换空间执行请求调页,从而获取到好的分页吞吐量。
另一个选择是开始时在文件系统进行请求调页,但是在置换页面时将换出的页面写入交换空间。这保证了后续的调页都是从交换空间完成的。

写时复制

在父进程调用fork()创建子进程时后,我们曾说子进程应该创建一个父进程地址空间的副本,复制属于父进程的页面。然而考虑到了许多子进程在创建之后立马执行了exec()函数替换了,所以复制父进程的页面可能没有必要。因此我们采用了写时复制技术。他通过允许父进程和子进程最初共享相同的页面来进行工作。这些页面是共享页面,被标记为写时复制,这意味着任何进程写入共享页面,那么就创建一个共享页面的副本。

进程1修改页面C之前.png
进程1修改页面C之后.png

可以看到使用写时复制技术,仅复制任何一个进程修改的页面,所有未修改的页面可以由父进程和子进程共享。
还要注意,只有可以修改的页面才需要标记写时复制。不能修改的页面可以父子共享(比如代码段)。

在实现上,许多操作系统都为这类请求提供了一个空闲的页面池(和个人认为和内存池差不多)。当进程的堆栈或者堆要进行扩展时,或者有写时复制的请求时,通常分配这里的空闲页面。操作系统分配这些页面通常采用按需填0的技术。以清理原来的内存。
Linux 提供了 fork()的变种,vfork()。vfork()的操作不同于写时复制的fork()。它会将父进程挂起,子进程使用父进程的地址空间。
由于vfork()不会发生写时复制,所以他的修改的页面在父进程中是可以看到的。当子进程在创建后立即会被exec()的情况下,可以使用vfork()。因为他们有复制页面,可以高效的启动新进程。

你可能感兴趣的:(《操作系统概念精要》之内存篇(四)-虚拟内存)