The Linux kernel uses lazy (on-demand) allocation of physical pages,
deferring the allocation until necessary and avoiding allocating physical
pages which will never actually be used.
Memory mappings generally start out with no physical pages attached. They
define virtual address ranges without any associated physical memory. So
malloc() and similar allocate space, but the actual memory is allocated
later by the page fault handler.
Virtual pages with no associated physical page will have the read,
write, and execute bits disabled in their page table entries. This causes
any access to that address to generate a page fault, interrupting the
program and calling the page fault handler.
When the page fault handler needs to allocate physical memory to handle
a page fault, it zeroes a free physical page (or grabs a page from a pool
of prezeroed pages), attaches that page of memory to the Page Table Entry
associated with the fault, updates that PTE to allow the appropriate access,
and resumes the faulting instruction.
Note that implementing this requires two sets of page table flags for
read, write, execute, and share. The VM_READ, VM_WRITE, VM_EXEC, and
VM_SHARED flags (in linux/mm.h) are used by the MMU to generate faults.
The VM_MAYREAD, VM_MAYWRITE, VM_MAYEXEC, and VM_MAYSHARE flags are used by
the page fault handler to determine whether the attempted access was legal
and thus the fault handler should adjust the PTE to resolve the fault and
allow the process to continue.