计算机结构(Computer Architecture)chapter three ----------- 虚拟内存(Virtual Memory)页替换算法(Java实现OPT/ESC/FIFO)

版权声明:此为个人学习与研究成果,若需转载请提前告知。 https://blog.csdn.net/weixin_35811044/article/details/83929189

当某个进程将内存中分配的空间全部使用了,这时需要使用一个新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

猜你喜欢

转载自blog.csdn.net/weixin_35811044/article/details/83929189
今日推荐