原理
LRU
LRU是一种页面置换算法,在对于内存中但是又不用的数据块,叫做LRU,操作系统会根据那些数据属于LRU而将其移出内存而腾出空间来加载另外的数据
LRU算法:最近最少使用,简单来说就是将数据块中,每次使用过的数据放在数据块的最前端,然后将存在的时间最长的,也就是数据块的末端的数据剔除掉这就是LRU算法
如果进程被调度,该进程需要使用的外存页(数据)不存在于数据块中,这个现象就叫做缺页。如果这个数据此时不在,就会将这个数据从加入到数据块首部
数据块插入与剔除:每次有新数据到来时,会将其放入数据块首部,当数据每次被访问时,将这个数据插入数据块的首部如果数据块满了,每次新进的数据都会将数据块尾部的数据挤出数据块
为什么要使用LRU
关于操作系统内存管理,如何节省利用容量不大的内存多的进程提供资源。而内存的虚拟存储管理是现在最通用,最成功的方式。在内存有限的情况下,扩展一部分外存作为虚拟内存,真正的内存只存储当前运行时所用到的信息,极大的扩充了内存的功能,极大提高计算机的并发度,虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,将其余页面放入外存的管理方式
虚拟页式存储管理增加了进程所需的内存空间,却也带来了运行时间变长这一缺点,运行过程中,需要将外存中存放的信息和内存中已有的进行交换,由于外存的低速,影响了运行时间因此,应当采取尽量好的算法以减少读取外存的次数
对于虚拟页式存储,内外存信息替换是以页面为单位进行的,当需要一个外存中的页面是 将它调入内存,同时为了保持原有空间大小,我们需要不断地剔除掉一些存页面。数据块大小有限,因此我们需要每次调用外存数据时,都能准确的命中。这时就需要一个较好的页面管理算法。
实现图例
LRU页面置换算法用双向队列实现,共有三个操作,插入,淘汰,删除,插入将页插到队列开始位置,淘汰淘汰队列末端页面,删除则直接删除特定某一页。
代码
抽象类
template <typename T> class Replacer {
public:
Replacer() {}
virtual ~Replacer() {}
virtual void Insert(const T &value) = 0;
virtual bool Victim(T &value) = 0;
virtual bool Erase(const T &value) = 0;
virtual size_t Size() = 0;
};
头文件
template <typename T> class LRUReplacer : public Replacer<T> {
struct Node {
Node() {};
Node(T val) : val(val) {};
T val;
shared_ptr<Node> prev;
shared_ptr<Node> next;
};
public:
// do not change public interface
LRUReplacer();
~LRUReplacer();
void Insert(const T &value);
bool Victim(T &value);
bool Erase(const T &value);
size_t Size();
private:
shared_ptr<Node> head;
shared_ptr<Node> tail;
unordered_map<T,shared_ptr<Node>> map;
mutable mutex latch;
// add your member variables here
};
实现
template <typename T> LRUReplacer<T>::LRUReplacer() {
head = make_shared<Node>();
tail = make_shared<Node>();
head->next = tail;
tail->prev = head;
}
template <typename T> LRUReplacer<T>::~LRUReplacer() {
while( head ) {
shared_ptr<Node> tmp = head->next;
head->next = nullptr;
head = tmp;
}
while (tail) {
shared_ptr<Node> tmp = tail->prev;
tail->prev = nullptr;
tail = tmp;
}
}
/*
* Insert value into LRU
*/
template <typename T> void LRUReplacer<T>::Insert(const T &value) {
lock_guard<mutex> lck(latch);
shared_ptr<Node> cur;
if (map.find(value) != map.end()) {
cur = map[value];
shared_ptr<Node> prev = cur->prev;
shared_ptr<Node> succ = cur->next;
prev->next = succ;
succ->prev = prev;
} else {
cur = make_shared<Node>(value);
}
shared_ptr<Node> fir = head->next;
cur->next = fir;
fir->prev = cur;
cur->prev = head;
head->next = cur;
map[value] = cur;
return;
}
/* If LRU is non-empty, pop the head member from LRU to argument "value", and
* return true. If LRU is empty, return false
*/
template <typename T> bool LRUReplacer<T>::Victim(T &value) {
lock_guard<mutex> lck(latch);
if (map.empty()) {
return false;
}
shared_ptr<Node> last = tail->prev;
tail->prev = last->prev;
last->prev->next = tail;
value = last->val;
map.erase(last->val);
return true;
}
/*
* Remove value from LRU. If removal is successful, return true, otherwise
* return false
*/
template <typename T> bool LRUReplacer<T>::Erase(const T &value) {
lock_guard<mutex> lck(latch);
if (map.find(value) != map.end()) {
shared_ptr<Node> cur = map[value];
cur->prev->next = cur->next;
cur->next->prev = cur->prev;
}
return map.erase(value);
}
template <typename T> size_t LRUReplacer<T>::Size() {
lock_guard<mutex> lck(latch);
return map.size();
}