fork() membuat proses baru dengan virtual address space yang berasal dari proses pemanggil, tetapi Linux tidak perlu menggandakan setiap physical page privat ketika syscall selesai. Untuk mapping privat yang writable, kernel dapat mengatur page table parent dan child agar keduanya pada awalnya merujuk physical memory yang sama, sementara operasi write dibatasi oleh state copy-on-write.

Desain ini membuat biaya pembuatan proses lebih bergantung pada page table dan bookkeeping kernel daripada seluruh ukuran data privat yang resident. Penyalinan fisik ditunda sampai sebuah write membuat kedua address space harus berbeda.

Virtual address space tetap terpisah meski page fisik dibagi

Setelah fork(), parent dan child merupakan proses berbeda dengan state memory management masing-masing. Virtual mapping keduanya kemudian dapat berubah secara independen melalui operasi seperti mmap(), munmap(), dan mprotect().

Pemisahan tersebut tidak mengharuskan setiap virtual page langsung memiliki physical page unik. Page-table entry pada masing-masing proses dapat menunjuk physical page yang sama. Kernel melacak hubungan mapping dan mencegah write privat biasa mengubah data yang masih terlihat oleh proses lain.

Perbedaan penting berada pada kepemilikan virtual dan backing fisik. Dua address space independen dapat sementara berbagi storage fisik untuk byte yang belum mengalami divergensi.

Mapping privat writable menjadi kandidat copy-on-write

Mapping privat writable harus mempertahankan semantik privat setelah sebuah proses memodifikasinya. Saat fork(), Linux dapat mempertahankan isi saat ini dengan berbagi physical page dan mengatur permission page table sehingga write berikutnya tidak dapat berjalan sebagai akses writable biasa.

Secara konseptual, state tersebut seperti ini:

sebelum fork:
virtual page parent -> physical page A

setelah fork:
virtual page parent -> physical page A
virtual page child  -> physical page A
                        dibagi sampai ada write privat

Page-table entry untuk state ini tidak memberikan mapping writable tanpa batas ke page privat yang sedang dibagi. Ketika salah satu proses mencoba menulis, CPU menghasilkan page fault karena translasi saat itu tidak mengizinkan write tersebut.

Page fault pada kasus ini bukan tanda bahwa alamat tidak valid. Fault menjadi mekanisme untuk menyerahkan kontrol kepada kernel agar semantik private-write dapat dibentuk.

Write fault membuat kedua mapping berbeda

Saat Linux menangani copy-on-write fault, kernel memeriksa mapping dan state page terkait. Jika physical page masih perlu terlihat oleh mapping lain, kernel mengalokasikan page pengganti, menyalin isi lama, lalu memperbarui page table proses yang memicu fault agar merujuk salinan privat dengan permission write yang sesuai.

Hubungannya kemudian menjadi:

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

Hanya page yang terkena write yang memerlukan perlakuan ini. Proses dengan beberapa gigabyte private memory dapat melakukan fork tanpa langsung menyalin beberapa gigabyte data fisik.

Pada kondisi tertentu, penyalinan data penuh tidak diperlukan ketika fault ditangani. Jika kernel dapat memastikan mapping yang memicu fault sudah memakai page terkait secara eksklusif, permission dapat dipulihkan tanpa mempertahankan salinan fisik kedua. Jalur persisnya bergantung pada state mapping dan page.

Page table tetap membawa biaya pembuatan

Copy-on-write menghindari penyalinan eager atas isi page, tetapi fork() tetap memiliki biaya. Child memerlukan process state dan struktur memory management, sementara kernel harus membentuk mapping yang merepresentasikan address space hasil pewarisan.

Address space besar karena itu dapat membuat fork() lebih mahal walaupun data pengguna yang disalin sangat sedikit. Traversal page table, allocation, reference accounting, dan translation invalidation berikutnya tetap menghasilkan pekerjaan.

Perbedaan ini relevan untuk software yang sensitif terhadap latency. Ukuran resident memory saja tidak menggambarkan biaya fork, dan tidak adanya penyalinan page secara massal saat awal tidak membuat pembuatan proses menjadi operasi constant-time.

exec setelah fork memperbesar manfaat penyalinan tertunda

Pola peluncuran proses yang umum adalah fork() lalu segera execve(). execve() mengganti process image dengan mapping executable lain beserta runtime state-nya.

Menyalin semua private page secara eager ketika fork() akan membuang memory bandwidth jika child segera membuang mapping tersebut. Copy-on-write membuat image hasil pewarisan tetap sebagian besar dibagi sampai execve() menggantinya.

Jika child melakukan banyak write sebelum execve(), lebih banyak private page dapat terbentuk. Besarnya manfaat karena itu bergantung pada aktivitas antara pembuatan proses dan penggantian image.

Shared mapping memiliki semantik berbeda

Copy-on-write terkait dengan semantik private mapping. Mapping yang dibuat dengan MAP_SHARED memang ditujukan agar update terlihat melalui mapping lain atas shared object yang sama, sesuai aturan memory dan filesystem yang berlaku.

Mapping seperti itu tidak seharusnya diubah menjadi salinan privat independen hanya karena proses dibuat dengan fork(). Tipe mapping menentukan apakah write setelah fork merupakan shared update atau divergensi privat.

Anonymous private memory, private file mapping, shared memory, dan special mapping karena itu dapat memiliki perilaku berbeda walaupun menempati virtual address yang berdekatan.

Angka memory dapat terlihat lebih besar dari kebutuhan fisik langsung

Parent dan child dapat masing-masing melaporkan virtual atau resident mapping yang besar setelah fork(), sementara banyak physical page masih dibagi. Menjumlahkan angka memory per proses tanpa memperhitungkan sharing dapat melebihkan physical memory yang benar-benar unik untuk pasangan proses tersebut.

Ketika write bertambah, copy-on-write page berubah menjadi privat dan kebutuhan fisik dapat meningkat. Workload yang melakukan fork pada proses besar lalu memodifikasi sebagian besar memory hasil pewarisan pada akhirnya dapat memakai memory mendekati kebutuhan eager copying, tetapi allocation berlangsung bertahap.

Metric seperti proportional set size berguna ketika shared physical page perlu dibagi secara proporsional antarpenggunaannya, bukan dihitung penuh pada setiap proses.

Copy-on-write memindahkan pekerjaan ke titik mutasi

Linux fork() menggabungkan virtual address space terpisah dengan physical page yang sementara dibagi. Proteksi page table mengubah private write pertama menjadi fault terkontrol, lalu fault handling membuat physical page privat hanya saat divergensi diperlukan.

Hasilnya merupakan trade-off yang disengaja: penyalinan memory di awal berkurang dan pembuatan proses dapat lebih ringan pada banyak workload, dengan konsekuensi page-table work serta kemungkinan page-fault latency pada tahap berikutnya. Mekanisme ini sangat efektif ketika memory hasil pewarisan sebagian besar tidak berubah atau segera diganti oleh execve().