The exact mechanism is given here, on Linux: in handling a page fault on anonymous mappings you check to see whether it's a "grown does allocation" that you should expand like a stack. If the VM area record says you should, then you adjust the start address to expand the stack.
When a page fault occurs, depending on the address, it may be serviced (and the fault quashed) via stack expansion. This "growing downwards on a fault" behavior for virtual memory can be requested by arbitrary user programs with the MAP_GROWSDOWN
flag being passed to the mmap
syscall.
You can mess around with this mechanism in a user program as well:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
int main() {
long page_size = sysconf(_SC_PAGE_SIZE);
void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (MAP_FAILED == mem) {
perror("failed to create growsdown mapping");
return EXIT_FAILURE;
}
volatile char *tos = (char *) mem + page_size;
int i;
for (i = 1; i < 10 * page_size; ++i)
tos[-i] = 42;
fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
(void) getchar();
if (munmap(mem, page_size))
perror("failed munmap");
return EXIT_SUCCESS;
}
When it prompts you find the pid of the program (via ps
) and look at /proc/$THAT_PID/maps
to see how the original area has grown.