页面置换FIFO与LRU的实现

最近复习操作系统,想到了两个常用的页面置换算法,但之前一种没实现过,想想应如何实现。

FIFO(先进先出页面置换)

FIFO最易理解,也易于实现,但在应用中,其缺页率比较高。

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1005;
int a[maxn];
int d[maxn];    //散列便于查询
int main()
{
    int m,n,x,c=0;
    printf("输入物理块数和页面数:\n");
    scanf("%d %d",&m,&n);
    for(int i=0;i<m;i++){
        a[i]=1004;
    }
    memset(d,0,sizeof(d));
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        if(d[x]==0){        //如果物理块中没有
            d[a[c%m]]=0;    //循环队列中将尾指针所指的元素删除
            a[c%m]=x;       //插入到尾指针
            d[x]=1;
            c++;            //尾指针进行自增
        }
    }
    printf("缺页数:%d\n",c);
    return 0;
}

实测效果:

LRU(最近最久未使用算法)

顾名思义,该算法每次将内存中最久未使用过的内存页面换到存中,通常情况下,缺页率比FIFO低,所以更优。但这里仅提高了单链表的实现方法,由于每次查询页面是否存在内存时,都需要进行遍历,时间复杂度为O(n),效率比较低。如何使用哈希链表实现时间复杂度为O(1),需要再去学习一下(本人太菜,暂时无能为力)。

/*单链表实现LRU*/
#include <bits/stdc++.h>

using namespace std;
typedef int ElemType;                       //链表的数据类型
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;
int CreateList(LinkList &L,int n){          //&L为引用变量
    L = (LinkList)malloc(sizeof(LNode));    //分配空间
    L->data = n;
    L->next = NULL;                         //头指针为空
    for(int i=n;i>0;--i){
        LinkList p;                         //LinkList p 相当于 LNode *p
        p = (LinkList)malloc(sizeof(LNode));
        p->data = -1;                       //初始化时将链表值置为-1;
        p->next = L->next;                  //后插法
        L->next = p;
    }
    return 0;
}

int ListInsert(LinkList &L,int i,ElemType e){   //i为插入的位置
    LinkList p = L;
    LinkList s;
    int j = 0;
    while(p&&j<i-1){
        p = p->next;
        ++j;
    }
    if(!p || j>i-1){
        return 0;
    }
    s = (LinkList)malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    return 1;
}

int ListDelet(LinkList &L,int i){   //删除第i位置上的元素
    LinkList p = L;int j = 0;
    while(p->next && j<i-1) {
        p = p->next;++j;
    }
    if(!(p->next) || j>i-1)return 0;
    LinkList q = L;
    q = p->next;
    p->next = q->next;
    free(q);
    return 1;
}

int List_search(LinkList L,ElemType e){
    LinkList p;
    p = L->next;
    int j=1;
    while(p != NULL){
        if(p->data == e){
            return j;       //有该元素
            break;
        }
        else{
            p = p->next;
            j++;
        }
    }
    return 0;               //没有该元素
}
int main(){
    int m,n,x;
    int c = 0;
    LinkList L;
    printf("输入物理块数和页面数:\n");
    scanf("%d %d",&m,&n);   //m为内存空间,n为元素的个数
    CreateList(L,m);
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        int p = List_search(L,x);
        if(p>0){                //找到该点要更新
            ListDelet(L,p);
            ListInsert(L,1,x);
        }
        else{                   //没找到该点要将该点插入头节点,然后删除尾节点
            ListDelet(L,m);
            ListInsert(L,1,x);
            c++;
        }
    }
    printf("缺页数:%d\n",c);
    return 0;
}

实现效果(通常情况下缺页率比FIFO更低): 

要求对容器的访问、加入新元素、修改元素的时间复杂度都为O(1)。需要使用哈希配合双链表实现。假设缓存块为页面,哈希表存放关键字key对应的页面节点。使用head指针指向链表开头、tail指针指向结尾,双链表可以让链表刷新时找到前后页面。当访问或修改某个关键字对应页面时,将该页面放置到链表末尾,然后更新tail指针。当加入新关键字时,若当前链表长度达到容量上限,移除head指针指向元素,然后更新head指针即可。所以无论访问、修改或添加元素,时间复杂的都为O(1)。

     在实现上,可以在链表的头尾分别加上 虚拟的头尾指针,避免移除head节点或添加tail节点时需要单独考虑。空间复杂度方面,双向链表最大节点量为capacity + 2,哈希表最大节点量为capacity,所以空间复杂度为O(capacity)。

// 内存节点
public class cacheNode
{
    public var index:Int
    public var value:Int
    public var next:cacheNode?
    public var pre:cacheNode?
    
    init(_ id_:Int, _ value_: Int)
    {
        index = id_
        value = value_
        next = nil
        pre = nil
    }
}
// LRU算法
class LRUCache {
    private var head:cacheNode?
    private var map:Dictionary<Int, cacheNode>
    private var capacity_:Int
    private var count:Int
    private var tail:cacheNode?
    
    init(_ capacity: Int) {
        capacity_ = capacity
        head = cacheNode(-1, -1)
        head?.next =  cacheNode(-2, -1)
        tail = head?.next
        tail?.pre = head
        map = Dictionary<Int, cacheNode>()
        count = 0
    }

    private func delete()
    {
        map.removeValue(forKey: head!.next!.index)
        head!.next!.next?.pre = head
        head!.next = head!.next!.next
        count -= 1
    }
    
    private func refresh(_ key:Int)
    {
        let cur = map[key]
        
        if cur?.next === tail
        {
            return
        }
        
        cur?.pre?.next = cur?.next
        cur?.next?.pre = cur?.pre
        cur?.pre = tail?.pre
        tail?.pre?.next = cur
        tail?.pre = cur
        cur?.next = tail
        map[key] = tail?.pre
    }

    func get(_ key: Int) -> Int {
        if map.keys.contains(key) == true
        {
            refresh(key)
        }
        return map[key]?.value ?? -1
    }

    func put(_ key: Int, _ value: Int) {
        if map.keys.contains(key) == true
        {
            map[key]?.value = value
            refresh(key)
        }
        else
        {
            count += 1
            if count > capacity_
            {
                delete()
            }
            
            let tmp:cacheNode? = tail?.pre
            tmp?.next = cacheNode(key, value)
            tmp?.next?.pre = tmp
            tail?.pre = tmp?.next
            tmp?.next?.next = tail
            map[key] = tail?.pre
        }
    }
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值