Victim(T*) : Remove the object that was accessed the least recently compared to all the elements being tracked by the Replacer, store its contents in the output parameter and return True. If the Replacer is empty return False.
Pin(T) :This method should be called after a page is pinned to a frame in the BufferPoolManager. It should remove the frame containing the pinned page from the LRUReplacer.
Unpin(T) : This method should be called when the pin_count of a page becomes 0. This method should add the frame containing the unpinned page to the LRUReplacer.
Size() : This method returns the number of frames that are currently in the LRUReplacer.
boolLRUReplacer::Victim(frame_id_t *frame_id){ std::scoped_lock lock{latch}; // If empty, it means that no victim can be found. if (lruMap.empty()) { returnfalse; } // Find the last frame and remove it. *frame_id = lru_list.back(); lruMap.erase(*frame_id); lru_list.pop_back(); returntrue; }
pin 函数实现
注意这里必须要加锁,以防止并发错误。
pin的作用是标记该页(如正在使用),防止并发错误。
如果找到该frame, 将其从replacer中删除。
1 2 3 4 5 6 7 8 9
voidLRUReplacer::Pin(frame_id_t frame_id){ std::scoped_lock lock{latch}; // Find the frame and pin it. auto it = lruMap.find(frame_id); if (it != lruMap.end()) { lru_list.erase(lruMap[frame_id]); lruMap.erase(frame_id); } }
unpin 函数实现
注意这里必须要加锁,以防止并发错误。
unpin的作用是取消标记,将该页加入replacer中。
如果在replacer中找不到该frame,且replacer不满,加入该frame。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
voidLRUReplacer::Unpin(frame_id_t frame_id){ std::scoped_lock lock{latch}; // If exist, do nothing. auto it = lruMap.find(frame_id); if (it != lruMap.end()) { return; } // if list size >= capacity, do nothing if (Size() >= capacity) { return; } // insert lru_list.push_front(frame_id); lruMap.emplace(frame_id, lru_list.begin()); }
Test
检测语法及格式
1 2 3 4 5
$ cd build $ make format $ make check-lint $ make check-clang-tidy $ make lru_replacer_test
测试
1 2 3 4 5 6 7 8 9 10 11 12
$ ./test/lru_replacer_test Running main() from gmock_main.cc [==========] Running 1 test from 1 test suite. [----------] Global test environment set-up. [----------] 1 test from LRUReplacerTest [ RUN ] LRUReplacerTest.SampleTest [ OK ] LRUReplacerTest.SampleTest (0 ms) [----------] 1 test from LRUReplacerTest (0 ms total)
[----------] Global test environment tear-down [==========] 1 test from 1 test suite ran. (0 ms total) [ PASSED ] 1 test.
/** Number of pages in the buffer pool. */ size_t pool_size_; /** Array of buffer pool pages. */ Page *pages_; /** Page table for keeping track of buffer pool pages. */ std::unordered_map<page_id_t, frame_id_t> page_table_; /** List of free pages. */ std::list<frame_id_t> free_list_;
// Put the new page in page table. page_table_.erase(page->page_id_); if (new_page_id != INVALID_PAGE_ID) { page_table_.emplace(new_page_id, new_frame_id); }
// Make the page data empty. page->ResetMemory(); page->page_id_ = new_page_id; }
FindVictimPage函数实现
这是一个辅助函数,目的是找到牺牲页。
如果free_list_不满,意味着buffer pool还有空位,找到frame_id。
如果缓冲池已满,调用LRU算法找牺牲页。
1 2 3 4 5 6 7 8 9 10
boolBufferPoolManager::FindVictimPage(frame_id_t *frame_id){ // Buffer pool is not full. Get the frame id from free_list. if (!free_list_.empty()) { *frame_id = free_list_.front(); free_list_.pop_front(); returntrue; } // Buffer pool is full. Use LRU algorithm. return replacer_->Victim(frame_id); }
Page *BufferPoolManager::FetchPageImpl(page_id_t page_id){ // 1. Search the page table for the requested page (P). // 1.1 If P exists, pin it and return it immediately. // 1.2 If P does not exist, find a replacement page (R) from either the free list or the replacer. // Note that pages are always found from the free list first. // 2. If R is dirty, write it back to the disk. // 3. Delete R from the page table and insert P. // 4. Update P's metadata, read in the page content from disk, and then return a pointer to P. std::scoped_lock lock{latch_};
auto iter = page_table_.find(page_id); if (iter != page_table_.end()) { // Find the frame id through page table. frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; // Pin it and and record it in pin count. replacer_->Pin(frame_id); page->pin_count_++; return page; }
// Cannot find. It means that this page is in the disk. frame_id_t frame_id = -1; // Find the victim page and insert the disk page instead. if (!FindVictimPage(&frame_id)) { returnnullptr; }
// Find the page and get the frame id. Page *page = &pages_[frame_id]; UpdatePage(page, page_id, frame_id); disk_manager_->ReadPage(page_id, page->data_); // pin it replacer_->Pin(frame_id); page->pin_count_ = 1;
boolBufferPoolManager::UnpinPageImpl(page_id_t page_id, bool is_dirty){ std::scoped_lock lock{latch_}; auto iter = page_table_.find(page_id); // Cannot find the page in buffer pool. if (iter == page_table_.end()) { returnfalse; } // Find the page. frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; // Check pin count. if (page->pin_count_ == 0) { returnfalse; } // Do unpin. page->pin_count_--; if (page->pin_count_ == 0) { replacer_->Unpin(frame_id); }
if (is_dirty) { page->is_dirty_ = true; }
returntrue; }
FlushPageImpl函数实现
注意这里必须要加锁,以防止并发错误。
这个函数是要把一个page写入磁盘。
在缓冲中找到该页,调用WritePage,并标记该页非脏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
boolBufferPoolManager::FlushPageImpl(page_id_t page_id){ // Make sure you call DiskManager::WritePage! std::scoped_lock lock{latch_}; auto iter = page_table_.find(page_id);
Page *BufferPoolManager::NewPageImpl(page_id_t *page_id){ // 0. Make sure you call DiskManager::AllocatePage! // 1. If all the pages in the buffer pool are pinned, return nullptr. // 2. Pick a victim page P from either the free list or the replacer. Always pick from the free list first. // 3. Update P's metadata, zero out memory and add P to the page table. // 4. Set the page ID output parameter. Return a pointer to P. std::scoped_lock lock{latch_};
frame_id_t frame_id = -1; if (!FindVictimPage(&frame_id)) { // Delete page in free list. returnnullptr; } // Get the frame id. *page_id = disk_manager_->AllocatePage(); Page *page = &pages_[frame_id]; // Update and pin page. UpdatePage(page, *page_id, frame_id); replacer_->Pin(frame_id); page->pin_count_ = 1;
boolBufferPoolManager::DeletePageImpl(page_id_t page_id){ // 0. Make sure you call DiskManager::DeallocatePage! // 1. Search the page table for the requested page (P). // 1. If P does not exist, return true. // 2. If P exists, but has a non-zero pin-count, return false. Someone is using the page. // 3. Otherwise, P can be deleted. Remove P from the page table, reset its metadata and return it to the free list. std::scoped_lock lock{latch_}; // Cannot find the page. auto iter = page_table_.find(page_id); if (iter == page_table_.end()) { returntrue; } // Get the frame id. frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; // Canot delete pin page. if (page->pin_count_ != 0) { returnfalse; }
$ cd build $ make format $ make check-lint $ make check-clang-tidy $ make buffer_pool_manager_test
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
$ ./test/buffer_pool_manager_test Running main() from gmock_main.cc [==========] Running 2 tests from 1 test suite. [----------] Global test environment set-up. [----------] 2 tests from BufferPoolManagerTest [ RUN ] BufferPoolManagerTest.BinaryDataTest [ OK ] BufferPoolManagerTest.BinaryDataTest (0 ms) [ RUN ] BufferPoolManagerTest.SampleTest [ OK ] BufferPoolManagerTest.SampleTest (0 ms) [----------] 2 tests from BufferPoolManagerTest (1 ms total)
[----------] Global test environment tear-down [==========] 2 tests from 1 test suite ran. (1 ms total) [ PASSED ] 2 tests.