MADV_DONTFORK changes a specific part of Linux process creation: a mapping marked with this advice is not made available in the child created by fork(). The parent keeps the mapping. The child starts without that address range, so an address that was valid there in the parent is not automatically valid in the child.
This is a semantic control over mapping inheritance, not a cache hint. It belongs to the Linux-specific madvise() operations whose effects can change memory behavior.
The advice is attached to an address range
madvise() operates on the range beginning at a page-aligned address. With MADV_DONTFORK, Linux records that the selected mapping range must be excluded when a later fork() constructs the child’s address space.
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);
}The call does not unmap p from the current process. Code in the parent can continue using the mapping according to its existing protection and mapping rules. The difference appears at a subsequent fork() boundary.
That distinction matters for code that treats madvise() only as performance advice. Several conventional advice values mainly communicate expected access patterns, but Linux also defines operations with observable semantic effects. MADV_DONTFORK is one of them.
The child receives a hole instead of inherited pages
Normal fork() duplicates the parent’s virtual address-space layout, with private writable mappings typically using copy-on-write behavior. MADV_DONTFORK removes the selected range from that inheritance model.
The child does not receive a private copy of the range, nor a copy-on-write view of it. The range is absent. Dereferencing an address solely because it was valid in the parent can therefore fault in the child.
This property is different from clearing data after a fork. MADV_WIPEONFORK, for example, gives the child zero-filled memory for eligible private anonymous ranges while retaining a mapping at the address. MADV_DONTFORK instead prevents the range from being made available to the child.
The distinction affects invariants. A child-side pointer into a MADV_DONTFORK range is stale as an address-space reference, even if the numeric pointer value was copied as part of another inherited object.
Copy-on-write can be the behavior being avoided
The Linux manual documents a hardware-facing use case for MADV_DONTFORK: avoiding copy-on-write relocation of a physical page when the parent writes after fork(). A device performing DMA into memory can depend on physical-page relationships established before the fork. A later copy-on-write event can create a different physical page for a process mapping, which may violate assumptions made by the device-facing setup.
Excluding the range from the child removes that mapping from the fork copy-on-write relationship. The mechanism does not itself configure DMA, pin pages, or establish device ownership. It only changes inheritance of the advised virtual-memory range.
This boundary is important because MADV_DONTFORK is not a general substitute for the APIs required by a device or driver. The surrounding subsystem still defines pinning, lifetime, synchronization, and ownership requirements.
MADV_DOFORK restores default inheritance
MADV_DOFORK reverses the effect of MADV_DONTFORK. After it succeeds for a range, a later fork() again uses the default inheritance behavior for that mapping.
if (madvise(p, page, MADV_DONTFORK) == 0) {
/* Later policy change */
madvise(p, page, MADV_DOFORK);
}The reversal affects future forks. It does not retroactively insert the mapping into children that were already created while the range carried the MADV_DONTFORK setting.
That makes the advice part of mapping lifecycle state. A runtime can establish a range, mark it as excluded, keep using it in the parent, and restore normal inheritance before a later fork if its process model changes.
Pointer graphs can cross the inheritance boundary
A subtle failure mode appears when an inherited object contains pointers into a non-inherited range. The object holding the pointer may exist in the child, while its target does not.
For example, a heap structure inherited normally might contain a pointer to a special arena marked MADV_DONTFORK. After fork(), the structure and pointer value remain visible, but following that pointer reaches an unmapped address.
The kernel cannot repair such application-level references. Software using this advice needs an ownership boundary that matches the memory boundary. Child-side code may need to discard, replace, or reinitialize state that can reference excluded mappings.
This is also a reason to keep the advised region conceptually narrow. Marking a range changes the topology of the child’s address space, and data structures outside that range can still retain numeric addresses into it.
The effect is scoped to fork inheritance
MADV_DONTFORK does not make memory secret from the parent, erase its contents, or revoke current access. It does not provide synchronization between threads, and it does not replace mprotect() access controls.
Its contract is narrower: selected pages are not made available to a child across fork(). MADV_DOFORK restores the default inheritance policy for later forks.
That narrow contract makes the operation useful when a mapping has a lifecycle that must remain parent-local. The key design constraint is not merely the mapping itself, but every inherited object that can retain a reference into the excluded range.