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

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


person hhafez    schedule 10.03.2009    source источник


Ответы (9)


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

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

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

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

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

person Andrew Hare    schedule 10.03.2009
comment
все, для чего используется обычный массив... однако в некоторых ситуациях этот тип стратегии приводит к более оптимизированным результатам. - person Andrew Flanagan; 10.03.2009
comment
@hhafez: Linux использует его, когда использует clone() для реализации fork() - память родительского процесса используется для дочернего процесса. - person Kerrek SB; 12.01.2012
comment
@hhafez Некоторые файловые системы используют CoW, например, BTRFS . - person Geremia; 22.05.2016
comment
Так работает SandboxIE? когда программа в песочнице хочет перезаписать что-то, песочница перехватывает операцию файловой системы и копирует файл в папку песочницы и позволяет программе писать в файл песочницы вместо оригинала. Это называется копировать при записи? - person Ronnie Matthews; 04.09.2017
comment
Как в конечном итоге происходит слияние? Если есть N копий, какая из них в конечном итоге сохраняется, скажем, на диск? - person SimpleGuy; 13.02.2018
comment
мы не знаем, как открывать вики-страницы и читать определения (не каламбур!). - person snr; 11.03.2019
comment
безупречный ответ! COW также используется в докере. - person Gaurav; 11.06.2020

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

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

person Charlie Martin    schedule 10.03.2009
comment
Если вы сделаете изменение, как другой получит уведомление о вашей новой копии? Не увидят ли они неверные данные. - person powder366; 11.12.2015
comment
@powder366 - Нет, они не увидят неверные данные, потому что, когда вы вносите изменения, фактически создается копия. Например, у вас есть блок данных с именем A. Каждый из процессов 1, 2, 3, 4 хочет сделать его копию и начать чтение, в системе копирования при записи ничего не копируется, но все еще читается A. Теперь процесс 3 хочет внести изменения в свою копию A, процесс 3 фактически сделает копию A и создаст новый блок данных с именем B. Процесс 1, 2, 4 все еще читает блок A, процесс 3 сейчас читает B. - person Puddler; 10.05.2016
comment
@Puddler, что произойдет, если в «A» будут внесены изменения. Все процессы будут читать обновленную информацию или старую? - person Developer; 08.07.2016
comment
@Разработчик: Какой бы процесс ни вносил изменения в A, он должен создавать новую копию. Если вы спрашиваете, что произойдет, если появится совершенно новый процесс и изменит A, тогда мое объяснение не будет вдаваться в подробности. Это будет зависеть от реализации и потребует знаний о том, как вы хотите, чтобы остальная часть реализации работала, например, блокировка файлов\данных и т. д. - person Puddler; 11.07.2016

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

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

person Shamik    schedule 10.03.2009
comment
vfork следует концепции копирования при записи. Пожалуйста, рассмотрите возможность изменения этой строки. vfork НЕ использует COW. Ведь если ребенок что-то напишет, это может привести к неопределенному поведению, а не к копированию страниц!! На самом деле, можно сказать, что верно и обратное. COW действует как vfork, пока что-то не будет изменено в общем пространстве! - person Pavan Manjunath; 05.03.2012
comment
Полностью согласен с Паваном. Удаление строк vfork следует концепции копирования при записи. В наши дни COW используется в fork в качестве оптимизации, так что он действует как vfork и не делает копию родительских данных для дочернего процесса (если мы вызываем только exec * в дочернем) - person Shekhar Kumar; 11.08.2013

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

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

person harpo    schedule 24.09.2010

Книга Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения автора Эрих Гамма и др. ясно описывает оптимизацию копирования при записи (раздел «Последствия», глава «Прокси»):

Шаблон Proxy вводит уровень косвенности при доступе к объекту. Дополнительная косвенность имеет множество применений, в зависимости от типа прокси:

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

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

Чтобы копирование при записи работало, ссылка на тему должна учитываться. Копирование прокси не сделает ничего, кроме увеличения этого счетчика ссылок. Только когда клиент запрашивает операцию, изменяющую субъект, прокси фактически копирует его. В этом случае прокси также должен уменьшить счетчик ссылок субъекта. Когда счетчик ссылок обнуляется, тема удаляется.

Копирование при записи может значительно снизить стоимость копирования тяжеловесных объектов.

Ниже приведена реализация оптимизации копирования при записи на Python с использованием шаблона прокси. Целью этого шаблона проектирования является предоставление суррогата другому объекту для управления доступом к нему.

Диаграмма классов шаблона Proxy:

Диаграмма классов шаблона прокси

Диаграмма объекта паттерна 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
person Maggyero    schedule 14.04.2020

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

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

person Amir Shabani    schedule 15.11.2015

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

person Chris    schedule 10.03.2009
comment
Я не думаю, что он имел в виду использование в этом смысле. - person spydon; 05.10.2015

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

person Sam Keays    schedule 14.09.2019

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

person Sushant    schedule 31.12.2019