2021-6-24 LRU缓存设计

原题传送门

缺页中断:

在请求分页系统中,可以通过查询页表中的状态位来确定需要访问的页面在不在内存中,如果不在内存中,会产生一次缺页中断,操作系统会根据页表中的外存地址在外存中找到所缺页,将其调入内存中。

中断的具体步奏:(缺页中断本来也属于一种中断)

  1. 保护CPU现场
  2. 分析中断原因
  3. 转入缺页中断程序进行处理
  4. 恢复CPU现场,继续执行

缺页中断与其他中断的区别:

  1. 在指令执行期间产生和处理缺页中断信号
  2. 一条指令在执行期间,可能产生多次缺页中断
  3. 缺页中断返回时,执行产生中断的那一条指令,而一般的中断返回时,执行下一条指令

缺页置换算法

OPT最佳置换算法

思想:置换那些不再被访问,或者最长时间不被访问的页面
缺陷:这种算法无法实现,是否被访问很难被预测
通常OPT思想被用来判断其他算法优劣的一个标准。

FIFO先进先出算法

思想:置换那些被最早加载进内存的页面,数据结构和队列类似
缺陷:容易置换出那些经常使用的页面 目前这种算法不被经常使用

LRU最近最久未使用置换算法(需要会实现)

思想:置换最近一段时间以来最长时间未访问过的页面
缺陷:系统要时时刻刻对各页的访问历史情况加以记录和更新,开销太大,因此LRU算法必须要有硬件的支持
实现:哈希表+双向队列

typedef struct tagListNode{
    int key,val;
    tagListNode* pre;
    tagListNode* next;
    tagListNode():key(0),val(0),pre(nullptr),next(nullptr){}
    tagListNode(int _key,int _val):key(_key),val(_val),next(nullptr),pre(nullptr){}
}Node;    //别名对应typedef
class LRUCache{
private:
    int size;
    int capacity;
    unordered_map<int,Node*> cache;
    Node* head;
    Node* tail;
public:
     LRUCache(int capacity):capacity(capacity),size(0) {
        //初始化过程,除了哈希map不需要处理,其他的都需要处理
        head=new Node();
        tail=new Node();
        head->next=tail;
        tail->pre=head;
    }

    int get(int key) {
        if(!cache.count(key)){
            return -1;
        }
        Node *node=cache[key];
        moveToHead(node);
        return node->val;
    }

    void put(int key, int value) {
        if(!cache.count(key)){
            Node * node=new Node(key,value);
            cache[key]=node;
            addToHead(node);
            ++size;
            if(size>capacity){
                //如果超出尺寸,需要做的 1. 从哈希表中删除对应的key  2. remove掉tail的一个节点 3. size-1
                Node* removenode=removeTail();
                cache.erase(removenode->key);
                delete removenode;
                --size;
            }
        }
       else{
            Node * node=cache[key];
            //如果要查找的key,之前就存在,则把要查找的key移动到双向链表的前面即可,更新节点值
            node->val=value;
            moveToHead(node);
        }
    }

    /*
    * 这里就是把我们需要处理的节点,插在head和head->next之间
    * 注意是双向链表,处理四次指向
    */
    void addToHead(Node* node) {
        node->pre = head;
        node->next = head->next;
        head->next->pre = node;
        head->next = node;
    }

    void removeNode(Node* node) {
        node->pre->next = node->next;
        node->next->pre = node->pre;
    }
    //删除节点,容易理解
    void moveToHead(Node* node) {
        removeNode(node);
        addToHead(node);
    }

    //作用,是删除掉tail前面的节点,超出容量了,自动删除
    Node* removeTail() {
        Node* node = tail->pre;
        removeNode(node);
        return node;
    }
};
posted @ 2021-06-24 16:45  shenlei_blog  阅读(94)  评论(0)    收藏  举报