Câu trả lời:
Tôi sẽ viết lên lời giải thích của riêng tôi nhưng bài viết trên Wikipedia này đã tóm tắt khá nhiều.
Đây là khái niệm cơ bản:
Copy-on-write (đôi khi được gọi là "COW") là một chiến lược tối ưu hóa được sử dụng trong lập trình máy tính. Ý tưởng cơ bản là nếu nhiều người gọi yêu cầu các tài nguyên ban đầu không thể phân biệt được, bạn có thể cung cấp cho họ các con trỏ tới cùng một tài nguyên. Chức năng này có thể được duy trì cho đến khi người gọi cố gắng sửa đổi "bản sao" tài nguyên của mình, tại đó, một bản sao riêng thực sự được tạo để ngăn những thay đổi hiển thị cho mọi người khác. Tất cả điều này xảy ra trong suốt cho người gọi. Ưu điểm chính là nếu người gọi không bao giờ thực hiện bất kỳ sửa đổi nào, thì không cần phải sao chép riêng tư.
Ngoài ra đây là một ứng dụng sử dụng chung của COW:
Khái niệm COW cũng được sử dụng để bảo trì ảnh chụp nhanh tức thì trên các máy chủ cơ sở dữ liệu như Microsoft SQL Server 2005. Ảnh chụp nhanh tức thì giữ chế độ xem tĩnh của cơ sở dữ liệu bằng cách lưu trữ bản sao dữ liệu sửa đổi trước khi cập nhật dữ liệu. Ảnh chụp nhanh tức thì được sử dụng để kiểm tra sử dụng hoặc báo cáo phụ thuộc vào thời điểm và không nên được sử dụng để thay thế bản sao lưu.
clone()
để thực hiện fork()
- bộ nhớ của tiến trình cha mẹ được COWed cho đứa trẻ.
"Sao chép trên ghi" có nghĩa là ít nhiều có vẻ như: mọi người đều có một bản sao được chia sẻ của cùng một dữ liệu cho đến khi nó được viết và sau đó một bản sao được tạo. Thông thường, copy-on-write được sử dụng để giải quyết các loại vấn đề tương tranh. Trong ZFS , ví dụ, các khối dữ liệu trên đĩa được phân bổ sao chép khi ghi; miễn là không có thay đổi, bạn giữ nguyên các khối ban đầu; một thay đổi chỉ thay đổi các khối bị ảnh hưởng. Điều này có nghĩa là số lượng tối thiểu của các khối mới được phân bổ.
Những thay đổi này cũng thường được thực hiện để giao dịch , nghĩa là chúng có các thuộc tính ACID . Điều này giúp loại bỏ một số vấn đề tương tranh, bởi vì sau đó bạn được đảm bảo rằng tất cả các cập nhật là nguyên tử.
A
. Quy trình 1
, 2
, 3
, 4
từng muốn thực hiện một bản sao của nó và bắt đầu đọc nó, trong một "Copy trên ghi" hệ thống không có gì được sao chép nhưng mọi thứ vẫn đang đọc A
. Bây giờ quá trình 3
muốn thực hiện thay đổi đối với bản sao của nó A
, quy trình 3
thực sự sẽ tạo một bản sao A
và tạo một khối dữ liệu mới được gọi B
. Quy trình 1
, 2
, 4
vẫn đọc khối A
quá trình 3
bây giờ đã đọc B
.
A
sẽ tạo ra một bản sao mới. Nếu bạn đang hỏi điều gì xảy ra nếu một quá trình hoàn toàn mới xuất hiện và thay đổi A
thì lời giải thích của tôi không thực sự đi sâu vào chi tiết cho điều đó. Đó sẽ là triển khai cụ thể và yêu cầu kiến thức về cách bạn muốn phần còn lại của triển khai hoạt động, chẳng hạn như tệp \ khóa dữ liệu, v.v.
Tôi sẽ không lặp lại câu trả lời tương tự trên Copy-on-Write. Tôi nghĩ câu trả lời của Andrew và câu trả lời của Charlie đã làm cho nó rất rõ ràng. Tôi sẽ cho bạn một ví dụ từ thế giới HĐH, chỉ đề cập đến việc khái niệm này được sử dụng rộng rãi như thế nào.
Chúng ta có thể sử dụng fork()
hoặc vfork()
để tạo ra một quy trình mới. vfork tuân theo khái niệm copy-on-write. Ví dụ, quy trình con được tạo bởi vfork sẽ chia sẻ dữ liệu và phân đoạn mã với quy trình cha. Điều này tăng tốc thời gian rèn. Dự kiến sẽ sử dụng vfork nếu bạn đang thực hiện exec theo sau là vfork. Vì vậy, vfork sẽ tạo tiến trình con sẽ chia sẻ dữ liệu và phân đoạn mã với cha mẹ của nó, nhưng khi chúng ta gọi exec, nó sẽ tải lên hình ảnh của một tệp thực thi mới trong không gian địa chỉ của tiến trình con.
vfork
KHÔNG sử dụng COW. Trong thực tế nếu đứa trẻ viết một cái gì đó, nó có thể dẫn đến hành vi không xác định và không sao chép các trang !! Trong thực tế, bạn có thể nói cách khác vòng là hơi đúng. COW hoạt động như vfork
cho đến khi một cái gì đó được sửa đổi trong không gian chia sẻ!
Chỉ để cung cấp một ví dụ khác, Mercurial sử dụng copy-on-write để biến các kho lưu trữ cục bộ thành một hoạt động thực sự "rẻ tiền".
Nguyên tắc này giống như các ví dụ khác, ngoại trừ việc bạn đang nói về các tệp vật lý thay vì các đối tượng trong bộ nhớ. Ban đầu, một bản sao không phải là một bản sao mà là một liên kết cứng với bản gốc. Khi bạn thay đổi tệp trong bản sao, các bản sao được viết để thể hiện phiên bản mới.
Tôi tìm thấy này bài viết tốt về zval trong PHP, mà đề cập COW quá:
Copy On Write (viết tắt là 'COW') là một mẹo được thiết kế để tiết kiệm bộ nhớ. Nó được sử dụng phổ biến hơn trong công nghệ phần mềm. Điều đó có nghĩa là PHP sẽ sao chép bộ nhớ (hoặc phân bổ vùng bộ nhớ mới) khi bạn viết vào một ký hiệu, nếu cái này đã được trỏ đến một giá trị zval.
Một ví dụ điển hình là Git, sử dụng chiến lược để lưu trữ các đốm màu. Tại sao nó sử dụng băm? Một phần vì những thứ này dễ thực hiện khác hơn, nhưng cũng vì nó đơn giản hơn để tối ưu hóa chiến lược COW. Khi bạn thực hiện một cam kết mới với một vài tệp thay đổi, phần lớn các đối tượng và cây sẽ không thay đổi. Do đó, cam kết, sẽ thông qua các con trỏ khác nhau được tạo bởi các giá trị băm tham chiếu một loạt các đối tượng đã tồn tại, làm cho không gian lưu trữ cần thiết để lưu trữ toàn bộ lịch sử nhỏ hơn nhiều.
Đây là một triển khai Python sao chép trên ghi (COW) bằng cách sử dụng mẫu thiết kế trang trí . Một tham chiếu đến một Value
đối tượng bất biến được giữ bởi một CowValue
đối tượng có thể thay đổi (người trang trí). Đối CowValue
tượng chuyển tiếp tất cả các yêu cầu đọc đến Value
đối tượng bất biến và chặn tất cả các yêu cầu ghi bằng cách tạo một Value
đối tượng bất biến mới với trạng thái chính xác. Đối CowValue
tượng phải được sao chép nông giữa các biến để cho phép chia sẻ Value
đối tượng.
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)