Copy-on-Write¶
Memory-sharing method — instantiates Demand-Triggered Deferred Evaluation
Lets many holders share one physical copy of some data and defers the actual copy until a holder first writes, so unmodified sharing costs nothing.
Copy-on-Write (COW) defers a duplication rather than a computation. Several logical owners are handed what looks like an independent copy of some data, but they all share one physical instance marked read-only; the real copy is made only at the moment an owner first writes, and only for the part being written. Its distinguishing trigger is the mutation itself: a write is the demand signal that activates realization, so read-only sharing — however wide — stays free, and you pay the copy cost precisely where and when divergence actually happens. That makes COW the write-triggered member of this family, as opposed to the read-triggered load of demand paging; the "work" it saves is the copy that would otherwise be made eagerly for owners who never modify anything.
Example¶
A Unix process calls fork(). Semantically the child gets its own full copy of the parent's address space — potentially gigabytes. Copying all of it eagerly would be enormous waste, because the very next thing many children do is exec() a new program and discard that memory entirely.
Under copy-on-write, fork() instead marks every shared page read-only in both processes and copies nothing. Both run against the same physical pages until one writes: that write traps as a protection fault, the kernel copies just that one page, gives the writer its private version, and lets execution resume. A child that forks and immediately exec()s never triggers a single copy. A child that mutates a few pages copies a few pages. The original stays valid for every reader as long as any reader still holds it, and duplication cost tracks actual divergence rather than nominal ownership.
How it works¶
- Share read-only. All holders point at one instance; the shared region is marked so that a write cannot proceed silently.
- Trap the first write. A write to shared data faults; the fault is the activation rule that fires the deferred copy — allocate a private copy for the writer, redirect it, resume.
- Preserve the shared original. The unmodified instance remains valid for every other holder; its lifetime extends until the last sharer releases it (typically reference-counted).
- Copy at the chosen granularity. Only the written unit — a page, a block, a node — is duplicated; everything untouched stays shared.
Tuning parameters¶
- Copy granularity — page, block, or whole object. Finer units copy less per write but multiply bookkeeping and faults; coarser units copy more but amortize overhead.
- Sharing breadth — how many holders share one instance. Wider sharing saves more memory up front but raises the stakes of a write storm.
- Fault vs. check — hardware write-protection faults vs. an explicit "am I shared?" test before each write. Faults are transparent but costly per event; checks are cheaper per write but must be woven into every mutator.
- Reference-counting cost — how sharing and release are tracked. Tighter tracking reclaims sooner but adds per-operation overhead and, naïvely, contention.
When it helps, and when it misleads¶
COW is a clear win when copies are frequently made but rarely modified: process creation, filesystem and volume snapshots, persistent data structures, "copy" of a large buffer that is usually read and discarded. It converts eager O(size) duplication into lazy O(bytes-actually-changed).
It misleads under write-heavy sharing, where its economics invert. If most shared units are eventually written, every first write pays a fault plus the copy — strictly more than an eager copy would have cost — and a burst of writes across widely shared data becomes a fault storm and an allocation spike at the worst moment. In COW storage this also shows up as write amplification: a small logical change forces a whole block to be copied.[1] The classic misuse is treating a COW "copy" as if isolation were free and cheap forever, then being surprised by a latency cliff the first time the workload turns write-mostly. The discipline: reserve COW for read-dominated sharing, size the copy granularity to the real write pattern, and budget for the worst case where every shared unit diverges at once.
How it implements the components¶
activation_rule— the first write to shared data is the trigger that fires the deferred copy; until that write, realization does not happen.input_lifetime_and_capture_rule— the shared original is captured read-only and kept valid for every holder until the last reference is released, so deferral never invalidates a live reader.
It does NOT decide *whether the data is needed (need_predicate — that's the Conditional Workflow Gate), cache computed results (memoized_result_store — that's Memoization), or load absent data on a read fault (demand_signal — that read-triggered path is Demand Paging); COW defers a copy, and its trigger is a write.*
Related¶
- Instantiates: Demand-Triggered Deferred Evaluation — COW defers duplication until a write makes a private copy genuinely necessary.
- Sibling mechanisms: Demand Paging · Late-Materialization Query Plan · Lazy-Initialization Proxy · Thunk or Lazy Promise · Memoization
Notes¶
Copy-on-write and Demand Paging are close kin — both exploit the hardware fault as a demand signal — but they defer opposite things and trigger on opposite accesses: demand paging loads absent data on a read, COW duplicates shared data on a write. A single mapped region can use both at once, which is exactly why keeping the two mechanisms distinct matters when reasoning about a fault.
References¶
[1] Write amplification is the ratio of physical bytes written to logical bytes changed. Copy-on-write storage incurs it because a small update forces a full block (or extent) to be copied and rewritten elsewhere; it is the storage-layer face of COW's "cheap until you write" trade-off. ↩