Что такое копирование при записи?


134

Я хотел бы знать, что такое копирование при записи и для чего оно используется? Термин «массив копирования при записи» несколько раз упоминается в руководствах Sun JDK, но я не понял, что он означает.

Ответы:


156

Я собирался написать свое собственное объяснение, но эта статья в Википедии в значительной степени подводит итог.

Вот основная концепция:

Копирование при записи (иногда называемое «COW») - это стратегия оптимизации, используемая в компьютерном программировании. Фундаментальная идея состоит в том, что если несколько вызывающих запрашивают ресурсы, которые изначально неотличимы, вы можете дать им указатели на один и тот же ресурс. Эта функция может поддерживаться до тех пор, пока вызывающий объект не попытается изменить свою «копию» ресурса, после чего будет создана настоящая частная копия, чтобы изменения не стали видимыми для всех остальных. Все это происходит прозрачно для вызывающих абонентов. Основное преимущество состоит в том, что, если вызывающий объект никогда не вносит никаких изменений, не нужно создавать личную копию.

Также вот пример общего использования COW:

Концепция COW также используется при обслуживании мгновенных снимков на серверах баз данных, таких как Microsoft SQL Server 2005. Мгновенные снимки сохраняют статическое представление базы данных, сохраняя копию данных до модификации при обновлении лежащих в основе данных. Мгновенные снимки используются для тестирования или отчетов, зависящих от момента, и не должны использоваться для замены резервных копий.


все, для чего используется обычный массив ... однако в некоторых ситуациях этот тип стратегии дает более оптимизированные результаты.
Эндрю Фланаган,

3
@hhafez: Linux использует его, когда он использует clone()для реализации fork()- память родительского процесса ЗАКРЫВАЕТСЯ для дочернего.
Kerrek SB

@hhafez Некоторые файловые системы используют CoW, например, BTRFS .
Geremia

Так работает SandboxIE? когда изолированная программа хочет что-то перезаписать, песочница перехватывает операцию файловой системы и копирует файл в папку изолированной программной среды и позволяет программе писать в изолированный файл вместо оригинала. Это называется "Копировать при записи"?
Ронни Мэтьюз,

Как в конечном итоге происходит слияние? Если есть N копий, какая из них будет сохранена, скажем, на диске?
SimpleGuy

59

«Копировать при записи» означает более или менее то, на что это похоже: у всех есть одна общая копия одних и тех же данных до тех пор, пока они не будут записаны , а затем копия будет сделана. Обычно копирование при записи используется для решения проблем, связанных с параллелизмом. В ZFS , например, блоки данных на диске выделяются для копирования при записи; до тех пор, пока нет изменений, вы сохраняете исходные блоки; изменение изменило только затронутые блоки. Это означает, что выделяется минимальное количество новых блоков.

Эти изменения также обычно реализуются как транзакционные , т. Е. Они имеют свойства ACID . Это устраняет некоторые проблемы с параллелизмом, поскольку тогда вам гарантируется, что все обновления являются атомарными.


1
Если вы внесете изменения, как другой получит уведомление о вашей новой копии? Разве они не увидят неправильные данные.
Powder366

12
@ Powder366 - Нет, они не увидят неправильные данные, потому что когда вы вносите изменения, именно тогда создается копия. Например, у вас есть блок данных с именем A. Процесс 1, 2, 3, 4каждый хочет сделать копию и начать читать его, в «копировании при записи» система ничего не копируется пока все еще читают A. Теперь процесс 3хочет внести изменения в свою копию A, 3теперь процесс фактически сделает копию Aи создаст новый блок данных с именем B. Процесс 1, 2, 4все еще читает блок - Aпроцесс 3в настоящее время чтения B.
Puddler

1
@Puddler, что будет, если в 'A' внести изменения. Все процессы будут читать обновленную информацию или старую?
Разработчик

3
@Developer: Какой бы процесс ни вносил изменения, он Aдолжен создавать новую копию. Если вы спрашиваете, что произойдет, если возникнет и изменится совершенно новый процесс, Aтогда мое объяснение не содержит достаточно подробностей для этого. Это будет зависеть от конкретной реализации и потребует знаний о том, как вы хотите, чтобы остальная часть реализации работала, например о блокировке файлов / данных и т. Д.
Puddler

10

Я не буду повторять один и тот же ответ о копировании при записи. Я думаю , что ответ Эндрю и ответ Чарли уже очень ясно. Я приведу вам пример из мира ОС, просто чтобы отметить, насколько широко используется эта концепция.

Мы можем использовать fork()или vfork()создать новый процесс. vfork следует концепции копирования при записи. Например, дочерний процесс, созданный vfork, будет совместно использовать данные и сегмент кода с родительским процессом. Это ускоряет время разветвления. Ожидается, что будет использоваться vfork, если вы выполняете exec, а затем vfork. Таким образом, vfork создаст дочерний процесс, который будет совместно использовать данные и сегмент кода со своим родительским процессом, но когда мы вызываем exec, он загрузит образ нового исполняемого файла в адресное пространство дочернего процесса.


3
«vfork следует концепции копирования при записи». Пожалуйста, подумайте об изменении этой строки. vforkНЕ использует КОРОВУ. Фактически, если ребенок что-то напишет, это может привести к неопределенному поведению, а не к копированию страниц !! На самом деле, можно сказать, что это в некоторой степени верно. COW действует как будто vforkчто-то не модифицируется в общем пространстве!
Паван Манджунатх

Полностью согласен с Паваном. Удалите строки «vfork следует концепции копирования при записи». В наши дни COW используется в форке в качестве оптимизации, так что он действует как vfork и не копирует родительские данные для дочернего процесса (если мы вызываем только exec * в дочернем процессе)
Шекхар Кумар

7

Еще один пример: Mercurial использует копирование при записи, чтобы сделать клонирование локальных репозиториев действительно «дешевой» операцией.

Принцип такой же, как и в других примерах, за исключением того, что вы говорите о физических файлах, а не об объектах в памяти. Изначально клон - это не дубликат, а жесткая ссылка на оригинал. Когда вы изменяете файлы в клоне, создаются копии, представляющие новую версию.


2

Я нашел эту хорошую статью о zval в PHP, в которой также упоминается COW:

Копирование при записи (сокращенно COW) - это уловка, предназначенная для экономии памяти. Обычно он используется в разработке программного обеспечения. Это означает, что PHP будет копировать память (или выделять новую область памяти) при записи в символ, если этот уже указывал на zval.


0

Он также используется в Ruby «Enterprise Edition» как изящный способ экономии памяти.


2
Я не думаю, что он имел в виду «использованный для» в этом смысле.
spydon 05

0

Хорошим примером является Git, который использует стратегию хранения больших двоичных объектов. Почему он использует хеши? Отчасти потому, что с ними легче выполнять различия, но также потому, что упрощает оптимизацию стратегии COW. Когда вы делаете новую фиксацию с несколькими изменениями файлов, подавляющее большинство объектов и деревьев не изменится. Следовательно, фиксация через различные указатели, состоящие из хэшей, будет ссылаться на группу объектов, которые уже существуют, что значительно уменьшит пространство для хранения, необходимое для хранения всей истории.


0

Это концепция защиты памяти. В этом компиляторе создается дополнительная копия для изменения данных в дочернем элементе, и эти обновленные данные не отражаются в данных родителей.


0

Ниже представлена ​​реализация Python с копированием при записи (COW) с использованием шаблона проектирования декоратора . Ссылка на неизменяемый Valueобъект содержится в изменяемом CowValueобъекте (декораторе). CowValueОбъект передает все запросы на чтение к неизменному Valueобъекта и перехватывает все запросы на запись, создавая новый неизменный Valueобъект с правильным состоянием. CowValueОбъект должен быть неглубокий копироваться между переменными , чтобы позволить совместное использование Valueобъекта.

import abc
import copy

class BaseValue(abc.ABC):
    @abc.abstractmethod
    def read(self):
        raise NotImplementedError
    @abc.abstractmethod
    def write(self, data):
        raise NotImplementedError

class Value(BaseValue):
    def __init__(self, data):
        self.data = data
    def read(self):
        return self.data
    def write(self, data):
        pass

class CowValue(BaseValue):
    def __init__(self, data):
        self.value = Value(data)
    def read(self):
        return self.value.read()
    def write(self, data):
        self.value = Value(data)

v = CowValue(1)
w = copy.copy(v)  # shares the immutable Value object
assert v.read() == w.read()
assert id(v.value) == id(w.value)
w.write(2)  # creates a new immutable Value object with the correct state
assert v.read() != w.read()
assert id(v.value) != id(w.value)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.