Co to jest kopiowanie przy zapisie?

135

Chciałbym wiedzieć, co to jest kopiowanie podczas zapisu i do czego służy? Termin „tablica kopiowania przy zapisie” jest wspominany kilka razy w tutorialach Sun JDK, ale nie rozumiałem, co to znaczy.

hhafez
źródło

Odpowiedzi:

157

Zamierzałem napisać własne wyjaśnienie, ale ten artykuł w Wikipedii podsumowuje to.

Oto podstawowa koncepcja:

Kopiowanie przy zapisie (czasami określane jako „COW”) to strategia optymalizacji stosowana w programowaniu komputerowym. Podstawową ideą jest to, że jeśli wielu dzwoniących prosi o zasoby, które są początkowo nie do odróżnienia, możesz podać im wskaźniki do tego samego zasobu. Ta funkcja może być utrzymywana do momentu, gdy wywołujący spróbuje zmodyfikować swoją „kopię” zasobu, w którym to momencie tworzona jest prawdziwa prywatna kopia, aby zmiany nie stały się widoczne dla wszystkich innych. Wszystko to dzieje się w sposób przejrzysty dla dzwoniących. Podstawową zaletą jest to, że jeśli dzwoniący nigdy nie wprowadza żadnych modyfikacji, nie ma potrzeby tworzenia kopii prywatnej.

Również tutaj jest zastosowanie powszechnego użycia COW:

Koncepcja COW jest również wykorzystywana do konserwacji natychmiastowych migawek na serwerach baz danych, takich jak Microsoft SQL Server 2005. Błyskawiczne migawki zachowują statyczny widok bazy danych, przechowując przed modyfikacją kopię danych podczas aktualizacji danych. Natychmiastowe migawki są używane do testowania zastosowań lub raportów zależnych od chwili i nie powinny być używane do zastępowania kopii zapasowych.

Andrew Hare
źródło
cokolwiek, do czego jest używana zwykła tablica ... jednak w niektórych sytuacjach ten typ strategii skutkuje bardziej zoptymalizowanymi wynikami.
Andrew Flanagan
3
@hhafez: Linux używa go, gdy używa clone()do implementacji fork()- pamięć procesu nadrzędnego jest zabezpieczona dla dziecka.
Kerrek SB
@hhafez Niektóre systemy plików używają CoW, np . BTRFS .
Geremia
Czy tak działa SandboxIE? gdy program w piaskownicy chce nadpisać coś, co sandboxie przechwytuje działanie systemu plików i kopiuje plik do folderu w piaskownicy i pozwala programowi na zapisanie do pliku w piaskownicy zamiast do oryginału. Czy to się nazywa Kopiuj przy zapisywaniu?
Ronnie Matthews
Jak ostatecznie następuje połączenie? Jeśli jest N kopii, która z nich jest ostatecznie przechowywana, powiedzmy na dysku?
SimpleGuy,
59

„Kopiuj przy zapisie” oznacza mniej więcej to, co brzmi: każdy ma jedną wspólną kopię tych samych danych, dopóki nie zostaną zapisane , a potem kopia zostanie wykonana. Zwykle kopiowanie przy zapisie służy do rozwiązywania różnego rodzaju problemów związanych z współbieżnością. Na przykład w ZFS bloki danych na dysku są przydzielane do kopiowania przy zapisie; dopóki nie ma zmian, zachowujesz oryginalne bloki; zmiana dotyczyła tylko bloków, na które miała wpływ. Oznacza to, że przydzielono minimalną liczbę nowych bloków.

Zmiany te są również zwykle implementowane w celu transakcyjnym , tj. Mają właściwości ACID . Eliminuje to niektóre problemy ze współbieżnością, ponieważ wtedy masz gwarancję, że wszystkie aktualizacje są atomowe.

Charlie Martin
źródło
1
Jeśli wprowadzisz zmianę, w jaki sposób druga strona zostanie powiadomiona o Twojej nowej kopii? Czy nie widzieliby niewłaściwych danych.
proszek 366
12
@ powder366 - Nie, nie zobaczyliby złych danych, ponieważ kiedy wprowadzasz zmianę, wtedy faktycznie tworzona jest kopia. Na przykład masz blok danych o nazwie A. Proces 1, 2, 3,4 każda chcesz zrobić kopię i zacząć czytać ją w „copy on write” system nie jest jeszcze skopiowane wszystko jest jeszcze czyta A. Teraz proces 3chce wprowadzić zmiany w swojej kopii A, proces 3faktycznie wykona kopię Ai utworzy nowy blok danych o nazwie B. Proces 1, 2, 4nadal odczytu blok Aprocesu 3jest odczytu B.
Puddler
1
@Puddler, co się stanie, jeśli zmiany zostaną wprowadzone w „A”. Wszystkie procesy będą czytać zaktualizowane informacje lub stare?
Deweloper
3
@Developer: Cóż, niezależnie od tego, który proces wprowadza zmiany, Apowinien tworzyć nową kopię. Jeśli pytasz, co się stanie, jeśli pojawi się zupełnie nowy proces i ulegnie zmianie, Ato moje wyjaśnienie nie jest wystarczająco szczegółowe. Byłoby to specyficzne dla implementacji i wymagałoby wiedzy o tym, jak chcesz, aby reszta implementacji działała, na przykład blokowanie plików \ ​​danych itp.
Puddler
10

Nie powtórzę tej samej odpowiedzi w przypadku kopiowania przy zapisie. Myślę, że odpowiedź Andrzeja i odpowiedź Charliego już bardzo wyraźnie. Podam przykład ze świata OS, żeby wspomnieć, jak szeroko jest to pojęcie używane.

Możemy użyć fork()lub vfork()stworzyć nowy proces. vfork działa zgodnie z koncepcją kopiowania przy zapisie. Na przykład proces potomny utworzony przez vfork będzie współdzielił dane i segment kodu z procesem nadrzędnym. Przyspiesza to czas rozwidlenia. Oczekuje się, że użyje vfork, jeśli wykonujesz exec, a następnie vfork. Zatem vfork utworzy proces potomny, który będzie współdzielił dane i segment kodu ze swoim rodzicem, ale kiedy wywołasz exec, załaduje obraz nowego pliku wykonywalnego do przestrzeni adresowej procesu potomnego.

Shamik
źródło
3
„vfork działa zgodnie z koncepcją kopiowania przy zapisie”. Proszę rozważyć zmianę tej linii. vforkNIE używa COW. W rzeczywistości, jeśli dziecko coś napisze, może to skutkować niezdefiniowanym zachowaniem i nie kopiowaniem stron !! W rzeczywistości można powiedzieć, że jest nieco odwrotnie. COW działa tak, jakby vforkcoś zostało zmodyfikowane we wspólnej przestrzeni!
Pavan Manjunath
Całkowicie zgadzam się z Pavanem. Usuń wiersze „vfork działa zgodnie z koncepcją kopiowania przy zapisie”. Teraz przez kilka dni COW jest używany w fork jako optymalizacja, dzięki czemu działa jak vfork i nie tworzy kopii danych rodzica dla procesu potomnego (jeśli wywołujemy tylko exec * w dziecku)
Shekhar Kumar
8

Aby podać inny przykład, Mercurial używa kopiowania przy zapisie aby klonowanie lokalnych repozytoriów było naprawdę „tanią” operacją.

Zasada jest taka sama, jak w innych przykładach, z tym wyjątkiem, że mówisz o plikach fizycznych zamiast o obiektach w pamięci. Początkowo klon nie jest duplikatem, ale twardym połączeniem z oryginałem. Gdy zmieniasz pliki w klonie, zapisywane są kopie reprezentujące nową wersję.

harpo
źródło
2

Znalazłem ten dobry artykuł o zval w PHP, w którym wspomniano również o COW:

Copy On Write (w skrócie „COW”) to sztuczka zaprojektowana w celu oszczędzania pamięci. Jest używany bardziej ogólnie w inżynierii oprogramowania. Oznacza to, że PHP skopiuje pamięć (lub przydzieli nowy obszar pamięci), gdy napiszesz do symbolu, jeśli ten już wskazywał na zval.

Amir Shabani
źródło
1

Kopiowanie przy zapisie to technika zmniejszania wykorzystania pamięci przez kopie zasobów poprzez udostępnianie pamięci do momentu zmodyfikowania jednej z kopii. Innymi słowy, kopie są początkowo kopiami wirtualnymi i stają się rzeczywistymi kopiami dopiero przy pierwszej operacji zapisu, stąd nazwa „kopiuj przy zapisie”.

Poniżej znajduje się implementacja w Pythonie techniki kopiowania przy zapisie przy użyciu wzorca projektowego proxy . ValueProxyObiektu ( proxy ) implementuje technikę kopiowanie przy zapisie poprzez:

  • posiadanie atrybutu związanego z niezmiennym Valueobiektem ( podmiotem );
  • przekazywanie żądań odczytu do atrybutu podmiotu;
  • tłumaczenie żądań zapisu na utworzenie nowego niezmiennego Valueobiektu z nowym stanem i ponowne wiązanie atrybutu podmiotu na nowy niezmienny Valueobiekt;
  • przetłumaczenie żądań kopiowania na utworzenie nowego ValueProxyobiektu mającego ten sam atrybut podmiotu, co oryginalny ValueProxyobiekt.
import abc

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 ValueProxy(BaseValue):
    def __init__(self, subject):
        self.subject = subject
    def read(self):
        return self.subject.read()
    def write(self, data):
        self.subject = Value(data)
    def clone(self):
        return ValueProxy(self.subject)

v1 = ValueProxy(Value('foo'))
v2 = v1.clone()  # shares the immutable Value object between the copies
assert v1.subject is v2.subject
v2.write('bar')  # creates a new immutable Value object with the new state
assert v1.subject is not v2.subject
Maggyero
źródło
0

Jest również używany w Ruby „Enterprise Edition” jako zgrabny sposób oszczędzania pamięci.

Chris
źródło
2
Nie sądzę, żeby miał na myśli „używany do” w tym sensie.
spydon
0

Dobrym przykładem jest Git, który wykorzystuje strategię do przechowywania obiektów blob. Dlaczego używa skrótów? Częściowo dlatego, że są one łatwiejsze do wykonywania różnic, ale także dlatego, że upraszczają optymalizację strategii COW. Kiedy dokonujesz nowego zatwierdzenia z kilkoma zmianami plików, zdecydowana większość obiektów i drzew nie ulegnie zmianie. Dlatego zatwierdzenie, poprzez różne wskaźniki utworzone z hashów, będzie odnosić się do zbioru obiektów, które już istnieją, sprawiając, że przestrzeń magazynowa wymagana do przechowywania całej historii będzie znacznie mniejsza.

Sam Keays
źródło
0

Jest to koncepcja ochrony pamięci. W tym kompilatorze tworzy dodatkową kopię w celu modyfikacji danych w podrzędnym, a te zaktualizowane dane nie odzwierciedlają danych rodziców.

Sushant
źródło