Respuestas:
Iba a escribir mi propia explicación, pero este artículo de Wikipedia lo resume bastante.
Aquí está el concepto básico:
Copiar en escritura (a veces denominado "VACA") es una estrategia de optimización utilizada en la programación de computadoras. La idea fundamental es que si varias personas que llaman solicitan recursos que inicialmente no se pueden distinguir, puede darles punteros al mismo recurso. Esta función se puede mantener hasta que la persona que llama intente modificar su "copia" del recurso, momento en el que se crea una copia privada verdadera para evitar que los cambios sean visibles para todos los demás. Todo esto sucede de manera transparente a las personas que llaman. La principal ventaja es que si la persona que llama nunca realiza modificaciones, no es necesario crear una copia privada.
También aquí hay una aplicación de un uso común de VACA:
El concepto COW también se utiliza en el mantenimiento de instantáneas instantáneas en servidores de bases de datos como Microsoft SQL Server 2005. Las instantáneas instantáneas conservan una vista estática de una base de datos al almacenar una copia de datos previa a la modificación cuando se actualizan los datos subyacentes. Las instantáneas instantáneas se usan para probar usos o informes dependientes del momento y no se deben usar para reemplazar las copias de seguridad.
clone()
implementa fork()
: la memoria del proceso principal está COWed para el hijo.
"Copiar al escribir" significa más o menos lo que parece: todos tienen una sola copia compartida de los mismos datos hasta que se escribe , y luego se hace una copia. Por lo general, la copia en escritura se utiliza para resolver problemas de concurrencia. En ZFS , por ejemplo, los bloques de datos en el disco se asignan copia en escritura; mientras no haya cambios, conserva los bloques originales; un cambio solo cambió los bloques afectados. Esto significa que se asigna el número mínimo de nuevos bloques.
Estos cambios también suelen implementarse para ser transaccionales , es decir, tienen las propiedades ACID . Esto elimina algunos problemas de concurrencia, porque entonces está garantizado que todas las actualizaciones son atómicas.
A
. Proceso 1
, 2
, 3
, 4
cada uno desea hacer una copia del mismo y empezar a leerlo, en una "copia en escritura" sistema nada es copiado sin embargo, todo lo que todavía está leyendo A
. Ahora el proceso 3
quiere hacer un cambio en su copia A
, el proceso 3
ahora hará una copia A
y creará un nuevo bloque de datos llamado B
. Proceso 1
, 2
, 4
todavía está leyendo bloque de A
proceso 3
es ahora la lectura B
.
A
debe crear una nueva copia. Si está preguntando qué sucede si aparece un proceso completamente nuevo y cambia, A
entonces mi explicación realmente no entra en suficientes detalles para eso. Eso sería de aplicación específica y requieren conocimientos sobre cómo desea que el resto de la aplicación al trabajo, tales como archivo \ bloqueo de datos, etc.
No repetiré la misma respuesta en Copy-on-Write. Creo que la respuesta de Andrew y la respuesta de Charlie ya han dejado muy claro. Le daré un ejemplo del mundo del sistema operativo, solo para mencionar cuán ampliamente se usa este concepto.
Podemos usar fork()
o vfork()
crear un nuevo proceso. vfork sigue el concepto de copia en escritura. Por ejemplo, el proceso hijo creado por vfork compartirá los datos y el segmento de código con el proceso padre. Esto acelera el tiempo de bifurcación. Se espera que use vfork si está ejecutando exec seguido de vfork. Entonces vfork creará el proceso hijo que compartirá datos y segmento de código con su padre pero cuando llamamos a exec, cargará la imagen de un nuevo ejecutable en el espacio de direcciones del proceso hijo.
vfork
NO usa VACA. De hecho, si el niño escribe algo, puede provocar un comportamiento indefinido y no copiar páginas. De hecho, puedes decir que al revés es algo cierto. ¡VACA actúa como vfork
hasta que algo se modifica en el espacio compartido!
Solo para proporcionar otro ejemplo, Mercurial utiliza la copia en escritura para hacer que la clonación de repositorios locales sea una operación realmente "barata".
El principio es el mismo que el de los otros ejemplos, excepto que se trata de archivos físicos en lugar de objetos en la memoria. Inicialmente, un clon no es un duplicado sino un enlace rígido al original. A medida que cambia los archivos en el clon, se escriben copias para representar la nueva versión.
Encontré este buen artículo sobre zval en PHP, que también mencionaba COW:
Copiar al escribir (abreviado como 'VACA') es un truco diseñado para ahorrar memoria. Se usa más generalmente en ingeniería de software. Significa que PHP copiará la memoria (o asignará una nueva región de memoria) cuando escriba en un símbolo, si este ya apuntaba a un zval.
Un buen ejemplo es Git, que utiliza una estrategia para almacenar blobs. ¿Por qué usa hashes? En parte porque estos son más fáciles de realizar, pero también porque simplifica la optimización de una estrategia COW. Cuando realiza una nueva confirmación con pocos cambios de archivos, la gran mayoría de los objetos y árboles no cambiarán. Por lo tanto, el commit, a través de varios punteros hechos de hashes, hará referencia a un grupo de objetos que ya existen, haciendo que el espacio de almacenamiento requerido para almacenar todo el historial sea mucho más pequeño.
Aquí hay una implementación de Python de copia en escritura (COW) usando el patrón de diseño del decorador . Un objeto Value
mutable CowValue
(el decorador) mantiene una referencia a un objeto inmutable . El CowValue
objeto reenvía todas las solicitudes de lectura al Value
objeto inmutable e intercepta todas las solicitudes de escritura creando un nuevo Value
objeto inmutable con el estado correcto. El CowValue
objeto debe copiarse superficialmente entre las variables para permitir compartir el Value
objeto.
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)