Kopiowanie przy zapisie
Ten artykuł od 2011-09 wymaga zweryfikowania podanych informacji. |
Kopiowanie przy zapisie (ang. copy-on-write, COW) – technika optymalizacji używana w programowaniu komputerów, gdy istnieje potrzeba współdzielenia względnie dużej ilości danych (np. między wątkami czy procesami), co do których nie ma pewności, że zostaną zmodyfikowane przez używające je obiekty.
Początkowo zamiast rzeczywistego, kosztownego (czasowo i pamięciowo) kopiowania pamięci zwracany jest wskaźnik do oryginalnych danych; kopiowanie jest wykonywane dopiero wtedy, gdy zachodzi potrzeba ich modyfikacji. Największą zaletą tej techniki jest to, że jeżeli nigdy nie będą realizowane żadne zmiany, to nie będzie również potrzeby wykonywania kopii.
Zastosowanie kopiowania przy zapisie w systemach operacyjnych
Kopiowanie przy zapisie jest wykorzystywane przez system operacyjny w celu szybszego wykonywania funkcji fork. Funkcja ta tworzy proces potomny, który posiada dokładną kopię kontekstu procesu nadrzędnego, jak również kopię jego pamięci. Ponieważ kopiowanie pamięci jest czasochłonne, dlatego zamiast kopiowania system operacyjny (kernel) — korzystając z mechanizmu stronicowania — początkowo odwzorowuje przestrzeń adresową procesu potomnego na pamięć fizyczną zarezerwowaną dla procesu nadrzędnego. Strony pamięci, które mogą być zmodyfikowane zarówno przez proces jak i jego potomka, otrzymują znacznik „kopiowane przy zapisie”.
Gdy jeden z procesów modyfikuje pamięć, kernel przechwytuje to wywołanie i kopiuje modyfikowane strony tak, aby zmiany dokonane przez jeden proces były niewidoczne dla drugiego. Od tej chwili proces nadrzędny i potomny zaczynają odwoływać się do fizycznie różnych stron.
Kolejnym zastosowaniem jest funkcja calloc, która może być zaimplementowana przy pomocy strony pamięci fizycznej wypełnionej zerami. Gdy przydzielana jest pamięć, wszystkie zwracane w wyniku tej operacji strony pamięci wirtualnej odnoszą się do fizycznej strony wypełnionej zerami i oznaczane są jako „kopiowane przy zapisie”. Dzięki temu ilość fizycznej pamięci zaalokowanej przez proces nie zwiększa się tak długo, jak długo nie są zapisywane żadne dane. Technika ta wykorzystywana jest zwykle w przypadku dużych alokacji.
Kopiowanie przy zapisie może być zaimplementowane w ten sposób, że MMU otrzymuje informację, że pewne strony w przestrzeni adresowej procesu są tylko do odczytu. Gdy proces próbuje zapisać te strony, MMU generuje wyjątek, który jest z kolei przechwytywany przez kernel. Kernel przydziela wówczas nową przestrzeń w pamięci fizycznej i modyfikuje odpowiednie struktury danych tak, aby zapisywana strona odpowiadała nowo przydzielonej pamięci fizycznej.
Inne zastosowania kopiowana przy zapisie
Kopiowanie przy zapisie stosowane jest również poza jądrem systemu operacyjnego, np. w bibliotekach. Klasa string w bibliotece standardowej C++ została specjalnie zaprojektowana tak, by umożliwić kopiowanie przy zapisie:
std::string x("Hello");
std::string y = x; // x i y używają tego samego bufora
y += ", World!"; // y używa innego bufora
// x wciąż używa starego bufora
W systemach wielowątkowych kopiowanie przy zapisie może być używane zamiast tradycyjnego lokowania oraz zamiast Compare-and-swap w celu zwiększenia lub zmniejszenia wewnętrznych liczników odwołań. Jeśli pewien zasób nigdy nie zostanie zmieniony, można on być bezpiecznie kopiowany przez wiele wątków (poprzez zwiększenie licznika odwołań) bez potrzeby stosowania ciężkich mechanizmów lokujących takich jak np. muteksy. Gdy licznik odwołań do zasobu osiągnie wartość 0, do zasobu odwołuje się dokładnie jeden wątek, a więc pamięć zajęta przez zasób może zostać bezpiecznie zwolniona, bez konieczności używania mechanizmów lokujących. Brak konieczności kopiowania zasobu (w porównaniu z tworzonymi tradycyjnie głębokimi kopiami) wpływa na polepszenie wydajności zarówno systemów jedno- jak i wielowątkowych.
Koncepcja kopiowania przy zapisie wykorzystywana jest również w oprogramowaniu wirtualizacyjnym takim jak Bochs, QEMU (qcow2[1]), Linux vserver, UML oraz VirtualBox w celu uzyskania wirtualnej przestrzeni dyskowej. Mechanizm ten pozwala na znaczne zmniejszenie wymaganej przestrzeni dyskowej w sytuacji, gdy ten sam obraz dysku twardego wykorzystywany jest przez wiele maszyn wirtualnych oraz przyczynia się do zwiększenia wydajności, gdyż odczyty z dysku są przechowywane w pamięci cache znajdującej się w RAM i z tej pamięci serwowane są żądania kolejnych odczytów pochodzące od innych maszyn wirtualnych.
Kolejne zastosowanie to zarządzanie snapshotami w serwerach bazodanowych takich jak np. Microsoft SQL Server 2005. Snapshoty przechowują statyczny widok bazy danych poprzez zapamiętywanie kopii danych sprzed modyfikacji w chwili, gdy dane te są modyfikowane. Tego rodzaju snapshoty używane są do testowania lub przygotowywania raportów i nie powinny być wykorzystywane jako technika tworzenia kopii zapasowych.
Technika ta może być również użyta w celu emulacji urządzeń do zapisu-odczytu na fizycznych urządzeniach wymagających równoważenia obciążenia lub które mają charakter Write Once Read Many.