当某个进程将内存中分配的空间全部使用了,这时需要使用一个新page的内容,这page不在内存中,因此就会发生page fault。此时就要在内存空间中挑选一个牺牲的page,将它换回Disk,把需要的新page引入内存中。如何挑选一个最适合的牺牲page(比如将来进程不再使用或最少或最久才会使用的page)这就是页面替换算法要做的事情。
在介绍页替换算法前,我们先来看一个公式:
- Effective access time:EAT = (1-p) * memory access time + p * page fault time ; (p 为 page fault rate)
由上面的公式我们可以看出,一个系统的执行效率跟发生 page fault 的次数与概率息息相关,因此就需要一个好的页面替换算法来提升系统存取数据的效率。下面我们就来聊一聊在Virtual Memory中存在的几种页面替换算法。
页面替换算法:
- FIFO algorithm:
FIFO的本质其实就是一个队列,秉承着先进先出的原则。如图:
第4次引用的 page 为 2 ,内存中只有 7/0/1,发生 page fault 则将最早进入内存的 7 替换。第5次 page 为0 ,在内存中命中,不发生page fault,第6次 page 为 3,将内存最早的0 替换...。以此类推。
Page Fault 次数:15;
优点:实现简单。
缺点:效能不一定很高,而且会发生Belady‘s anomaly,即内存越大分越多的page frame反而会导致page fault rate的上升。
- Optimal algorithm:
OPT是最佳的页面替换算法,没有之一。
原理:预测未来,在未来内存中哪个page是最久才会被再次使用,就先将此 page 替换。如图:
第4次引用的 page 为 2 ,发生page fault,OS开始预测未来,同样page为7的是最久后才会被再次使用,将它替换。第6次 page 为 3,未来内存中page 为 1的是最久以后才会再次被使用,将它替换...。以此类推。
Page Fault 次数:9;
优点:最佳即page fault rate最低。而且不会发生 Belady's anomaly。
缺点:无法实现,因为人类无法预测未来。但仍然可以用此作为标杆来检测其他算法的效能。
- LRU algorithm:(Least recently used)
最接近OPT的算法。
原理:最近内存中最久没被使用或最少被使用的page,作为被替换者。其实就是假定认为一个page你最近一段时间没用它,那么将来一段时间也很可能不会用它。
Page Fault 次数:12;
主要实现方式有两种:
- Counter Implement
原理:为page table 中的每一个entry都添加一个“时间域” , 然后CPU添加一个时钟或者计数器。一个page每次被引用,时钟或计数器的值就会被copy到此page所在entry的“时间域”内,发生page fault时就将时间最小或者计数值最小的被替换。
- Stack Implement
采用栈的方式,栈的实现可以用指针链表。
原理:每当引用一个page,该page就从栈中删除,然后重新放到栈顶,这样栈底部的 page 就是最久未使用的,将作为替换页。
原栈长这样 ==> 当读下一个为 7 的page,栈变成==>
优点:效能好,同样不会发生Belady's anomaly。
缺点:实现的成本太高,而且不太实际,需要随时更新、记录时钟或者栈。
- ESC algorithm:(Enhance second-change)
近似LRU的算法。
ESC算法采用了,reference bit 和 modify bit,用来辅助选择替换的页面。
Bits (ref , mod) | 含义 | 优先级 |
---|---|---|
(0,0) | 近期既未被引用,也未被修改 | 最佳替换页 |
(0,1) | 被修改过,但近期未被引用 | 需要写入Disk在被替换前 |
(1,0) | 近期被引用过,但未被修改 | 可能再次被使用,给第二次机会 |
(1,1) | 近期即被修改,也被引用 | 最糟替换页 |
原理:OS第一次会搜索内存中为(0,0)的page,若没有,则会重新再搜(0,1) 的page,此时若遇到 ref 位为 1 的page 会将其 ref 位 清零等于给了第二次机会,若搜完连为(0,1)的page也没有。就重新执行一次上面的搜索动作,先(0,0)后(0,1)。因为上一次在搜(0,1)时已经对 ref 位清零过,所以这次一定能够找到(0,0)或(0,1)的page。
- ARB algorithm:(Additional-reference-bits)
ARB算法,在内存中为page table中的每一个 page 额外保存一个 8 bits 移位寄存器,在规定间隔时间内(100ms)产生一个中断,将控制权交给OS。然后OS会将此时刻每个page的 reference bit 值记录到移位寄存器中的最高位,而移位寄存器中的其它位右移动1位。因此这8bits的移位寄存器记录了某page 8个周期的使用情况,根据 8 bits 的值,最小的将作为替换页。移位寄存器,不一定是8bits,如是1bit 就等于是单纯使用reference bit,也就是SC algorithm。
如下图:11000100值是比01110111大,所以01110111将作为替换页。
其实还有SC,MFU,LFU等算法,不一一介绍了。
FIFO/OPT/ESC Java代码模拟实现:
import java.util.ArrayList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
public class Algorithm {
// frame capacity
private int CAPACITY;
ArrayList<Integer> optIndex;
int FifoPageFault;
int OptPageFault;
int EscPageFault;
int EscWriteDisk;
Queue<Integer> fifoFrame;
ArrayList<Integer> optFrame;
ArrayList<int[]> escFrame;
public Algorithm(int capacity) {
super();
this.CAPACITY = capacity;
}
/*
* ESC算法
*/
public void ESC(ArrayList<Integer> arr) {
escFrame = new ArrayList<int[]>();
for (int i = 0; i < arr.size(); i++) {
if (escFrame.size() < CAPACITY) {
boolean indexHit = false;
for (int j = 0; j < escFrame.size(); j++) {
int[] eFrame = escFrame.get(j);
if (eFrame != null && eFrame[2] == arr.get(i)) {
indexHit = true;
break;
}
}
if (!indexHit) {
int[] eFrame = createEachEscFrame();
eFrame[2] = arr.get(i);
escFrame.add(eFrame);
EscPageFault++;
}
} else {
boolean indexHit = false;
for (int j = 0; j < escFrame.size(); j++) {
int[] eFrame = escFrame.get(j);
if (eFrame[2] == arr.get(i)) {
// 再次被引用,Hit后内存中此frame reference bit set 1
eFrame[0] = 1;
indexHit = true;
break;
}
}
if (!indexHit) {
EscPageFault++;
boolean indexRep = false;
for (int j = 0; j < escFrame.size(); j++) {
int[] check00 = escFrame.get(j);
if (check00[0] == 0 && check00[1] == 0) {
int[] eFrame = createEachEscFrame();
eFrame[2] = arr.get(i);
escFrame.set(j, eFrame);
indexRep = true;
break;
}
}
if (!indexRep) {
for (int j = 0; j < escFrame.size(); j++) {
int[] check01 = escFrame.get(j);
if (check01[0] == 0 && check01[1] == 1) {
int[] eFrame = createEachEscFrame();
eFrame[2] = arr.get(i);
escFrame.set(j, eFrame);
EscWriteDisk++;
indexRep = true;
break;
} else {
check01[0] = 0;
}
}
}
if (!indexRep) {
for (int j = 0; j < escFrame.size(); j++) {
int[] checkAll = escFrame.get(j);
if (checkAll[0] == 0 && checkAll[1] == 0) {
int[] eFrame = createEachEscFrame();
eFrame[2] = arr.get(i);
escFrame.set(j, eFrame);
break;
} else if (checkAll[0] == 0 && checkAll[1] == 1) {
int[] eFrame = createEachEscFrame();
eFrame[2] = arr.get(i);
escFrame.set(j, eFrame);
EscWriteDisk++;
break;
}
}
}
}
}
}
}
//创建page,模拟每一个frame中page的引用與修改
public int[] createEachEscFrame() {
int[] eFrame = new int[3];
// page一旦被使用reference就置1, 随机获得0或1,赋值给 dirty bit
int referenceBit = 1;
int dirtyBit = (int) (Math.random() * 2);
eFrame[0] = referenceBit;
eFrame[1] = dirtyBit;
return eFrame;
}
/*
* FIFO算法
*/
public void FIFO(ArrayList<Integer> arr) {
fifoFrame = new ArrayBlockingQueue<Integer>(CAPACITY);
for (int i = 0; i < arr.size(); i++) {
if (fifoFrame.size() < CAPACITY) {
if (fifoFrame.contains(arr.get(i))) {
continue;
} else {
fifoFrame.add(arr.get(i));
FifoPageFault++;
}
} else {
if (fifoFrame.contains(arr.get(i))) {
continue;
} else {
fifoFrame.poll();
fifoFrame.add(arr.get(i));
FifoPageFault++;
}
}
}
}
/*
* OPT算法
*/
public void OPT(ArrayList<Integer> arr) {
optFrame = new ArrayList<Integer>();
for (int i = 0; i < arr.size(); i++) {
if (optFrame.size() < CAPACITY) {// frame未满
if (optFrame.contains(arr.get(i))) {
continue;
} else {// 页面不在内存,发生page fault
optFrame.add(arr.get(i));
OptPageFault++;
}
} else {// frame已满
if (optFrame.contains(arr.get(i))) {
continue;
} else {// 页面不在内存,发生page fault
OptPageFault++;
optIndex = new ArrayList<Integer>();
for (int j = i; j < arr.size(); j++) {
if (optFrame.contains(arr.get(j)) && !optIndex.contains(optFrame.indexOf(arr.get(j)))) {
optIndex.add(optFrame.indexOf(arr.get(j)));
if (optIndex.size() == CAPACITY) {
optFrame.set(optIndex.get(CAPACITY - 1), arr.get(i));
break;
}
}
}
if (optIndex.size() < CAPACITY) {
int[] useless = new int[CAPACITY - optIndex.size()];
int a = 0;
for (int z = 0; z < CAPACITY; z++) {
if (!optIndex.contains(z)) {
useless[a++] = z;
}
}
Random rnd = new Random();
int replace = rnd.nextInt(useless.length);
optFrame.set(useless[replace], arr.get(i));
}
}
}
}
}
仅为个人理解,如有不足,请指教。 https://blog.csdn.net/weixin_35811044