MADV_DONTFORK mengubah satu bagian spesifik dari pembuatan proses di Linux: mapping yang ditandai dengan advice ini tidak dibuat tersedia pada child hasil fork(). Parent tetap memiliki mapping tersebut. Child dimulai tanpa range address itu, sehingga address yang valid pada parent tidak otomatis valid pada child.

Ini adalah kontrol semantik terhadap pewarisan mapping, bukan sekadar petunjuk cache. Operasi ini termasuk extension madvise() khusus Linux yang dapat mengubah perilaku memori yang terlihat oleh program.

Advice melekat pada range address

madvise() bekerja pada range yang dimulai dari address dengan alignment page. Dengan MADV_DONTFORK, Linux mencatat bahwa range mapping tersebut harus dikecualikan ketika fork() berikutnya membentuk address space milik child.

long page = sysconf(_SC_PAGESIZE);
void *p = mmap(NULL, page,
               PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS,
               -1, 0);

if (p != MAP_FAILED) {
    madvise(p, page, MADV_DONTFORK);
}

Pemanggilan itu tidak melakukan unmap terhadap p pada proses saat ini. Kode pada parent tetap dapat memakai mapping sesuai protection dan aturan mapping yang sudah berlaku. Perbedaannya muncul pada batas fork() berikutnya.

Pemisahan ini penting karena madvise() sering diasosiasikan dengan petunjuk performa. Sejumlah advice konvensional terutama menyampaikan pola akses yang diperkirakan, tetapi Linux juga menyediakan operasi dengan efek semantik yang dapat diamati. MADV_DONTFORK termasuk kelompok kedua.

Child menerima hole, bukan page hasil pewarisan

Secara normal, fork() menduplikasi layout virtual address space milik parent, sedangkan private writable mapping biasanya memakai mekanisme copy-on-write. MADV_DONTFORK mengeluarkan range terpilih dari model pewarisan tersebut.

Child tidak menerima private copy dari range itu dan juga tidak menerima view copy-on-write. Range tersebut tidak ada. Dereference terhadap address hanya karena address itu valid pada parent dapat menghasilkan fault pada child.

Perilaku ini berbeda dari mengosongkan data setelah fork. MADV_WIPEONFORK, misalnya, memberi child memori berisi nol untuk private anonymous range yang memenuhi syarat, sementara mapping tetap ada pada address tersebut. MADV_DONTFORK mencegah range tersedia pada child.

Perbedaannya memengaruhi invariant program. Pointer pada sisi child yang mengarah ke range MADV_DONTFORK menjadi referensi address space yang tidak valid, walaupun nilai numerik pointer ikut tersalin melalui objek lain yang diwariskan.

Copy-on-write dapat menjadi perilaku yang perlu dihindari

Manual Linux mencatat penggunaan MADV_DONTFORK yang terkait hardware: mencegah relokasi physical page akibat copy-on-write ketika parent menulis setelah fork(). Device yang melakukan DMA ke memori dapat bergantung pada relasi physical page yang dibentuk sebelum fork. Event copy-on-write setelahnya dapat menghasilkan physical page berbeda bagi mapping proses dan merusak asumsi pada konfigurasi yang berhadapan dengan device.

Mengecualikan range dari child mengeluarkan mapping tersebut dari relasi copy-on-write akibat fork. Mekanisme ini tidak mengonfigurasi DMA, tidak melakukan pin terhadap page, dan tidak menetapkan ownership device. Efeknya terbatas pada pewarisan range virtual memory yang diberi advice.

Batas tersebut penting karena MADV_DONTFORK bukan pengganti umum untuk API yang diwajibkan device atau driver. Subsystem terkait tetap menentukan aturan pinning, lifetime, sinkronisasi, dan ownership.

MADV_DOFORK memulihkan pewarisan default

MADV_DOFORK membalik efek MADV_DONTFORK. Setelah operasi ini berhasil pada suatu range, fork() berikutnya kembali memakai perilaku pewarisan default untuk mapping tersebut.

if (madvise(p, page, MADV_DONTFORK) == 0) {
    /* Perubahan kebijakan berikutnya */
    madvise(p, page, MADV_DOFORK);
}

Pembalikan tersebut berlaku untuk fork pada masa berikutnya. Operasi ini tidak memasukkan mapping secara retroaktif ke child yang sudah dibuat ketika range masih memiliki status MADV_DONTFORK.

Dengan demikian, advice ini merupakan bagian dari state lifecycle mapping. Runtime dapat membuat range, menandainya agar tidak diwariskan, tetap memakainya pada parent, lalu memulihkan pewarisan normal sebelum fork berikutnya jika model proses berubah.

Pointer graph dapat melintasi batas pewarisan

Failure mode yang lebih halus muncul ketika objek yang diwariskan menyimpan pointer menuju range yang tidak diwariskan. Objek pemilik pointer dapat tetap ada pada child, sementara target pointer tidak ada.

Sebagai contoh, struktur heap yang diwariskan secara normal dapat menyimpan pointer menuju arena khusus yang diberi MADV_DONTFORK. Setelah fork(), struktur dan nilai pointer masih terlihat, tetapi mengikuti pointer tersebut menuju address yang tidak terpetakan.

Kernel tidak dapat memperbaiki referensi pada level aplikasi seperti ini. Software yang memakai advice tersebut memerlukan batas ownership yang selaras dengan batas memorinya. Kode pada child mungkin perlu membuang, mengganti, atau menginisialisasi ulang state yang dapat merujuk ke mapping yang dikecualikan.

Ini juga menjadi alasan untuk menjaga region yang diberi advice tetap terdefinisi secara sempit. Menandai range akan mengubah topologi address space child, sedangkan data structure di luar range masih dapat menyimpan address numerik yang menunjuk ke dalamnya.

Efeknya terbatas pada pewarisan fork

MADV_DONTFORK tidak menyembunyikan memori dari parent, tidak menghapus isinya, dan tidak mencabut akses yang sedang berlaku. Operasi ini tidak menyediakan sinkronisasi antar-thread dan tidak menggantikan access control dari mprotect().

Kontraknya lebih sempit: page terpilih tidak dibuat tersedia pada child melewati fork(). MADV_DOFORK mengembalikan kebijakan pewarisan default untuk fork berikutnya.

Kontrak yang sempit ini cocok ketika lifecycle sebuah mapping harus tetap lokal pada parent. Batas desain yang perlu dijaga bukan hanya mapping tersebut, tetapi juga setiap objek yang diwariskan dan masih dapat menyimpan referensi ke range yang dikecualikan.