Unix - UNIX Copy-on-Write Memory: Process Creation and Memory Efficiency

Copy-on-Write (COW) is a memory-management technique used by UNIX-like operating systems to make process creation more efficient. It is closely associated with the fork() system call, which creates a new process from an existing process. Instead of immediately creating a complete physical copy of the parent's memory, the operating system initially allows the parent and child processes to share the same physical memory pages. Actual copying happens only when one of the processes attempts to modify a shared page.

Why Copy-on-Write Is Needed

When a process calls fork(), the child process initially needs a memory layout that is almost identical to the parent. A straightforward approach would be to copy every memory page belonging to the parent into newly allocated physical memory for the child. This can be expensive, especially when the parent process uses a large amount of memory.

For example, imagine a process using 500 MB of memory. If the operating system physically copied all 500 MB during every fork(), the operation would require significant memory allocation and copying time. In many situations, this copying would be unnecessary because the child may immediately call exec() to run another program. In that case, most of the copied memory would never actually be used.

Copy-on-Write solves this problem by delaying the copying process until it becomes necessary.

How Copy-on-Write Works

When fork() is executed, the UNIX kernel creates a new process and establishes the child's virtual memory mappings. Initially, the parent and child can point to the same physical memory pages.

These shared pages are normally marked as read-only for both processes. The operating system maintains information indicating that the pages are being shared.

Suppose the parent and child both have access to a memory page containing a variable:

Parent process
       |
       v
   Physical Page
       ^
       |
       Child process

Both processes can read the page. No physical copy is required because reading does not change its contents.

However, if the child attempts to modify the page, the processor generates a page fault because the page is marked as read-only. The kernel handles this fault by allocating a new physical page, copying the original contents into it, and allowing the child to modify its private copy.

The resulting structure becomes:

Parent process
       |
       v
 Original Physical Page

Child process
       |
       v
 New Physical Page

From this point onward, changes made by the child do not affect the parent's copy.

Step-by-Step Example

Consider a parent process containing:

int value = 100;

The parent calls:

pid_t pid = fork();

After fork(), both the parent and child initially have their own virtual address spaces, but the corresponding physical memory pages may be shared.

If the child only executes:

printf("%d\n", value);

no memory copying is necessary. Both processes can read the same physical page.

Now suppose the child executes:

value = 200;

The child is attempting to modify the shared page. Because the page is protected as read-only, a page fault occurs.

The kernel then:

  1. Detects that the page is shared.

  2. Allocates a new physical memory page.

  3. Copies the original page contents to the new page.

  4. Updates the child's page-table entry.

  5. Allows the write operation to continue.

  6. Keeps the parent's mapping connected to the original page.

The parent continues to see:

value = 100

while the child sees:

value = 200

This provides the expected process isolation while avoiding unnecessary copying.

Relationship Between fork() and exec()

Copy-on-Write is particularly valuable because UNIX programs frequently use the combination of fork() and exec().

A typical sequence is:

Parent
  |
  | fork()
  v
Child
  |
  | exec()
  v
New Program

The child initially shares many memory pages with its parent. It then calls exec() to replace its process image with another program.

Because the child's old memory is going to be replaced, physically copying all of the parent's memory would be wasteful. Copy-on-Write allows the system to avoid most of that unnecessary copying.

This is one of the major reasons why the traditional UNIX process-creation model can remain efficient even when processes have relatively large address spaces.

Role of Page Tables

Copy-on-Write depends heavily on virtual memory and page tables.

A process does not directly access physical memory. Instead, virtual addresses are translated into physical addresses through page tables maintained by the operating system.

After fork(), the parent and child receive separate virtual address spaces, but their page-table entries can initially refer to the same physical pages.

The kernel marks appropriate pages as read-only. If either process attempts to write to a shared page, the processor detects the protected write and generates a page fault.

The kernel then performs the necessary copying.

Therefore, Copy-on-Write is not simply a process-level technique. It depends on cooperation between the process-management system, virtual-memory subsystem, page tables, and hardware memory-protection mechanisms.

Copy-on-Write and Page Faults

A page fault does not always mean that something has gone wrong. UNIX-like operating systems use page faults as part of normal memory-management operations.

In the case of Copy-on-Write, a write attempt to a shared read-only page produces a protection-related page fault.

The kernel examines the fault and determines why it occurred. If the page belongs to a Copy-on-Write mapping, the kernel can create a private copy rather than terminating the process.

This is sometimes called a COW fault.

The process generally does not need to know that the fault occurred. The kernel handles it transparently and resumes execution after establishing the private page.

Advantages of Copy-on-Write

The primary advantage is reduced memory consumption. Parent and child processes can share physical pages as long as they do not modify them.

Another important advantage is faster process creation. Instead of immediately copying the entire address space, the kernel initially creates memory mappings and postpones copying until required.

Copy-on-Write is also beneficial when a child process quickly executes another program using exec(). Much of the parent's memory never needs to be copied because the child's original address space is replaced.

It also improves the efficiency of systems that create many processes or frequently use process-based workloads.

Limitations of Copy-on-Write

Copy-on-Write does not eliminate memory usage completely. If both the parent and child modify many different pages, those pages eventually need separate physical copies.

For example, if a process has 1 GB of memory and both processes modify a large portion of that memory, the system may eventually require substantially more physical memory.

There is also some processing overhead when a COW page fault occurs. The kernel must allocate a new page, copy the contents, update the page tables, and resume execution.

Therefore, Copy-on-Write is most beneficial when shared pages are primarily read or when the child quickly replaces its memory using exec().

Copy-on-Write and Process Isolation

Although parent and child processes may initially share physical pages, they do not normally share writable memory directly.

The operating system uses memory-protection mechanisms to ensure that a modification by one process does not unexpectedly alter another process's memory.

Once a process attempts to modify a shared page, that page is copied. Each process can then modify its own private version.

Thus, Copy-on-Write combines two seemingly conflicting goals:

Efficient Memory Sharing
          +
Process Memory Isolation
          =
Copy-on-Write

Simple Conceptual Example

Suppose the parent process has three memory pages:

Parent:
Page A
Page B
Page C

After fork():

Parent              Child
Page A  ------------ Page A
Page B  ------------ Page B
Page C  ------------ Page C

The physical pages are shared.

If the child modifies Page B:

Parent              Child
Page A  ------------ Page A
Page B                Page B'
Page C  ------------ Page C

Only Page B needs to be copied. Pages A and C can continue to be shared.

This illustrates an important principle: Copy-on-Write copies memory at the page level rather than copying the entire address space immediately.

Copy-on-Write in Modern UNIX-Like Systems

Modern UNIX-like operating systems, including Linux and other systems derived from UNIX concepts, use sophisticated virtual-memory mechanisms to implement forms of Copy-on-Write.

The exact implementation details differ between operating systems, architectures, and kernel versions. However, the basic principle remains the same: memory pages can initially be shared, and a private copy is created when a process needs to modify a shared page.

Copy-on-Write is also used in other areas of operating systems, such as memory mapping and certain filesystem implementations. The general idea is always similar: share data while it remains unchanged and create a separate copy only when modification occurs.

Conclusion

UNIX Copy-on-Write is an important technique for improving the efficiency of process creation and virtual memory management. After fork(), the parent and child can initially share physical memory pages rather than immediately duplicating the entire address space. When either process attempts to modify a shared page, the operating system detects the write, creates a private copy, updates the relevant memory mappings, and allows the modification to proceed.

The technique is particularly valuable in the common fork() followed by exec() model because it prevents large amounts of memory from being copied unnecessarily. By combining delayed copying, virtual memory, page protection, and page-fault handling, Copy-on-Write provides efficient memory utilization while preserving the isolation expected between UNIX processes.