Copy-On-Write Overview

随着阅历的增长,越发感慨在千差万别的软件中总能找到某些非常相通相似的思想、技术或者算法,比如 Copy On Write(写时复制) 便是资源管理方面的一种优化技术,广泛的应用于软件开发(如 Java 的 Copy On Write 容器)、虚拟内存管理(如进程的 fork、qemu-kvm 虚拟机镜像乃至 Docker 的 AUFS 文件系统等等。维基百科的定义如下:

Copy-on-write (COW), sometimes referred to as implicit sharing or shadowing, is a resource management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources. If a resource is duplicated but not modified, it is not necessary to create a new resource; the resource can be shared between the copy and the original. Modifications must still create a copy, hence the technique: the copy operation is deferred to the first write. By sharing resources in this way, it is possible to significantly reduce the resource consumption of unmodified copies, while adding a small overhead to resource-modifying operations.

通俗的解释,假定多方需要使用同一个资源时,没有必要为每一方都创建该资源的一个完整的副本,反而令多方共享这个资源,当某方需要修改资源的某处时,利用引用计数,把该处复制一个副本,再把跟新的内容写入该副本中,从而节省创建多个完整副本时带来的空间和时间上的开销。

                             Resource 

                             +-------+
                             |   1   |
                             +-------+
User A   ---->               |   2   |               <---- User B
                             +-------+          
                             |   3   |
                             +-------+                             

当 A 需更新 Block 1 时,A 新建 New 1 Block,并把新的内容写入该 Block,从而避免影响 B 的 Block 1。同理,当 B 需更新 Block 3 时,B 新建 New 3 Block,并把新的内容写入该 Block,避免影响 A 的 Block 3。

                             Resource 

                +-------+    +-------+
                | New 1 |    |   1   |
                +-------+    +-------+
User A   ---->               |   2   |               <---- User B
                             +-------+    +-------+          
A see: New 1, 2, 3           |   3   |    | New 3 |        B see: 1, 2, New 3
                             +-------+    +-------+

Example

C++ STL String Class

C ++ 曾在性能问题上被广泛地质疑指责过,为了提升性能,STL 的许多类采用了 Copy-On-Write 技术。Coolshell 博文 C++ STL string 的 Copy-On-Write 技术 详细的从 C++ 层面介绍改技术。例如:

#include <stdio.h>
#include <string>
using namespace std;
  
main()
{
    string str1 = "hello world";
    string str2 = str1;
 
    printf ("Sharing the memory:/n");
    printf ("/tstr1's address: %x/n", str1.c_str() );
    printf ("/tstr2's address: %x/n", str2.c_str() );
 
    str1[1]='q';
    str2[1]='w';
 
    printf ("After Copy-On-Write:/n");
    printf ("/tstr1's address: %x/n", str1.c_str() );
    printf ("/tstr2's address: %x/n", str2.c_str() );
}

运行结果如下:

> g++ -o stringTest stringTest.cpp
> ./stringTest
Sharing the memory:
        str1's address: 343be9
        str2's address: 343be9
After Copy-On-Write:
        str1's address: 3407a9
        str2's address: 343be9

Fork()

早期的 Unix 在实现 fork 系统调用时,并没有使用该技术,创建新进程的开销很大。出于效率考虑,Copy On Write 技术引入到进程中,fork 之后的父进程和子进程完全共享数据段、代码段、堆和栈等的完全副本,而且内核将共享的地址空间的访问权限改变为只读,如果父进程和紫金陈中的任何一个仕途修改这些区域,则内核职位修改区域的那块内存制作一个副本,通常是虚拟存储系统中的一“页”(引自 Unix 高级环境编程)。

Qcow2 image

Qcow2(QEMU Copy On Write) 是格式镜像是 qemu 模拟器支持的一种镜像,和普通的 Raw 格式镜像相比,具有以下优点:

  • 更小的空间占用
  • 支持 Copy On Write.
  • 支持多级快照
  • 支持压缩
  • 支持 AES

经过多次优化,Qcow2 镜像的性能已经逼近 Raw 格式的镜像,所以 OpenStack 的虚拟机多采用 Qcow2 镜像。

ide 驱动时的性能对比:

cache                      off         writethrough      writeback
Old qcow2 (0.10.5)         16:52 min   28:58 min         6:02 min
New qcow2 (0.11.0-rc1)     5:44 min    9:18 min          6:11 min
raw                        5:41 min    7:24 min          6:03 min

virtio 驱动时的性能对比:

cache                      off         writeback
Old qcow2 (0.10.5)         31:09 min   8:00 min
New qcow2 (0.11.0-rc1)     18:35 min   8:41 min
raw                        8:48 min    7:51 min

除了镜像格式以外,很多文件系统都使用了 Copy-On-Write 技术,以节省空间、提升效率和更好的支持快照。