设计一个页面缓存机制

题目描述:

请描述页面置换算法?
1.最佳置换算法(OPT)
2.先进先出置换算法(FIFO)
3.最少使用(LRU)置换算法

4 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key); 如果关键字 (key) 存在于缓存中,
则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value); 如果关键字已经存在,则变更其数据值;
如果关键字不存在,则插入该组「关键字/值」。
当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
再问:你是否可以在 O(1) 时间复杂度内完成这两种操作?

解答:

页面缓存机制:

在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。

描述三种页面置换算法:

最佳置换(OPT)算法
这是一种理想情况下的页面置换算法,但实际上是不可能实现的。该算法的基本思想是:发生缺页时,有些页面在内存中,其中有一页将很快被访问(也包含紧接着的下一条指令的那页),而其他页面则可能要到10、100或者1000条指令后才会被访问,每个页面都可 [1] 以用在该页面首次被访问前所要执行的指令数进行标记。最佳页面置换算法只是简单地规定:标记最大的页应该被置换。这个算法唯一的一个问题就是它无法实现。

不可实现的原因:当缺页发生时,操作系统无法知道各个页面下一次是在什么时候被访问。虽然这个算法不可能实现,但是最佳页面置换算法可以用于对可实现算法的性能进行衡量比较。

先进先出置换(FIFO)算法:
最简单的页面置换算法是先入先出(FIFO)法。这种算法的实质是,总是选择在主存中停留时间最长(即最老)的一页置换,即先进入内存的页,先退出内存。理由是:最早调入内存的页,其不再被使用的可能性比刚调入内存的可能性大。建立一个FIFO队列,收容所有在内存中的页。被置换页面总是在队列头上进行。当一个页面被放入内存时,就把它插在队尾上。

这种算法只是在按线性顺序访问地址空间 [1] 时才是理想的,否则效率不高。因为那些常被访问的页,往往在主存中也停留得最久,结果它们因变“老”而不得不被置换出去。
FIFO的另一个缺点是,它有一种异常现象,即在增加存储块的情况下,反而使缺页中断率增加了。当然,导致这种异常现象的页面走向实际上是很少见的。

最少使用置换(LRU)算法:
它的实质是,当需要置换一页时,选择在之前一段时间里最久没有使用过的页面予以置换。这种算法就称为最久未使用算法(Least Recently Used,LRU)。
LRU算法是与每个页面最后使用的时间有关的。当必须置换一个页面时,LRU算法选择过去一段时间里最久未被使用的页面。

实现一个LRU缓存机制:

分析:

我们可以运用一个链表结构,插入时插入到头部,使用数据时将该数据放到头部,然后每次要删除数据时我们都从尾部删除—尾部即为经久未用的数据。
put()两个函数要在链表中实现时间复杂度为O(1)的情况下完成是很简单的,因为我们只需要维护一个头指针和尾指针,put()的时候将数据放到头部就行了,链表如果满了的话,我们就将尾指针的pre域删除,再把数据放到头部。
get()函数只在一个链表的数据结构中要想实现O(1)是不太可能的,提到O(1)时间复杂度,我们就不得不想起顺序表与哈希表,所以我们还需要一个哈希表来实现get()这个函数。

代码:

维护一个链表结构:

struct DuListNode
{
	int key, value;
	DuListNode *prev, *next;

public:
	DuListNode():prev(NULL),next(NULL) {}
	DuListNode(int kx,int val):key(kx),value(val),prev(NULL),next(NULL)
	{}
};
class LRUCache 
{
	yhp::hash_map<int,DuListNode*> cache;
	DuListNode *head, *tail;
	int size;
	int capacity;
	DuListNode * Buynode()
	{
		DuListNode * s = (DuListNode*)malloc(sizeof(DuListNode));
		if(NULL == s) exit(1);
		memset(s,0,sizeof(DuListNode));
		return s;
	}
public:

    LRUCache(int ca):size(0),capacity(ca)
	{
		head = Buynode();
		tail = Buynode();
		head->next = tail;
		tail->prev = head;
    }
    ///将s放到链表头部
    void Add_Head(DuListNode *s)
	{
		s->next = head->next;
		s->prev = head;
		head->next = s;
		s->next->prev = s;
	}
	///获取关键码为key的value值
    int get(int key) 
	{
		if(cache.count(key) == 0)
		{
			return -1;
		}
		DuListNode *p = cache[key];
		p->next->prev = p->prev;
		p->prev->next = p->next;

		Add_Head(p);
		return p->value;		
    }
   删除尾部链表
    void Del_tail()
	{
		DuListNode *p = tail->prev; //
		p->next->prev = p->prev;
		p->prev->next = p->next;
		cache.erase(p->key);
		free(p);
		--size;
	}
    ///插入关键码为key,值为val的数据
    void put(int key, int val) 
	{
		if(cache.count(key) == 0)
		{
			if(size >= capacity)
			{
				Del_tail();
			}
			DuListNode * s = Buynode();
			s->key = key;
			s->value = val;
			Add_Head(s);
			cache.insert_noresize(yhp::hash_map<int,DuListNode*>::value_type(key,s));
			++size;
		}
		else如果链表中有关键码为key的数据则更新其值为val
		{
			DuListNode *p = cache[key];
			p->next->prev = p->prev;
			p->prev->next = p->next;
			p->value = val;
			Add_Head(p);
		}
    }
};
int main()
{
	LRUCache Lru(4);
	Lru.put(1,1);
	Lru.put(2,2);
	Lru.put(3,3);
	Lru.put(4,4);

	int x = Lru.get(2);
	Lru.put(1,10);
	Lru.put(3,30);
	x = Lru.get(4);
	Lru.put(5,5);
	Lru.put(6,6);

	x = Lru.get(5);
	

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值