Відповіді:
Я збирався написати своє власне пояснення, але ця стаття у Вікіпедії дуже резюмує це.
Ось основна концепція:
Copy-on-write (іноді його називають "COW") - це стратегія оптимізації, що використовується в комп'ютерному програмуванні. Основна ідея полягає в тому, що якщо кілька абонентів запитують ресурси, які спочатку не відрізняються, ви можете надати їм покажчики на один і той же ресурс. Цю функцію можна підтримувати, поки абонент не спробує змінити свою "копію" ресурсу, і в цей момент буде створена справжня приватна копія, щоб запобігти тому, щоб зміни стали видимими для всіх інших. Все це відбувається прозоро з абонентами. Основна перевага полягає в тому, що якщо абонент ніколи не вносить будь-яких модифікацій, приватна копія ніколи не створюватиметься.
Також тут представлено загальне застосування коров’ячої сировини:
Концепція COW також використовується для підтримки миттєвого знімка на серверах баз даних, таких як Microsoft SQL Server 2005. Миттєві знімки зберігають статичний вигляд бази даних, зберігаючи попередньо модифіковану копію даних при оновленні нижчих даних. Миттєві знімки використовуються для тестування використання або звітів, що залежать від моменту, і не повинні використовуватися для заміни резервних копій.
clone()
для реалізації fork()
- пам’ять батьківського процесу є COWed для дитини.
"Копіювати при записі" означає більш-менш те, що це звучить: кожен має одну спільну копію одних і тих же даних, поки не буде записано , а потім буде зроблена копія. Зазвичай копіювання при записі використовується для вирішення різновидів проблем. Наприклад, у ZFS блокам даних на диску виділяється копіювання під час запису; доки не буде змін, ви зберігаєте оригінальні блоки; зміна змінила лише постраждалі блоки. Це означає, що мінімальна кількість нових блоків виділяється.
Ці зміни також зазвичай реалізуються як транзакційні , тобто вони мають властивості кислотної кислоти . Це усуває деякі проблеми одночасності, оскільки тоді ви гарантуєте, що всі оновлення є атомними.
A
. Процес 1
, 2
, 3
, 4
кожен хоче зробити копію і почати читати його, в «копіюванні при записи» система нічого не копіюється поки все ще читають A
. Тепер процес 3
хоче змінити його копію A
, процес 3
тепер фактично зробить копію A
та створить новий блок даних, який називається B
. Процес 1
, 2
, 4
все ще читає блок - A
процес 3
в даний час читання B
.
A
слід створити нову копію. Якщо ви запитуєте, що відбувається, якщо відбудеться і зміниться абсолютно новий процес, A
то моє пояснення насправді не надто детально для цього. Це буде специфічно для впровадження та вимагає знань про те, як ви хочете, щоб решта імплементації працювала, наприклад, файл \ блокування даних тощо.
Я не повторюю ту саму відповідь у Copy-on-Write. Я думаю , що відповідь Ендрю і відповідь Чарлі вже дуже ясно. Я надам вам приклад зі світу ОС, просто зазначу, наскільки широко використовується ця концепція.
Ми можемо використовувати fork()
або vfork()
створити новий процес. vfork дотримується концепції копіювання на запис. Наприклад, дочірній процес, створений vfork, поділиться сегментом даних та коду з батьківським процесом. Це прискорює час розщеплення. Очікується використовувати vfork, якщо ви виконуєте exec, а потім vfork. Таким чином, vfork створить дочірній процес, який буде обмінюватися даними та кодовим сегментом зі своїм батьківським, але коли ми зателефонуємо до exec, він завантажить зображення нового виконуваного файлу в адресний простір дочірнього процесу.
vfork
НЕ використовує COW. Насправді, якщо дитина щось пише, це може призвести до невизначеної поведінки, а не до копіювання сторінок !! Насправді, можна сказати, навпаки, дещо правда. COW діє так, vfork
поки щось не зміниться в спільному просторі!
Просто для надання іншого прикладу, Mercurial використовує функцію копіювання на запис, щоб зробити клонування локальних сховищ дійсно «дешевою» операцією.
Принцип такий самий, як і в інших прикладах, за винятком того, що ви говорите про фізичні файли замість об'єктів у пам'яті. Спочатку клон - це не дублікат, а тверда посилання на оригінал. Під час зміни файлів у клоні записуються копії, які представляють нову версію.
Я знайшов цю хорошу статтю про zval в PHP, в якій згадується і COW:
Copy On Write (скорочено "COW") - фокус, призначений для економії пам'яті. Він використовується більш загально в інженерії програмного забезпечення. Це означає, що PHP буде копіювати пам'ять (або виділяти нову область пам'яті), коли ви пишете на символ, якщо ця вже вказувала на zval.
Хороший приклад - Git, який використовує стратегію зберігання крапок. Чому він використовує хеші? Частково через те, що це легше виконати, це відрізняється, а також тому, що спрощує оптимізацію стратегії роботи з коровами. Коли ви робите нову фіксацію з кількома файлами змін, переважна більшість об'єктів і дерева не змінюватимуться. Тому фіксація, через різні покажчики, зроблені з хешей, посилається на купу об'єктів, які вже існують, завдяки чому місця для зберігання, необхідні для зберігання всієї історії, значно менше.
Ось після цього - реалізація 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)