struct frame_entry * find_victim(void) { cursor = list_begin(&frame_table); struct frame_entry *victim = NULL; while(true) { struct frame_entry *temp = list_entry(cursor, struct frame_entry, elem); if(pagedir_is_accessed(temp->t->pagedir, temp->frame)) { pagedir_set_accessed(temp->t->pagedir, temp->frame, false); pagedir_set_accessed(temp->t->pagedir, temp->spe->uaddr, false); } else { victim = temp; cursor = list_next(cursor); if(cursor == list_end(&frame_table)) cursor = list_begin(&frame_table); return victim; } cursor = list_next(cursor); if(cursor == list_end(&frame_table)) cursor = list_begin(&frame_table); } return victim; }
/*! Use the clock algorithm to select a frame to evict and execute the eviction. */ void * frame_evict(enum palloc_flags flag) { struct list_elem *ce; struct thread *ct; struct frame_table_entry *cf; int i; if (f_table.lock.holder != thread_current()) lock_acquire(&f_table.lock); /* Get frame table lock */ if (list_empty(&f_table.table)) { lock_release(&f_table.lock); /* Nothing to evict */ return NULL; } f_table.hand = f_table.hand % list_size(&f_table.table); ce = list_begin(&f_table.table); /* Begin iteration */ for (i = 0; i < f_table.hand; i++) { /* Find where the "hand" points to: that is where the iteration starts */ ce = list_next(ce); } while (true) { cf = list_entry(ce, struct frame_table_entry, elem); ct = cf->owner; if (!cf->spt->pinned) { /* If pinned, skip this frame */ if (pagedir_is_accessed(ct->pagedir, cf->spt->upage)) /* If accessed, clear accessed bit */ pagedir_set_accessed(ct->pagedir, cf->spt->upage, false); else { /* Swap out the page! */ if (cf->spt->type != SPT_MMAP) { cf->spt->fr = NULL; cf->spt->type = SPT_SWAP; cf->spt->swap_index = swap_out(cf->physical_addr); } /* Write the frame back to the map'd file */ else if (cf->spt->type == SPT_MMAP && pagedir_is_dirty(ct->pagedir, cf->spt->upage)) { cf->spt->fr = NULL; file_write_at(cf->spt->file, cf->physical_addr, cf->spt->read_bytes, cf->spt->ofs); } /* Clear the frame */ list_remove(ce); pagedir_clear_page(ct->pagedir, cf->spt->upage); palloc_free_page(cf->physical_addr); free(cf); lock_release(&f_table.lock); return palloc_get_page(flag); } } ce = list_next(ce); ++f_table.hand; if (ce == list_end(&f_table.table)) { /* Iterate circularly */ ce = list_begin(&f_table.table); f_table.hand = 0; } } }
/** * Check if this page was accessed recently * Returns true if the PTE for virtual page VPAGE in PD has been * accessed recently, that is, between the time the PTE was * installed and the last time it was cleared. Returns false if * PD contains no PTE for VPAGE. */ bool is_page_accessed (struct supplementary_page_table_entry *page) { ASSERT(page -> supplementary_frame_in_memory != NULL) ASSERT( lock_held_by_current_thread ( &page->supplementary_frame_in_memory->this_lock)); bool access = pagedir_is_accessed (page->sup_thread->pagedir, page->vaddr); if (access) { pagedir_set_accessed (page->sup_thread->pagedir, page->vaddr, false); } return access; }
void * frame_evict (enum palloc_flags flags) { lock_acquire (&frame_table_lock); struct list_elem *e = list_begin (&frame_table); while (true) { struct frame_entry *frame_entry = list_entry (e, struct frame_entry, elem); struct SP_entry *page_entry = frame_entry->page_entry; if (!page_entry->pinned) { struct thread *t = frame_entry->thread; if (pagedir_is_accessed (t->pagedir, page_entry->page)) { pagedir_set_accessed (t->pagedir, page_entry->page, false); } else { if (pagedir_is_dirty (t->pagedir, page_entry->page) || page_entry->type == SP_SWAP) { if (page_entry->type == SP_MMAP) { lock_acquire (&filesys_lock); file_write_at (page_entry->file, frame_entry->frame, page_entry->read_bytes, page_entry->offset); lock_release (&filesys_lock); } else { page_entry->type = SP_SWAP; page_entry->swap_index = swap_out (frame_entry->frame); } } page_entry->is_loaded = false; list_remove (&frame_entry->elem); pagedir_clear_page (t->pagedir, page_entry->page); palloc_free_page (frame_entry->frame); free (frame_entry); lock_release (&frame_table_lock); return palloc_get_page (flags); } } e = list_next (e); if (e == list_end (&frame_table)) { e = list_begin (&frame_table); } } }
/* * select one frame to kick out * the page inside it. We implement * Clock Algorithm here. */ struct frame_table_entry* clock_kick(){ struct list_elem *elem; clock: elem = list_begin(&frame_table); while (elem != list_end(&frame_table)) { struct frame_table_entry* fte; fte = list_entry(elem, struct frame_table_entry, elem); ASSERT(fte!=NULL); if(!pagedir_is_accessed(fte->owner->pagedir,fte->pg_vaddr) && fte->pinned == false){ return fte; }else{ pagedir_set_accessed(fte->owner->pagedir,fte->pg_vaddr,0); } elem = list_next(elem); } goto clock; NOT_REACHED(); return NULL; }
struct frame_table_entry* frame_evict_choose_secondchance (void) { struct frame_table_entry *fte = frame_evict_choose_fifo (); if (pagedir_is_accessed (fte->owner->pagedir, fte->uaddr)) { pagedir_set_accessed (fte->owner->pagedir, fte->uaddr, false); lock_acquire (&frame_table_lock); list_remove (&fte->elem); list_push_front (&frame_table, &fte->elem); lock_release (&frame_table_lock); return frame_evict_choose_secondchance (); } else { return fte; } }
/* The implementation of LRU 'clock' algorithm follows.. we make use of the PTE's accessed bit and dirty bit: on any read or write to a page ==> accessed bit = 1; on any write ==> dirty bit = 1; Step 1: We navigate through the pages in a circular order.. i.e., if we hit the end of the frame list, we circle back to the start and continue our processing.. Step 2: If the accessed bit is 'accessed', turn it to 'not accessed', skip the page and proceed to look-up the next one in the list Step 3: If the accessed bit is 'not accessed', we can proceed with our page replacement */ void evict_page() { struct list_elem *e; int count = 0; lock_acquire(&ftable_lock); for (e = list_begin(&frame_list); e != list_end(&frame_list); e = list_next(e)) { struct frame *frame = list_entry(e, struct frame, frame_list_elem); // printf ("\n%d , %d", count, list_size(&frame_list) - 1); if (count != list_size(&frame_list) - 1) { // get the accessed flag for the current page bool accessed_flag = pagedir_is_accessed( frame->page->frame_holder_thread->pagedir, frame->page->addr); if (accessed_flag) pagedir_set_accessed(frame->page->frame_holder_thread->pagedir, frame->page->addr, !accessed_flag); else { // we need to page replace.. i.e., // step 1: remove the existing page from the frame (swap_out call) // step 2: remove the existing page from the page directory // step 3: set the accessed flag of the page to 'accessed' // step 4: free the page and free the frame, for subsequent use list_remove(e); swap_out(frame->page); frame->page->swap_flag = 1; pagedir_clear_page(frame->page->frame_holder_thread->pagedir, frame->page->addr); palloc_free_page(frame->base); free(frame); // frame->page->swap_flag = 1; break; } count++; } else { count = 0; e = list_begin(&frame_list); } } lock_release(&ftable_lock); }
struct frame_table_entry* pick_frame_to_evict( uint32_t *pagedir ) { size_t n = hash_size(&frame_map); if(n == 0) PANIC("Frame table is empty, can't happen - there is a leak somewhere"); size_t it; for(it = 0; it <= n + n; ++ it) // prevent infinite loop. 2n iterations is enough { struct frame_table_entry *e = clock_frame_next(); // if pinned, continue if(e->pinned) continue; // if referenced, give a second chance. else if( pagedir_is_accessed(pagedir, e->upage)) { pagedir_set_accessed(pagedir, e->upage, false); continue; } // OK, here is the victim : unreferenced since its last chance return e; } PANIC ("Can't evict any frame -- Not enough memory!\n"); }