我想知道写时复制是什么以及它的用途。这个术语在Sun JDK教程中被提到了多次。
我想知道写时复制是什么以及它的用途。这个术语在Sun JDK教程中被提到了多次。
我原本想写自己的解释,但这篇维基百科文章已经总结得很好了。
以下是基本概念:
“Copy-on-write”(有时称为“COW”)是计算机编程中使用的一种优化策略。其核心思想是,如果多个调用者请求最初不可区分的资源,您可以将它们指向同一个资源的指针。此函数可以保持不变,直到调用者尝试修改其资源“副本”,此时将创建真正的私有副本,以防止更改对其他人可见。所有这些都对调用者透明。主要优点是,如果调用者从不进行任何修改,则永远不需要创建私有副本。
以下是常见COW应用程序之一:
COW概念也用于数据库服务器(如Microsoft SQL Server 2005)上即时快照的维护。当底层数据更新时,即时快照通过存储数据的预修改副本来保留数据库的静态视图。即时快照用于测试或时序相关报告,并不应用于替换备份。
A
的数据块。进程1、2、3、4都想要复制它并开始读取它。在“写时复制”系统中,尚未复制任何内容,但所有内容仍在读取A。现在,进程3想要更改其对A
的副本,进程3将实际上创建A
的一个副本并创建一个名为B
的新数据块。进程1、2、4仍在读取A
块,而进程3现在正在读取B
块。 - PuddlerA
,都应该创建一个新的副本。如果您问的是如果出现完全新的进程并更改 A
,那么我的解释就不够详细了。这将是具体实现相关的,并需要了解您希望其余实现如何工作的知识,例如文件/数据锁定等。 - Puddlervfork
并没有使用 COW。实际上,如果子进程进行写操作,可能会导致不确定的行为和页面未被复制!事实上,可以说相反的情况有一定的真实性。当共享空间被修改时,COW 的行为类似于 vfork
。 - Pavan Manjunath举个例子,Mercurial使用写时复制来使克隆本地仓库变得十分“便宜”。
原理与其他的例子相同,只是这里所说的是物理文件而不是内存中的对象。最初,克隆并不是副本,而是指向原始文件的硬链接。当你在克隆中更改文件时,会写入副本以表示新版本。
由Erich Gamma等人编写的设计模式:可复用面向对象软件的基础一书,清晰地描述了写时复制优化(章节“代理”中的“后果”部分):
代理模式在访问对象时引入了一层间接性。这种额外的间接性有很多用途,取决于代理的类型:代理模式的对象图:
首先我们定义主题的接口:
import abc
class Subject(abc.ABC):
@abc.abstractmethod
def clone(self):
raise NotImplementedError
@abc.abstractmethod
def read(self):
raise NotImplementedError
@abc.abstractmethod
def write(self, data):
raise NotImplementedError
接下来,我们定义实现主题接口的真正主题:
import copy
class RealSubject(Subject):
def __init__(self, data):
self.data = data
def clone(self):
return copy.deepcopy(self)
def read(self):
return self.data
def write(self, data):
self.data = data
class Proxy(Subject):
def __init__(self, subject):
self.subject = subject
try:
self.subject.counter += 1
except AttributeError:
self.subject.counter = 1
def clone(self):
return Proxy(self.subject) # attribute sharing (shallow copy)
def read(self):
return self.subject.read()
def write(self, data):
if self.subject.counter > 1:
self.subject.counter -= 1
self.subject = self.subject.clone() # attribute copying (deep copy)
self.subject.counter = 1
self.subject.write(data)
if __name__ == '__main__':
x = Proxy(RealSubject('foo'))
x.write('bar')
y = x.clone() # the real subject is shared instead of being copied
print(x.read(), y.read()) # bar bar
assert x.subject is y.subject
x.write('baz') # the real subject is copied on write because it was shared
print(x.read(), y.read()) # baz bar
assert x.subject is not y.subject
我发现this一篇很好的关于 PHP 中 zval 的文章,其中提到了 COW:
Copy On Write(缩写为“COW”)是一种旨在节省内存的技巧。 它更普遍地用于软件工程。 这意味着当您写入符号时,如果该符号已经指向 zval,则 PHP 将复制内存(或分配新的内存区域)。
这是一个内存保护的概念。在这种情况下,编译器创建额外的副本来修改子级中的数据,而这些更新后的数据不会反映在父级数据中。
它也被用于 Ruby 的“企业版”中,作为一种节省内存的巧妙方式。
clone()
实现fork()
时会使用它——子进程会复制父进程的内存,采用COW(写时复制)技术。 - Kerrek SB