How does the Page Fault handler get to know that Non-Present, User page is due to an unallocated
page or a swapped
I am assuming the Non-Present pages were marked as 'User'. Also I am assuming that my App is bigger
with only 1 Page Present in memory due to RAM memory constraints for the moment (demand paging).
Is it because there should be some other attribute
at the Non-Present Page Entry (like Allocated/Unallocated)? If you could tell how this issue is done with popular OS's or one you know best it will be helpful.
Everyone does this differently. I'll describe the way I do it..
For each "present" page I figure out (enumerate) all the different types that the page could be. Example:
- present normal RAM
- present and "pinned" so it can't be sent to swap space (e.g. part of disk driver needed to access swap space)
- present and part of a memory mapped device (where the page should never be "freed" in the normal way)
- present but part of allocate on write area
- present but part of a copy on write area
- present but part of a memory mapped file
- present and a copy of the page already exists in swap space
For these; I sort them into 2 lists, one for "read only" and another for "read/write". There are some (3 or more depending on what sort of paging you're using) "available for OS use" flags in page table entries, and the goal is to store the most information you can in them. For example, "present normal RAM" might be "type 000b for read/write", "part of memory mapped device" might be "type 001b for read/write", etc. For whatever can't fit (e.g. some reference to which file it is, for "present but part of memory mapped file") you'd need additional information somewhere (e.g. a list of "memory mapped file area" structures).
For each "not present" page it's similar - figure out (enumerate) all the different types that the page could be. However, in this case there are many (31 or more depending on what sort of paging you're using) "available for OS use" flags (all bits except the "present" flag itself is an "available flag"). This means that you can split the (31-bit or larger) value it into ranges, where it might end up being vaguely like:
0x00000000 = not present, not allocated page
0x00000001 to 0x0000000F = reserved and/or other stuff
0x00000010 to 0x0FFFFFFF = not present, part of memory mapped file, entry number in list of "memory mapped file area" structures is "value - 0x00000010"
0x10000000 to 0x7FFFFFFF = not present read/write normal page stored in swap space, location in swap space is "value - 0x10000000"
Note that for this crude example; there'd be a maximum of about 260 million memory mapped files per process, and a maximum of 7 TiB of swap space. That's plenty for a 32-bit system (and for 64-bit you get a lot more "available flags").
Also note that the same "enumerate types and encode type in available bits" approach can be used for page directory entries (and page directory pointer table entries, and so on). This means that you can have (e.g.) a 4 MiB area marked as "not present, part of memory mapped file" without needing to allocate/use all the page tables.
Mostly (with a little cleverness); the page fault handler can figure out exactly what it needs to do in every possible case; by using the paging structures (page tables, page directories, etc) themselves (which are mostly unavoidable), plus some kind of list of "memory mapped file area" structures, plus something that manages swap space that remembers which pages in swap space are used for what (for the "present page where copy of the page already exists in swap space" case only).
Note that for my OS I support "area is part of a memory mapped file where all pages are in VFS cache"; but don't support memory mapped files where the pages are on disk (it's bad for fault tolerance in the "page fault handler gets read error from disk drive" case), don't support shared memory between processes (it's bad for performance for distributed systems) and don't support "fork-style copy on write" (also bad for performance for distributed systems). This tends to make virtual memory management less complicated; partly because reference counting (tracking how many processes are using a page) is only needed for the "area is part of a memory mapped file where all pages are in VFS cache" case and can therefore be delegated (done by VFS and not done by virtual memory management).