5.1 Lazy allocation
xv6’s lazy allocation has two parts.
First, when an application asks for memory by calling
sbrk with the flag SBRK_LAZY, the kernel notes the increase in size, but does not
allocate physical memory and does not create PTEs for the new range of
virtual addresses. Second, on a page fault on one of those new
addresses, the kernel allocates a page of physical memory and maps it
into the page table. The kernel implements lazy allocation
transparently to applications: no modifications to applications are
necessary for them to benefit.
Lazy allocation is convenient for applications because they don’t have to accurately predict how much memory they will need. For example, an application may process input, but not know in advance how large the input will be. With lazy allocations an application can ask for memory for the worst case, but not have to pay for this worst case: the kernel doesn’t have to do any work at all for pages that the application never uses.
Furthermore, if the application is asking to grow the address space by
a lot, then sbrk without lazy allocation is expensive: if
an application asks for a gigabyte of memory, the kernel has to
allocate and zero 262,144 4096-byte physical pages. Lazy allocation allows
this cost to be spread over time. On the other hand, lazy allocation
incurs the extra overhead of page faults, which involve a user/kernel
transition. Operating systems can reduce this cost by allocating a
batch of consecutive pages per page fault instead of one page and by
specializing the kernel entry/exit code for such page-faults (though
xv6 does neither).
On the other hand, when taking a page fault for a lazily-allocated
page, the kernel may find that it has not free memory to allocate. In
this case, the kernel has no easy way of returning an out-of-memory
error to the application and instead kills the application. For
applications that prefer an error on a failed allocation, xv6 allows
an application to allocate memory eagerly by calling sbrk
with the flag SBRK_EAGER.