fork() creates a new process with a virtual address space derived from the caller, but Linux does not need to duplicate every private physical page at the instant the syscall returns. For ordinary private writable mappings, the kernel can arrange parent and child page tables so both processes initially refer to the same physical memory while writes are constrained by copy-on-write state.

This design makes process creation proportional to page-table and kernel bookkeeping rather than to the full amount of resident private data. Physical copying is deferred until a write requires the two address spaces to diverge.

The virtual address spaces are separate even when pages are shared

After fork(), parent and child are distinct processes with distinct memory-management state. Their virtual mappings can later change independently through operations such as mmap(), munmap(), and mprotect().

That separation does not require every virtual page to have a unique physical page immediately. A page-table entry in each process can point at the same physical page. The kernel tracks the mapping relationships and prevents an ordinary private write from silently changing data visible through the other process.

The key distinction is between virtual ownership and physical backing. Two independent address spaces can temporarily share physical storage for bytes that have not diverged.

Writable private mappings become copy-on-write candidates

A private writable mapping needs private semantics after a process modifies it. During fork(), Linux can preserve the current contents by sharing the physical page and arranging page-table permissions so a later write cannot proceed as a normal writable access.

Conceptually, the state resembles:

before fork:
parent virtual page -> physical page A

after fork:
parent virtual page -> physical page A
child  virtual page -> physical page A
                         shared until a private write

The page-table entries used for this state do not grant an unrestricted writable mapping to the shared private page. When either process attempts a write, the CPU raises a page fault because the current translation does not permit that write.

A page fault in this case is not evidence that the address is invalid. It is the mechanism that transfers control to the kernel so private-write semantics can be established.

The write fault creates divergence

When Linux handles a copy-on-write fault, it checks the mapping and page state involved. If the physical page still needs to remain visible to another mapping, the kernel allocates a replacement page, copies the old contents, and updates the faulting process’s page table to reference the private replacement with suitable write permission.

The resulting relationship is:

parent virtual page -> physical page A
child  virtual page -> physical page B

Only the page touched by the write needs this treatment. A process with gigabytes of mapped private memory can therefore fork without immediately copying gigabytes of physical data.

There are cases where a full data copy is unnecessary during fault handling. If the kernel can establish that the faulting mapping already has exclusive use of the relevant page, permission can be restored without preserving a second physical copy. The exact path depends on mapping and page state.

Page tables still carry a creation cost

Copy-on-write avoids eager copying of page contents, but fork() is not free. The child needs process state and memory-management structures, and the kernel must establish the mappings required to represent the inherited address space.

Large address spaces can therefore make fork() more expensive even when very little user data is copied. Page-table traversal, allocation, reference accounting, and later translation invalidations all contribute work.

This distinction matters in latency-sensitive software. Resident memory size alone does not describe fork cost, and the absence of immediate bulk page copying does not imply constant-time process creation.

A later exec changes the economic value of deferred copying

A common process-launch sequence is fork() followed soon by execve(). execve() replaces the process image with mappings for a different executable and its runtime state.

Eagerly copying all private pages during fork() would waste substantial memory bandwidth when the child discards those mappings almost immediately. Copy-on-write allows the short-lived inherited image to remain mostly shared until execve() replaces it.

If the child performs many writes before execve(), more private pages can be created. The benefit therefore depends on behavior between process creation and image replacement.

Shared mappings follow different semantics

Copy-on-write is tied to private mapping semantics. A mapping created with MAP_SHARED is intended to make updates visible through mappings of the same shared object, subject to the normal memory and filesystem rules involved.

Such a mapping should not be converted into independent private copies merely because a process was created with fork(). The mapping type determines whether post-fork writes represent shared updates or private divergence.

Anonymous private memory, private file mappings, shared memory, and special mappings can therefore behave differently even when they occupy adjacent virtual addresses.

Memory accounting can look larger than immediate physical demand

Parent and child can each report large virtual or resident mappings after fork(), while many underlying physical pages remain shared. Summing per-process memory figures without accounting for sharing can overstate the physical memory uniquely attributable to the pair.

As writes accumulate, copy-on-write pages become private and physical demand can rise. A workload that forks a large process and then modifies most inherited memory may eventually consume memory close to what eager copying would have required, but the allocation occurs incrementally.

Metrics such as proportional set size are useful when shared physical pages need to be apportioned across processes rather than counted in full for each one.

Copy-on-write shifts work to the point of mutation

Linux fork() combines separate virtual address spaces with temporary physical sharing. Page-table protection turns the first private write into a controlled fault, and fault handling creates a private physical page only when divergence is required.

The result is a deliberate trade: less eager memory copying and faster creation for many workloads, in exchange for page-table work and possible page-fault latency later. The mechanism is especially effective when inherited memory remains mostly unchanged or is soon replaced by execve().