Copy-On-Write 策略

操作系统在分配内存时,有一个很重要的策略叫写时复制(Copy-On-Write,COW),在现实情况中,内存通常是不够应用程序分配的,应用程序通常会申请超过自己需求的内存。在操作系统内部,COW 对进程 fork 也有加速作用。

我们先从操作系统的 fork 操作来理解。当一个进程进行 fork 时,需要将父进程的内存复制到子进程。但实际情况是父进程中大部分的数据,在子进程中只需要读,而不会进行写,这部分的数据就变得可以共享,没有必要进行复制。

所以 COW 的策略是,暂时将所有数据进行共享,当子进程要修改某一部分的数据时,就将这部分数据进行复制,然后再修改。所以只有读的数据是共享的,需要写的部分则复制两份,这样就很好地提高了内存利用效率,避免了冗余数据。

在操作系统的具体实现中,是通过页表异常来实现的。父进程和子进程通过页表权限和页面异常来实现共享物理内存,我们知道进程有自己的虚拟空间,靠页表来映射到一个实际的物理空间。

  • 当进行 fork 时,父进程和子进程共享所有的物理页面,也就是同样的虚拟地址映射到同样的物理地址,但是此时的页表映射都为只读,父进程和子进程可以从页表中读取数据
  • 当其中任何一个向页表写入时,就会触发页面异常而发生中断。此时操作系统的内核会捕获该异常,并为该页面分配新的物理内存,重新设置映射,并允许写入,随后恢复进程

为了进一步支持 COW 策略,还需要对每个物理页表记录一个引用数,记录多少虚拟页表映射到该物理页表,只有其引用数为 0 时,才可以真正释放物理页表的内存。

在应用程序中,也可以被叫做延迟分配。当应用程序申请分配内存时,并不会立刻分配物理内存,创建的页表会被标记为无效。当应用程序对该页表进行读写时,会发生页表无效的中断,此时内核才会真正分配物理内存,并构建映射。这样的策略将分配内存的时间均摊到不同时刻,否则可能程序加载需要大量时间,而且占用大量的物理内存。

同样,类似的策略可以应用在硬盘数据的读写中。从硬盘读写数据对于 CPU 和内存来说耗时都是非常久的,而且硬盘的数据通常都较大,所以这种应用如果在启动时就要将数据全部加载到内存,这种启动成本太大。用户可能需要很长时间才能看到程序响应,或者运行几个程序就耗尽内存了。在这种情况下,也可以通过分配无效的页表,当需要访问时发生异常,由内核真正进行读取并分配物理内存。

由于物理内存通常较少,很多操作系统为了有效地支持多任务以及有效地使用硬件,许多正在运行的程序所使用的物理内存会被释放,转而存储在硬盘中,当需要使用时再加载回来。这种情况下,类似的策略也可以很好地使用,从而支持在小量的物理内存上运行许多应用程序。

上一篇 下一篇

评论 | 0条评论