Я хотел бы знать, что такое копирование при записи и для чего оно используется. Этот термин несколько раз упоминается в руководствах по Sun JDK.
Что такое копирование при записи?
Ответы (9)
Я собирался написать собственное объяснение, но эта статья в Википедии в значительной степени подводит итог вверх.
Вот основная концепция:
Копирование при записи (иногда называемое «COW») — это стратегия оптимизации, используемая в компьютерном программировании. Основная идея заключается в том, что если несколько вызывающих объектов запрашивают ресурсы, которые изначально неразличимы, вы можете дать им указатели на один и тот же ресурс. Эта функция может поддерживаться до тех пор, пока вызывающая сторона не попытается изменить свою «копию» ресурса, после чего создается настоящая частная копия, чтобы изменения не стали видны всем остальным. Все это происходит прозрачно для вызывающих абонентов. Основное преимущество заключается в том, что если вызывающий объект никогда не вносит никаких изменений, никакой частной копии создавать не нужно.
Также вот приложение общего использования COW:
Концепция COW также используется при обслуживании мгновенных снимков на серверах баз данных, таких как Microsoft SQL Server 2005. Мгновенные снимки сохраняют статическое представление базы данных, сохраняя копию данных до модификации при обновлении нижележащих данных. Мгновенные моментальные снимки используются для тестирования использования или отчетов, зависящих от момента, и не должны использоваться для замены резервных копий.
clone()
для реализации fork()
- память родительского процесса используется для дочернего процесса.
- person Kerrek SB; 12.01.2012
«Копирование при записи» означает примерно то, на что это похоже: у всех есть одна общая копия одних и тех же данных до тех пор, пока они не будут записаны, а затем будет создана копия. Обычно копирование при записи используется для решения проблем параллелизма. В ZFS, например, блоки данных на диске выделяются методом копирования при записи; пока нет изменений, вы сохраняете исходные блоки; изменение изменило только затронутые блоки. Это означает, что выделяется минимальное количество новых блоков.
Эти изменения также обычно реализуются как транзакционные, т. е. они имеют ACID а> свойства. Это устраняет некоторые проблемы параллелизма, поскольку гарантирует, что все обновления являются атомарными.
A
. Каждый из процессов 1
, 2
, 3
, 4
хочет сделать его копию и начать чтение, в системе копирования при записи ничего не копируется, но все еще читается A
. Теперь процесс 3
хочет внести изменения в свою копию A
, процесс 3
фактически сделает копию A
и создаст новый блок данных с именем B
. Процесс 1
, 2
, 4
все еще читает блок A
, процесс 3
сейчас читает B
.
- person Puddler; 10.05.2016
A
, он должен создавать новую копию. Если вы спрашиваете, что произойдет, если появится совершенно новый процесс и изменит A
, тогда мое объяснение не будет вдаваться в подробности. Это будет зависеть от реализации и потребует знаний о том, как вы хотите, чтобы остальная часть реализации работала, например, блокировка файлов\данных и т. д.
- person Puddler; 11.07.2016
Я не буду повторять тот же ответ на Copy-on-Write. Я думаю, что ответ Эндрю и Чарли answer уже ясно дал понять. Я приведу вам пример из мира ОС, просто чтобы отметить, насколько широко используется эта концепция.
Мы можем использовать fork()
или vfork()
для создания нового процесса. vfork следует концепции копирования при записи. Например, дочерний процесс, созданный vfork, будет делиться данными и сегментом кода с родительским процессом. Это ускоряет время разветвления. Ожидается, что будет использоваться vfork, если вы выполняете exec, а затем vfork. Таким образом, vfork создаст дочерний процесс, который будет делиться данными и сегментом кода со своим родителем, но когда мы вызовем exec, он загрузит образ нового исполняемого файла в адресное пространство дочернего процесса.
vfork
НЕ использует COW. Ведь если ребенок что-то напишет, это может привести к неопределенному поведению, а не к копированию страниц!! На самом деле, можно сказать, что верно и обратное. COW действует как vfork
, пока что-то не будет изменено в общем пространстве!
- person Pavan Manjunath; 05.03.2012
Чтобы привести еще один пример, Mercurial использует копирование при записи, чтобы сделать клонирование локальных репозиториев действительно «дешевой» операцией.
Принцип тот же, что и в других примерах, за исключением того, что вы говорите о физических файлах, а не об объектах в памяти. Изначально клон — это не дубликат, а жесткая ссылка на оригинал. Когда вы изменяете файлы в клоне, копии записываются для представления новой версии.
Книга Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения автора Эрих Гамма и др. ясно описывает оптимизацию копирования при записи (раздел «Последствия», глава «Прокси»):
Шаблон Proxy вводит уровень косвенности при доступе к объекту. Дополнительная косвенность имеет множество применений, в зависимости от типа прокси:
- Удаленный прокси может скрыть тот факт, что объект находится в другом адресном пространстве.
- Виртуальный прокси может выполнять оптимизацию, например создавать объект по запросу.
- Как прокси-серверы защиты, так и смарт-ссылки позволяют выполнять дополнительные служебные задачи при доступе к объекту.
Есть еще одна оптимизация, которую шаблон Proxy может скрыть от клиента. Это называется копирование при записи и связано с созданием по запросу. Копирование большого и сложного объекта может быть дорогостоящей операцией. Если копия никогда не изменялась, то нет необходимости нести эти затраты. Используя прокси для отсрочки процесса копирования, мы гарантируем, что заплатим за копирование объекта только в том случае, если он будет изменен.
Чтобы копирование при записи работало, ссылка на тему должна учитываться. Копирование прокси не сделает ничего, кроме увеличения этого счетчика ссылок. Только когда клиент запрашивает операцию, изменяющую субъект, прокси фактически копирует его. В этом случае прокси также должен уменьшить счетчик ссылок субъекта. Когда счетчик ссылок обнуляется, тема удаляется.
Копирование при записи может значительно снизить стоимость копирования тяжеловесных объектов.
Ниже приведена реализация оптимизации копирования при записи на Python с использованием шаблона прокси. Целью этого шаблона проектирования является предоставление суррогата другому объекту для управления доступом к нему.
Диаграмма классов шаблона Proxy:
Диаграмма объекта паттерна Proxy:
Сначала определим интерфейс субъекта:
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
Я нашел эту хорошую статью о zval в PHP, в которой упоминается COW слишком:
Копирование при записи (сокращенно «COW») — это трюк, предназначенный для экономии памяти. Он используется более широко в разработке программного обеспечения. Это означает, что PHP будет копировать память (или выделять новую область памяти) при записи в символ, если этот уже указывал на zval.
Он также используется в Ruby 'Enterprise Edition' как удобный способ экономии памяти.
Хорошим примером является Git, который использует стратегию для хранения BLOB-объектов. Почему он использует хэши? Отчасти потому, что с ними проще выполнять сравнения, но также и потому, что упрощается оптимизация стратегии COW. Когда вы делаете новую фиксацию с несколькими изменениями файлов, подавляющее большинство объектов и деревьев не изменится. Таким образом, фиксация через различные указатели, состоящие из хэшей, будет ссылаться на группу уже существующих объектов, что значительно уменьшит объем памяти, необходимый для хранения всей истории.
Это концепция защиты памяти. В этом компиляторе создается дополнительная копия для изменения данных в дочернем элементе, и эти обновленные данные не отражаются в родительских данных.