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.
Odpowiedzi:
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.
clone()
do implementacji fork()
- pamięć procesu nadrzędnego jest zabezpieczona dla dziecka.
„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.
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 3
chce wprowadzić zmiany w swojej kopii A
, proces 3
faktycznie wykona kopię A
i utworzy nowy blok danych o nazwie B
. Proces 1
, 2
, 4
nadal odczytu blok A
procesu 3
jest odczytu B
.
A
powinien tworzyć nową kopię. Jeśli pytasz, co się stanie, jeśli pojawi się zupełnie nowy proces i ulegnie zmianie, A
to 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.
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.
vfork
NIE 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 vfork
coś zostało zmodyfikowane we wspólnej przestrzeni!
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ę.
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.
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 . ValueProxy
Obiektu ( proxy ) implementuje technikę kopiowanie przy zapisie poprzez:
Value
obiektem ( podmiotem );Value
obiektu z nowym stanem i ponowne wiązanie atrybutu podmiotu na nowy niezmienny Value
obiekt;ValueProxy
obiektu mającego ten sam atrybut podmiotu, co oryginalny ValueProxy
obiekt.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
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.