When a process is created via fork, it starts out with a page directory and a page or so of the executable. So the page fault handler is the source of most of a processes' memory.
The page fault handler do_page_fault() retrieves the faulting address from the register cr2. The error code (retrieved in sys_call.S) differentiates user/supervisor access and the reason for the fault -- write protection or a missing page. The former is handled by do_wp_page() and the latter by do_no_page().
If the faulting address is greater than TASK_SIZE the process receives a SIGKILL. [Why this check? This can only happen in kernel mode because of segment level protection.]
These routines have some subtleties as they can get called from an interrupt. You can't assume that it is the `current' task that is executing.
do_no_page() handles three possible situations:
In all cases get_empty_pgtable() is called first to ensure the existence of a page table that covers the faulting address. In case 3 get_empty_page() is called to provide a page at the required address and in case of the swapped page, swap_in() is called.
In case 2, the handler calls share_page() to see if the page is shareable with some other process. If that fails it reads in the page from the executable or library (It repeats the call to share_page() in case another process did the same meanwhile). Any portion of the page beyond the brk value is zeroed.
A page read in from the disk is counted as a major fault ( maj_flt). This happens with a swap_in() or when it is read from the executable or a library. Other cases are deemed minor faults (min_flt).
When a shareable page is found, it is write-protected. A process that writes to a shared page will then have to go through do_wp_page() which does the copy-on-write.
do_wp_page() does the following: