In the previous lecture we saw that we needed a function, Bpid(va, cpl) to allocate a page in the virtual memory for a process with process ID pid where va is the virtual address of the page and cpl is the current protection level of the process. This function either returns a physical address (corresponding to the virtual address specified) or a page fault.
To implement Paged Virtual Memory we need another function-
swapmap(pid, va)
This function tells us if the process with ID = pid has a page with virtual address = va swapped out to disk or not. If the page at va has been swapped out, this function returns the location in disk where the page is now located. It returns NULL if the page has not been swapped out.
NOTE: When the process begins execution, swapmap() will return NULL for all virtual addresses because no pages have been swapped out yet!
How do we allocate a page at a given virtual memory for a given process?
If a free page of physical memory exists then we mark that page as allocated. If no free physical memory space exists then we need to swap a page out. To do this we need to do the following
Finally, we need to assign the page to the process that wanted it by updating its script B function.
We can thus define a function alloc_page() that allocates a page for a process with ID = pid at virtual address = va.
/* NOTE: Before calling the function, B(va, any cpl) = PF for the process because we haven't allocated a page at va yet*/ alloc_page(pid, va) { if(pa = get_free_page()) mark pa as allocated else //We need to swap out { (swap_pid, swap_va) = choose_page_swap(); swap_pa = Bswap_pid(swap_pa); Bswap_pid(swap_va, any cpl) = PF; swap_diskaddr = alloc_swap_location(); save_page(swap_pid, swap_va); swapmap(swap_pid, swap_va) = swap_diskaddr; pa = swap_pa; } Bpid(va, cpl) = pa; }
What happens now when the process with ID swap_pid wants to access data at location swap_va?
A PF occurs which will be handled as a segmentation fault. This is bad! We know that the data is now at a disk location. Therefore we need to update the PF handler as follows to fix this-
PF(pid, va,cpl) { if(swapmap(pid, va) != NULL) //Block has been swapped out to disk { alloc_page(pid,va); load_page(swapmap(pid,va), Bpid(va)); swapmap(pid, va) = NULL; //The page is not swapped out anymore return; } else //Regular case //cause segmentation fault. }
In the previous lecture we saw how to implement LRU using an age function to update the age of a physical address at every quantum. But this was expensive because we had to deal with a page fault at every accessed page.
We can improve this by coming up with a hardware solution: the memory management unit keeps track of whether a page was recently accessed. In particular, the x86 memory management unit assigns a bit called the ACCESSED bit to every page in page tables and sets this bit to high when a virtual address is used.
NOTE: Only OS/ Kernel can reset the ACCESSED bit to 0.
We can implement LRU now as follows-
On every timer interrupt Walk all page tables For all pid, va if Bpid (va) was accessed { age(pid, va) = 0; Reset ACCESSED bit. } else age(pid, va)++;
We thus got rid of the cost of page faults making LRU less expensive.
This basically means that if you don’t use a program, don’t bother loading it into memory. The motivation for demand paging is to-
In order to run a process without loading its data we use the swapmap function which tells us the locations where the data is actually stored. Thus before demand paging loading a process would be-
load_process(pid) { for every disk location/va { alloc_page(pid, va); red_from_disk(Bpid(va), location); } }
After demand paging loading a process would be-
load_process(pid) { for every disk location/va { swapmap(pid,va) = location. } }
the following edited by charles mata
An example: if the MS Word paperclip is swapped out of memory because we are not using it, we don't want to bother writing it to the disk unless the data on the page has changed.
We can distinguish a dirty page from a clean one by trapping writes by throwing a page fault when someone writes to it.
This means that the following (pseudocode) changes are made to our script-B function:
// Changes to B Bc( va, cpl, op ) // where op is either reading or writing To track 'dirtiness' initially B(va, CPL, write) <- PF for all va PF(va, cpl, op) if(swapped out) swap in; else if( write == op && Bpid(va, cpl, read) != PF) { dirty(pid, va) <- true; Bpid(va, cpl, write) <- Bpid(va, cpl, read); // allow a write } if( op == write && copy_on_write(pid, va) == true) { old_pa = Bpid(va, cpl, read); alloc_page (pid_va); copy old pa's data onto Bpid(va, cpl, write); }
Hardware support for Dirty Bit: this would mark a dirty bit in B whenever va is written. The advantage to this is that we have lesser page faults and hence we avoid the expenditure of page faults.
need to insert image of the parent's va, physicall address space, and child's va.
Example:
if ( (p=fork()) == 0 { ... execvp("ls", ...); }
int x; if ( (p=fork()) == 0 ) x = 100; execvp(...); else x = 200
If we just copy over the va space for the new process and have the new va map to the same physical address space, we violate ISOLATION of the two processes. But if we create new physical address space for a child each time and copy data over, then we would be wasting time because execvp would erase the old pa and load it with new data.
Therefore, we copy va over and mark all data as read only. When we write to a page a page fault occurs and the page fault handler is changed as follows to handle the case-
if (op == write && cow(pid,va) == true ) { old_pa = B(va, cpl, read); alloc_page(pid, va); copy old_pas data onto Bpid(va, cpl, write); Get old data, alloc page and copy into new page and mark it as writable }
In general W<<N. Therefore, real OS use some form of COW.
end of charles' section
System for storing named data on disk (Persistently).
Directory [ name, size, first sector]
External Fragmentation issue.
fewer seeks (worst case)
Internal fragmentation issue.
FAT - File Allocation Table