内存置换策略
1.最近最少使用(LRU)算法
-
概念:LRU策略置换内存中最长时间未被引用的页。根据局部性原理,这也是最近最不可能访问到的页。
-
实现方法:一种实现方法是给每页添加一个最后一次访问的时间戳,在每次访问内存时更新这个时间戳。
另一种实现方法是创建一个关于访问页的栈,当有新页进入时,将之前的页按顺序压向栈底,内存不足时将栈底的页面移除,然后将新页放置在栈顶位置。本次采用第一种方法:添加时间戳。
-
基于C语言数组使用LRU算法代码参考:
#include <stdio.h> #define N 3 //内存容量 struct lru{ int value; int t; } page[N]; void print(){ int i; printf("页面存储状态:\n"); for (i=0; i<N; i++){ printf("value=%d time=%d\n",page[i].value,page[i].t); } } int lru(int x){ int i,j,k; k=0; for (i=0; i<N; i++){ if (x==page[i].value){ printf("找到请求页面\n"); page[i].t = 0; for (j=0; j<N; j++){ if (j != i) { page[j].t += 1; } } return 1; } } for (i=0; i<N; i++){ if (page[i].value == 0){ j = i; break; } else if (k < page[i].t){ k = page[i].t; j = i; } } printf("淘汰页面:%d\n",page[j].value); page[j].value = x; page[j].t = 0; for (i=0; i<N; i++){ if (i != j) { page[i].t += 1; } } return 0; } int main(void){ int x; print(); while (1){ printf("输入请求页:"); scanf("%d",&x); lru(x); print(); } return 0; }
-
运行结果:
-
分析:在实际使用中,如果数据量较大的话最好选择采用双链表,链表的置换速度会比较快。Java的LinkedHashMap可以很好实现哈希链表,以后有空再来补充~~
2.FIFO(先进先出)算法
-
概念:FIFO策略把分配给进程的页框视为一个循环缓冲区,并按循环方式移动页,通俗点说就是将驻留在内存中时间最长的页面置换出去
-
实现方法:记录每个页面载入内存的顺序,若未在内存中找到所请求的页面,则将新页放置在页面载入顺序最大的位置,并将该位置的顺序记为0。每次进行请求页面操作后,将所存页面的载入顺序全部加1.
-
基于C语言数组实现FIFO算法代码参考:
#include <stdio.h> #define N 3 struct fifo{ int value; int t; //记录页面载入时间顺序 } page[N]; void print(){ int i; printf("页面存储状态:\n"); for (i=0; i<N; i++){ printf("%2d",page[i].value); } printf("\n"); } void fifo(int x){ int i,j; for (i=0; i<N; i++){ if (x==page[i].value){ printf("找到请求页面\n"); break; } if (page[i].value == 0){ j = i; break; } else if (page[i].t > page[j].t){ j = i; } } printf("淘汰页面:%d\n",page[j].value); page[j].value = x; page[j].t = 0; for(i=1; i<N; i++){ page[j].t++; if (j==2){ j = 0; }else j++; } } int main(void){ int x; print(); while (1){ printf("输入请求页:"); scanf("%d",&x); fifo(x); print(); } return 0; }
-
运行结果:
-
分析:
FIFO策略与LRU策略相比,FIFO实现简单,但性能相对较差。例如上面两个算法的输入是相同的,但FIFO多了一次缺页中断,尤其是出现一部分程序或数据在整个程序的生命周期中使用评率较高的情况,若使用FIFO算法,则这些页会被反复地换入和换出。
有没有方法能以较小的开销来获得近似于LRU的性能呢?有,这类方法几乎都是时钟策略(Clock)的各种变体。
3.Clock(时钟)算法
-
概念与实现方法:Clock策略给每一个页框附加一个使用位和一个指针,每次内存装入一个页面时,将该页框的使用位置为1。当输入请求页时,先循环整个内存空间判断是否有对应的值,若有,将该页框的使用位数值进行反转(即1转0,0转1),如果指针刚好指向该位置则将指针移动到下一位置;若内存中没有该请求页,则从指针位置开始判断该页框的使用位是否为0,若为0则将该页置换,若不为0,使用位数值反转,指针移向下一页框。
这里转载一篇用图来解释的博文,能帮助理解,针不戳:时钟(CLOCK)置换算法流程
-
基于C语言数组实现Clock算法代码参考:
#include <stdio.h> #define N 3 struct CLOCK{ int value; bool t; //使用位,值为0或1 }page[N]; int clock(int x,int p){ int i; for (i=0; i<N; i++){ if (page[i].value == x){ printf("找到页面%d\n",page[i].value); page[i].t = not page[i].t; if (page[p].value == page[i].value){ if (p<2){ p++; }else p=0; } return p; } } while(1){ if (page[p].value !=x && page[p].t == 0){ printf("置换页面%d\n",page[p].value); page[p].value = x; page[p].t = not page[p].t; if (p<2){ p++; }else p=0; return p; }else { page[p].t = not page[p].t; if (p<2){ p++; }else p=0; } } } void print(int p){ int i; printf("页面存储状态:\n"); for (i=0; i<N; i++){ printf("%2d",page[i].value); } printf(" 指针指向第%d页\n",p+1); printf("\n"); } int main(void){ int x,p; p = 0; print(p); while(1){ printf("请输入请求页面:"); scanf("%d",&x); p = clock(x,p); print(p); } return 0; }
-
运行结果:
-
补充:
改进型的CLOCK算法
在之前的CLOCK算法上面除了使用位,再增加一个修改位。现在每一页有两个状态,分别是(使用位,修改位),可分为以下四种情况考虑:
(0,0):最近没有使用也没有修改
(0,1):最近没有使用但修改过,将会被写
(1,0):最近使用过但没有被修改,下一轮将再次被用
(1,1):最近使用过也修改过,下一轮页面置换最后的选择
每次从指针当前位置先寻找内存中是否有(使用位,修改位)为(0,0)的页面,如果没有则找(0,1)的页面;如果指针循环一周后依然未找到,将集合中所有页面的使用位置为0,重复前面的操作,最后一定能找到能够替换的页框。