操作系统之LRU算法 C语言链表实现

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

为什么要使用链表实现呢,因为这个页面不会很多,内存和资源开销都小

在计算机中,开销往往是需要考虑的,我们不希望占用过多的系统资源,动态路由小型网络使用RIP(Bellman-Ford Routing Algorithm),大型网络设备用的就是OSPF(dijkstra),当然也有很多方面的考虑,比如RIP配置和管理更简单,RIP为了避免出现网络延迟太高,也将路由器最大的允许跳数设为15

我们存储的时候就按照时间吧,末尾为刚刚使用的,淘汰前面的

然后我们来考虑下这个算法,保证我们不使用无关变量。这个cache是空的

进行一次请求需要查看我当前的cache里是否存在这个数据

1存在

  存在就比较简单了,直接取出数据,页面数据不变,并把这个结点放在最后

2不存在

  2.1cache满

  把最靠前的页面用读取数据的数据覆盖,然后把它放到最后的cache

  2.2cache不满

  直接去读取数据,然后把他放在最后的页面

我需要维护的是一个编号(或者说地址)还有后结点,然后查询肯定是O(1)的,这是内部完成的,不需要我考虑(直接得到地址去取数据)

缺页中断都对应了一个硬件操作,就是去取这个数据

链表代码实现

#include <stdio.h>
#include <stdlib.h>
struct node
{
    int id;
    struct node *next;
} * head, *tail, *p;
void PushBack()
{
    /* 
    pre没有意义,仅需要多保留一个尾结点
    p->pre = tail; //使pre指向前一个节点,循环可得到反向链表
    */
    p->next = NULL;
    tail->next = p;
    tail = p;
}
void fun(int n,int *sum,int *page_faults_number)
{
    struct node *q;
    q = head;
    while (q->next != NULL)
    {
        if (q->next->id == p->id)//不缺页
        {
            PushBack();
            p = q->next;
            q->next = p->next;
            free(p);
            return; //执行完全部操作停掉
        }
        q = q->next;
    }
    printf("发生缺页中断 %d\n",p->id);
    *page_faults_number +=1;
    PushBack();
    if (*sum < n) //cache未满,放至后面
    {
        *sum += 1; //并对cache+1
        return;
    }
    //cache满,释放下最后一个节点
    p = head->next;
    head->next = p->next;
    free(p);
}
int main()
{
    int sum, n, i, page_faults_number;
    sum = 0;                        //初始cache内没有数据
    page_faults_number = 0;         //缺页次数清空
    scanf("%d", &n); //读入页数
    head = (struct node *)malloc(sizeof(struct node));
    head->next = NULL;
    tail = head;
    while (1)
    {
        p = (struct node *)malloc(sizeof(struct node));
        scanf("%d", &p->id);
        if (p->id < 0)
        {
            break;
        }
        else
        {
            fun(n,&sum,&page_faults_number);
        }
    }
    printf("发生缺页中断%d次\n",page_faults_number);
    return 0;
}

事后来看,我说pre没有意义是不对的,因为实际上并不是乱序的,往往我们先访问的到的会被继续访问,并不是一个完全的均摊复杂度。

所以应该记录pre进行倒序,有兴趣的可以实现一下,不过我还是觉得c++好写,但是内部肯定是更厉害的。

内部实现是双向链表,因为可以自我删除,虽然浪费了空间,但是节约了时间

c++实现就用list搞一下啊,把最近访问的放到最前面

#include <iostream>
#include <list>
void fun(std::list<int> &L, int x, int n, int &sum, int &page_faults_number)
{
    for (std::list<int>::iterator it = L.begin(); it != L.end(); it++)
    {
        if (*it == x)
        {
            L.push_front(x);
            L.erase(it);
            return;
        }
    }
    std::cout << "发生缺页中断 " << x << std::endl;
    page_faults_number += 1;
    L.push_front(x);
    if (sum < n) //cache未满
    {
        sum += 1; //并对cache+1
        return;
    }
    L.pop_back();
}
int main()
{
    std::list<int> L;
    int sum, n, i, x, page_faults_number;
    sum = 0; //初始cache内没有数据
    page_faults_number = 0;
    std::cin >> n; //读入页数
    while (true)
    {
        scanf("%d", &x);
        if (x < 0)
        {
            break;
        }
        else
        {
            fun(L, x, n, sum, page_faults_number);
        }
    }
    std::cout << "发生缺页中断 " << page_faults_number << "" << std::endl;
    return 0;
}
C++ list 因为内部就是双向链表

再加上map效率就更高了

#include <iostream>
#include <list>
#include <map>
void fun(std::list<int> &L,std::map<int, std::list<int>::iterator> &M, int x, int n, int &sum, int &page_faults_number)
{
    if (M.count(x))
    {
        L.erase(M[x]);
        L.push_front(x);
        M[x] = L.begin();
        return;
    }
    std::cout << "发生缺页中断 " << x << std::endl;
    page_faults_number += 1;
    L.push_front(x);
    M[x] = L.begin();
    // std::cout << "发生缺页中断 " << x << std::endl;
    if (sum < n) //cache未满,放至后面
    {
        sum += 1; //并对cache+1
        return;
    }
    //把最后一个删除
    M.erase(*L.rbegin());
    L.pop_back();
}
int main()
{
    std::list<int> L;
    std::map<int, std::list<int>::iterator> M;
    int sum, n, i, x, page_faults_number;
    sum = 0; //初始cache内没有数据
    page_faults_number = 0;
    std::cin >> n; //读入页数
    while (true)
    {
        scanf("%d", &x);
        if (x < 0)
        {
            break;
        }
        else
        {
            fun(L,M, x, n, sum, page_faults_number);
        }
    }
    std::cout << "发生缺页中断 " << page_faults_number << "" << std::endl;
    return 0;
}
public class LRUCache{
    private int limit;
    private HashMap<String,Node> hashMap;
    private Node head;
    private Node end;
 
    public LRUCache(int limit)
    {
        this.limit = limit;
        hashMap = new HashMap<String,Node>();
    }
 
    public String get(String key){
        Node node = hashMap.get(key);
        if(node ==null)
            return null;
        refreshNode(node);
        return node.value;
    }
 
    public void put(String key,String value){
        Node node = hashMap.get(key);
        if(node == null){
            if(hashMap.size()>=limit)
            {
                String oldKey = removeNode(head);
                hashMap.remove(oldKey);
            }
            node = new Node(key,value);
            addNode(node);
            hashMap.put(key,node)
        }else{
            node.value = value;
            refreshNode(node);
        }
    }
 
    public void remove(String key){
        Node node = hashMap.get(key);
        removeNode(node);
        hashMap.remove(key);
    }
 
 
    private void refreshNode(Node node)
    {
        if(node == end)
            return;
        removeNode(node);
        addNode(node);
    }
 
 
    public String removeNode(Node node){
        if(node == end)
            end = end.pre;
        else if(node ==head)
            head = head.next;
        else
            {
                node.pre.next = node.next;
                node.next.pre = node.pre;
            }
        return node.key;
    }
     
     public void addNode(Node node)
     {
        if(end!=null)
        {
            end.next = node;
            node.pre = end;
            node.next = null;
        }
        end = node;
        if(head == null)
            head = node;
     }
 
}
Java实现(高并发线程安全使用ConcurrentHashMap

 

posted @ 2019-07-22 10:39  暴力都不会的蒟蒻  阅读(2399)  评论(0编辑  收藏  举报