山东大学Nachos课设实验三:缓存与虚拟内存

实验三:缓存与虚拟内存

一.题目要求:

第一点:题目要求我们实现TLB管理和全局的反向页表的管理和维护。
第二点:题目要求我们实现按需调页的功能。
第三点:题目要求我们实现懒加载机制。

二.问题解决思路

此处我们将按照正常的进程执行流程来展开本实验的思路,涉及之前的实验的细节将跳过,只展开与本实验相关的流程细节,过程中会展示部分代码的细节以方便理解,此外,本实验涉及几个需要另外实现的类,我们会在下一个环节(变量说明)详细介绍他们。
在正式开始之前,我们需要明确一个非常关键的点:整个执行过程中,要确保硬件始终只能看到TLB这一层,即所有的底层操作都只能围绕TLB。另外,我们还要确定TLB、反向页表等的结构,TLB沿用上一个实验中的页表项的结构,他将具有以下几个状态位:valid,used,dirty。反向页表将使用java提供的hashmap来实现
在这里插入图片描述

当发生缺页时,我们将使用二次机会来选择受害者,这意味着我们需要一个关于当前内存的信息的副本(不用担心一致性问题,这个副本所看到的内容是实时存在于内存和反向页表的)
在这里插入图片描述

受害者将存储在以下的结构(交换区)中:
在这里插入图片描述

以上是需要补充的部分结构,现在,让我们正式进入流程:
当一个进程执行前,系统会获取进程的coff文件内容为其构建分段分页信息,与之前不同的是,由于使用了TLB的思想,我们并不会为进程构建一个页表,而是先将各个页先存储到交换区中,并在反向页表中注册该进程的虚拟页信息(暂时不分配物理帧),不过这里为了区分,我们将coff页存储到专门的区域,堆栈页和参数页存储到交换区
存储堆栈页和参数页:
在这里插入图片描述

存储coff页:

在这里插入图片描述
在这里插入图片描述

之后需要调用这些页时,我们将从这些结构里面去获取,这也就实现了懒加载机制。
然后进程正常取指令执行指令,它通过访问TLB来获取该页
在这里插入图片描述

如果TLB未命中,就抛出一个TLBMiss异常,再通过handleTLBFault()去处理异常,首先看反向页表里面该页是否分配了物理帧
在这里插入图片描述

如果分配了物理帧,就随机选择一个TLB作为受害者,将所需的页信息换入(这里同样不必担心一致性问题,TLB、反向页表、内存副本所看到的都是同样的内容,无需在换入换出时去刻意维护)
在这里插入图片描述

如果还没有分配物理帧,就从内存中选择空闲帧,
在这里插入图片描述

选择空闲帧会遇到以下几种情况:
内存还有空闲帧,则正常的分配,如果内存没有空闲帧,则通过二次机会选择一个帧作为受害者
在这里插入图片描述

该受害者将会被移出内存至交换区,这里会在交换区为该页分配一个索引,方便之后我们需要时可以顺利找到他,反向页表里面该页的valid变为false,分配的物理帧变为-1。
然后我们获得了一个可用的物理帧,接下来会遇到以下几种情况:
1.需要调用的是coff页,且第一次加载,则在反向页表里面更新信息,注意第一次加载时我们要获取该coff的只读信息,同时将dirty和used变为false,之后在硬件访问TLB时会对这两个位进行操作。
2.需要调用的是堆栈页或参数页,且第一次加载,则在反向页表里面更新信息,注意第一次加载时我们要将dirty和used变为false,之后在硬件访问TLB时会对这两个位进行操作。
3.需要调用的页已经加载过,则该页在交换区里面,我们通过交换区获得该页的索引从而取出该页,分配至内存,在反向页表里面更新信息。
通过以上流程我们便完成了一个简单的按需调页。
值得注意的是,当内存过小时,这种方案会显得吃力,即出现抖动现象(目前的测试是1个页时执行echo程序出现明显的抖动,大于1页顺利执行)
当进程被抢占而需要交出cpu时,他要将现场保留,之后换回自己时,再恢复现场
保存现场:
在这里插入图片描述

恢复现场:
在这里插入图片描述

当进程执行完正常结束时,需要释放资源,需要释放的包括:反向页表里面关于该进程的信息,交换区里面关于该进程的信息

在这里插入图片描述
在这里插入图片描述

至此,我们已经顺利的在实验要求下完成了一个进程的执行

三.变量说明:

CoffSectionAddress类:存储了关于coff段的信息,包括偏移,段号等信息,用于懒加载的信息获取
InvertedPageTable类:反向页表的相关操作,包括进程信息的注册,为进程虚拟页与物理帧建立一个链接,为操作系统提供服务。详细的包括插入一个进程的页信息、删除进程的页信息、设置页表项信息、换出时解除该页的物理帧绑定,换入时恢复该页的物理帧绑定、二次机会获取受害页
SecondChanceReplacement类:二次机会的算法提供者,InvertedPageTable类通过该类通过的服务选择下一个受害页
SwapperController类:交换区信息,为每个进程提供了堆栈页和参数页的懒加载的暂存场所,为按需调页提供了换出的暂存场所,会为进程的每个虚拟页维护一个索引信息,需要时将该页存到该索引对应的区域,通过该索引可以获取之前存储的页内容
TranslationEntryWithPid类:内存的一个副本,存储了每个物理帧被哪个进程的哪个页占用
VirtualPageFlag类:为进程与虚拟页提供了一个绑定,因为反向页表中是以进程加虚拟页号来搜索物理帧,可以通过该类来提供索引信息
VMKernel类:继承了UserKernel
VMProcess类:继承了UserProcess,在其基础上进行升级,取消了进程页表的设置,用TLB取缔,可以处理TLBMiss异常,确保完成按需调页,在初始化时采用懒加载模式,优化执行效率

四.源代码:

CoffSectionAddress类代码:
public class CoffSectionAddress {
    private int sectionNumber;
    private int pageOffset;

    public int getSectionNumber() {
        return sectionNumber;
    }

    public CoffSectionAddress(int sectionNumber, int pageOffset) {
        this.sectionNumber = sectionNumber;
        this.pageOffset = pageOffset;
    }

    public void setSectionNumber(int sectionNumber) {
        this.sectionNumber = sectionNumber;
    }

    public int getPageOffset() {
        return pageOffset;
    }

    public void setPageOffset(int pageOffset) {
        this.pageOffset = pageOffset;
    }

}
InvertedPageTable类代码:
//全局的反向页表  保存在真正内存位置的页的虚拟地址以及拥有该页的进程的信息
public class InvertedPageTable {

    //全局的反向页表
    private static HashMap<VirtualPageFlag, TranslationEntry> GlobalInvertedPageTable = new HashMap<VirtualPageFlag, TranslationEntry>();
    

    //全局的一个 页表  存放所有的物理页(对应 进程) 对应进程的物理页 的一个副本
    public static TranslationEntryWithPid[] PhysicalPageCopy = new TranslationEntryWithPid[Machine.processor().getNumPhysPages()];

    //二次机会
    private static SecondChanceReplacement secondChanceReplacement = new SecondChanceReplacement();  

    private InvertedPageTable() {

    }


    //向反向页表中插入一页
    public static boolean insertEntry(int pid, TranslationEntry entry) {
        VirtualPageFlag virtualPageFlag = new VirtualPageFlag(pid, entry.vpn);
        if (GlobalInvertedPageTable.containsKey(virtualPageFlag)) {
            return false;
        }
        GlobalInvertedPageTable.put(virtualPageFlag, entry);
        //如果则虚拟页在物理页中,而tlb可以直接由处理器使用。
        if (entry.valid) {
            PhysicalPageCopy[entry.ppn] = new TranslationEntryWithPid(GlobalInvertedPageTable.get(virtualPageFlag), pid);
            PhysicalPageCopy[entry.ppn].getTranslationEntry().valid = true;
        }
        return true;
    }

    //将某进程的某虚拟页 从反向页表中删除
    public static TranslationEntry deleteEntry(int pid, int vpn) {
        VirtualPageFlag virtualPageFlag = new VirtualPageFlag(pid, vpn);
        TranslationEntry entry = GlobalInvertedPageTable.get(virtualPageFlag);
        if (entry == null) {
            return null;
        }
        //如果则虚拟页在内存中,则将其清空
        if (entry.valid) {
            PhysicalPageCopy[entry.ppn] = null;
        }
        return entry;
    }

    //设置某进程的某一个TranslationEntry
    public static void setEntry(int pid, TranslationEntry newEntry) {
        VirtualPageFlag virtualPageFlag = new VirtualPageFlag(pid, newEntry.vpn);
        //如果此TranslationEntry本身就不在反向页表中
        if (!GlobalInvertedPageTable.containsKey(virtualPageFlag)) {
        	
            return;
        }
        //从反向页表中取出旧TranslationEntry
        TranslationEntry oldEntry = GlobalInvertedPageTable.get(virtualPageFlag);
        if (oldEntry.valid) {
            if (PhysicalPageCopy[oldEntry.ppn] == null) {

                return;
            }
            PhysicalPageCopy[oldEntry.ppn] = null;

        }
        GlobalInvertedPageTable.put(virtualPageFlag, newEntry);
        if (newEntry.valid) {
            if (PhysicalPageCopy[newEntry.ppn] != null) {

                return;
            }
            PhysicalPageCopy[newEntry.ppn] = new TranslationEntryWithPid(GlobalInvertedPageTable.get(virtualPageFlag), pid);
            PhysicalPageCopy[newEntry.ppn].getTranslationEntry().valid = true;
        }
        
    }
    
    //激活
    public static void liveEntry(int pID, TranslationEntry entry) {
        VirtualPageFlag key = new VirtualPageFlag(pID, entry.vpn);
        if (!GlobalInvertedPageTable.containsKey(key)) {
        	
            return;
        }
        TranslationEntry oldEntry = GlobalInvertedPageTable.get(key);
        
        oldEntry.valid = true;
        oldEntry.ppn = entry.ppn;
        PhysicalPageCopy[oldEntry.ppn] = new TranslationEntryWithPid(GlobalInvertedPageTable.get(key), pID);
        PhysicalPageCopy[oldEntry.ppn].getTranslationEntry().valid = true;
    }
    
    //禁用
    public static void invalidEntry(int pID, TranslationEntry entry) {
        VirtualPageFlag key = new VirtualPageFlag(pID, entry.vpn);
        if (!GlobalInvertedPageTable.containsKey(key)) {
        	
            return;
        }
        TranslationEntry oldEntry = GlobalInvertedPageTable.get(key);
        PhysicalPageCopy[oldEntry.ppn] = null;
        oldEntry.valid = false;
        oldEntry.ppn = -1;
        
    }

    public static void updateEntry(int pID, TranslationEntry entry) {
        VirtualPageFlag key = new VirtualPageFlag(pID, entry.vpn);
        if (GlobalInvertedPageTable.containsKey(key)) {
        	
            return;
        }
        TranslationEntry oldEntry = GlobalInvertedPageTable.get(key);
        TranslationEntry newEntry = mix(entry, oldEntry);
        System.out.println(newEntry.valid);
        if (oldEntry.valid) {
            if (PhysicalPageCopy[oldEntry.ppn] == null) {
                return;
            }
            PhysicalPageCopy[oldEntry.ppn] = null;

        }
        GlobalInvertedPageTable.put(key, newEntry);
        if (newEntry.valid) {
            if (PhysicalPageCopy[newEntry.ppn] != null) {
                return;
            }
            PhysicalPageCopy[newEntry.ppn] = new TranslationEntryWithPid(GlobalInvertedPageTable.get(key), pID);

        }
        
    }

    private static TranslationEntry mix(TranslationEntry neww, TranslationEntry old) {
        if (neww.dirty || old.dirty) {
            neww.dirty = true;
        }
        if (neww.used || neww.used) {
            neww.used = true;
        }
        return neww;
    }


    //获取 某进程 某虚拟页下对应的TranslationEntry
    public static TranslationEntry getEntry(int pid, int vpn) {
        VirtualPageFlag virtualPageFlag = new VirtualPageFlag(pid, vpn);
        if (GlobalInvertedPageTable.containsKey(virtualPageFlag)) {
        	return GlobalInvertedPageTable.get(virtualPageFlag);
        }else {
        	return null;
        }
        
    }

    //选取被置换的页
    public static TranslationEntryWithPid getVictimPage() {
        TranslationEntryWithPid entry = null;
        int i = secondChanceReplacement.findSwappedPage();
        entry = PhysicalPageCopy[i];
        PhysicalPageCopy[i] = null;
        
        return entry;
    }
    
    public static void print() {
    	for(int i = 0; i<PhysicalPageCopy.length;i++) {
    		if(PhysicalPageCopy[i]!=null) {
    			VirtualPageFlag virtualPageFlag = new VirtualPageFlag(PhysicalPageCopy[i].getPid(), PhysicalPageCopy[i].getTranslationEntry().vpn);
    			
    			System.out.println("进程"+PhysicalPageCopy[i].getPid()+" 拥有 "+PhysicalPageCopy[i].getTranslationEntry().vpn+" 在 "+PhysicalPageCopy[i].getTranslationEntry().ppn);
    			System.out.println(GlobalInvertedPageTable.get(virtualPageFlag).valid+" "+GlobalInvertedPageTable.get(virtualPageFlag).used+" "+GlobalInvertedPageTable.get(virtualPageFlag).dirty);
    		}
    		
    	}
    }
    
    public static void print(int i) {
    	
    		if(PhysicalPageCopy[i]!=null)
    		System.out.println("进程"+PhysicalPageCopy[i].getPid()+" 拥有 "+PhysicalPageCopy[i].getTranslationEntry().vpn+" 在 "+PhysicalPageCopy[i].getTranslationEntry().ppn);
    		
    	
    }

}
SecondChanceReplacement类代码:
/**
 * 二次机会算法
 */
public class SecondChanceReplacement {


    private int currentPhysicalPage;


    private int replacePhysicalPage;


    public SecondChanceReplacement() {
        currentPhysicalPage = 0;
        replacePhysicalPage = 0;


    }

    public int findSwappedPage() {
        if (!UserKernel.memoryLinkedList.isEmpty()) {
            replacePhysicalPage = UserKernel.memoryLinkedList.removeFirst();
            
            return replacePhysicalPage;
        } else {
        	
        	InvertedPageTable.print();
        	//System.out.println(currentPhysicalPage);
            while (InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getTranslationEntry().used) {
            	System.out.println(UserKernel.currentProcess().sectionlength +"  "+ InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getTranslationEntry().vpn);
            	//if(UserKernel.currentProcess().processID == InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getPid() && UserKernel.currentProcess().sectionlength > InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getTranslationEntry().vpn) {
            		InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getTranslationEntry().used = false;
                    InvertedPageTable.updateEntry( InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getPid(), InvertedPageTable.PhysicalPageCopy[currentPhysicalPage].getTranslationEntry());
                    
            	//}
                	currentPhysicalPage = ++currentPhysicalPage % Machine.processor().getNumPhysPages();
            }

            replacePhysicalPage = currentPhysicalPage;
            
            currentPhysicalPage++;
            currentPhysicalPage %= Machine.processor().getNumPhysPages();
            System.out.println("拿出 "+replacePhysicalPage);
            return replacePhysicalPage;
        }
    }


}
SwapperController类代码:
//将物理页中  不需要的置换到 磁盘上  将需要的 置换到内存中  物理内存一共16页
public class SwapperController {

    private int pageSize;

    //要交换的文件
    private HashMap<Integer, byte[]> swapFile;

    //内存中的文件名
    private String swapFileName;

    private HashMap<VirtualPageFlag, Integer> swapTable;
    
    private HashMap<VirtualPageFlag, Boolean> swapReadonly;

    private HashSet<VirtualPageFlag> unallocated;//未分配列表

    private LinkedList<Integer> availableLocations;//可用位置

    private static SwapperController instance = null;

    private SwapperController(String swapFileName) {
        pageSize = Processor.pageSize;
        this.swapFileName = swapFileName;
        swapTable = new HashMap<VirtualPageFlag, Integer>();
        swapReadonly = new HashMap<VirtualPageFlag, Boolean>();
        unallocated = new HashSet<VirtualPageFlag>();
        availableLocations = new LinkedList<Integer>();
        swapFile = new HashMap<Integer, byte[]>();
        //swapFile = ThreadedKernel.fileSystem.open(swapFileName, true);
        
        if (swapFile == null) {
            System.out.println("无法打开此文件");
        }
        //重制交换空间  将空间置0
        //byte[] zeroBuffer = new byte[Processor.pageSize * Machine.processor().getNumPhysPages()*100];
        //swapFile.write(zeroBuffer, 0, zeroBuffer.length);
    }

    public void deletePosition(int pid, int vpn) {
        VirtualPageFlag key = new VirtualPageFlag(pid, vpn);
        if (!swapTable.containsKey(key)) return;
        int availableLocation = swapTable.remove(key);
        if(swapReadonly.containsKey(key))
        	swapReadonly.remove(key);
        availableLocations.add(availableLocation);
    }

    public static SwapperController getInstance(String swapFileName) {
        if (instance == null) {
            instance = new SwapperController(swapFileName);
        }
        return instance;
    }

    //读取交换文件
    public byte[] readFromSwapFile(int pid, int vpn) {
    	
        int position = findEntry(pid, vpn);
        System.out.println("从读出 "+position);
        if (position == -1) {
        	
            return new byte[pageSize];
        }
        byte[] memcopy = swapFile.get(position).clone();
        
        if(memcopy == null) {
        	return new byte[pageSize];
        }
        
        //byte[] memcopy = new byte[pageSize];
        //int length = swapFile.read(position * pageSize, memcopy, 0, pageSize);
        //if (length == -1) {
        	//System.out.println("wrong");
            //return new byte[pageSize];
        //}
        System.out.println("读出Swap: "+memcopy);
        return memcopy;
    }
    
    public boolean RD(int pid, int vpn) {
    	VirtualPageFlag key = new VirtualPageFlag(pid, vpn);
        return swapReadonly.remove(key);
    }

    //写入交换文件
    public int writeToSwapFile(int pid, int vpn, byte[] page, int offset, boolean readOnly) {
    	
        //先 为进程在swapTable中分配位置  表示已经交换
        int position = allocatePosition(pid, vpn);
        System.out.println("写入到"+position);
        if (position == -1) {
        	
            return -1;
        }
        //写入交换文件
        System.out.println("写入Swap: "+page);
        byte[] copy = page.clone();
        swapFile.put(position, copy);
        VirtualPageFlag key = new VirtualPageFlag(pid, vpn);
        swapReadonly.put(key, readOnly);
        for(int i = 0; i < swapFile.size(); i++) {
        	System.out.println(swapFile.get(i));
        }
        
        //System.out.println(position);
        
        //int i = swapFile.write(position * pageSize, page, offset, pageSize);
        
        //if (i == -1) {
            //System.out.println("写入文件失败");
        //}
        return position;
    }

    public void insertUnallocatedPage(int pid, int vpn) {
        VirtualPageFlag key = new VirtualPageFlag(pid, vpn);
        unallocated.add(key);
        System.out.println("分配"+key.getPid()+"的"+key.getVirtualPageNum());
    }

    //在swapTable中为进程分配位置
    public int allocatePosition(int pid, int vpn) {
        VirtualPageFlag virtualPageFlag = new VirtualPageFlag(pid, vpn);
        if (unallocated.contains(virtualPageFlag)) {
            unallocated.remove(virtualPageFlag);
            if (availableLocations.isEmpty()) {
                availableLocations.add(swapTable.size());
            }
            //分配位置
            int index = availableLocations.removeFirst();
            swapTable.put(virtualPageFlag, index);
            
            
            System.out.println("Swap分配"+pid+"的"+vpn+"到自己的"+index);
            System.out.println(swapTable.size());
            return index;
        } else {
            //位置已经分配 直接返回
            int index = -1;
            index = swapTable.get(virtualPageFlag);
            System.out.println("Swap已经分配"+pid+"的"+vpn+"到自己的"+index);
            if (index == -1) {
                return -1;
            }
            return index;
        }
    }


    //获取进程虚拟页的 位置
    private int findEntry(int pid, int vpn) {
        Integer position = swapTable.get(new VirtualPageFlag(pid, vpn));

        if (position == null)
            return -1;
        else
            return position.intValue();
    }
}
TranslationEntryWithPid类代码:
//pid与 TranslationEntry的对应
public class TranslationEntryWithPid {

    private TranslationEntry translationEntry;
    private int pid;

    public TranslationEntryWithPid(TranslationEntry translationEntry, int pid) {
        this.translationEntry = translationEntry;
        this.pid = pid;
    }

    public TranslationEntry getTranslationEntry() {
        return translationEntry;
    }

    public void setTranslationEntry(TranslationEntry translationEntry) {
        this.translationEntry = translationEntry;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
}
VirtualPageFlag类代码:
//管理 进程号  以及  虚拟页数 (反向页表                                       )
public class VirtualPageFlag {
    private int pid;
    private int VirtualPageNum;

    public VirtualPageFlag(int pid, int virtualPageNum) {
        this.pid = pid;
        VirtualPageNum = virtualPageNum;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public int getVirtualPageNum() {
        return VirtualPageNum;
    }

    public void setVirtualPageNum(int virtualPageNum) {
        VirtualPageNum = virtualPageNum;
    }

    @Override
    public boolean equals(Object that) {
        if (that == null) return false;
        return this.pid == ((VirtualPageFlag) that).pid && this.VirtualPageNum == ((VirtualPageFlag) that).VirtualPageNum;
    }

    @Override
    public int hashCode() {
        return (new Integer(pid).toString() + new Integer(VirtualPageNum).toString()).hashCode();
    }
}
VMKernel类代码:
/**
 * A kernel that can support multiple demand-paging user processes.
 */
public class VMKernel extends UserKernel {
    /**
     * Allocate a new VM kernel.
     */
    public VMKernel() {
	super();
    }

    /**
     * Initialize this kernel.
     */
    public void initialize(String[] args) {
	super.initialize(args);
    }

    /**
     * Test this kernel.
     */	
    public void selfTest() {
	super.selfTest();
    }

    /**
     * Start running user programs.
     */
    public void run() {
	super.run();
    }
    
    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate() {
	super.terminate();
    }
    
   

    // dummy variables to make javac smarter
    private static VMProcess dummy1 = null;

    private static final char dbgVM = 'v';
    
    //空页
  	

  	//保证访问内存互斥的锁
}
VMProcess类代码:
/**
 * A <tt>UserProcess</tt> that supports demand-paging.
 */
public class VMProcess extends UserProcess {
    /**
     * Allocate a new process.
     */
    public VMProcess() {
	super();
	
		allocatedPages = new LinkedList<Integer>();
	    lazyLoadPages = new HashMap<Integer, CoffSectionAddress>();
	    tlbBackUp = new TranslationEntry[Machine.processor().getTLBSize()];
	    for (int i = 0; i < tlbBackUp.length; i++) {
	        tlbBackUp[i] = new TranslationEntry(-1, -1, false, false, false, false);
	    }
	    pagelength = Machine.processor().getNumPhysPages();
    }
    
    protected void swapOut(int pid, int vpn) {
        TranslationEntry entry = InvertedPageTable.getEntry(pid, vpn);
        
        if (entry == null) {

            return;
        }
        if (!entry.valid) {

            return;
        }
        for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            TranslationEntry tlbEntry = Machine.processor().readTLBEntry(i);
            //遍历tbl  置换出对应的页
            if (tlbEntry.vpn == entry.vpn && tlbEntry.ppn == entry.ppn && tlbEntry.valid) {
            	
            	
            	byte[] memory = Machine.processor().getMemory();
                int bufferOffset = Processor.makeAddress(entry.ppn, 0);
                byte[] data = new byte[pageSize];
                System.arraycopy(memory, entry.ppn * pageSize, data, 0, pageSize);
                System.out.println("从"+entry.ppn * pageSize+"开始为"+data);
                SwapperController.getInstance(SwapFileName).writeToSwapFile(pid, vpn, data, bufferOffset, tlbEntry.readOnly);
                //将反向页表中旧的页换掉
            	//表示 不在内存中
                InvertedPageTable.invalidEntry(pid, tlbEntry);
                tlbEntry.valid = false;
                //读取反向页表中对应的新页
                entry = InvertedPageTable.getEntry(pid, vpn);
                
                //写入tlb
                Machine.processor().writeTLBEntry(i, tlbEntry);
                break;
            }
        }

        
        System.out.println("换出成功");
    }
    
  //取得一个物理页 然后将 发生页错误的虚拟页 装到对应的物理页中
    protected void swapIn(int ppn, int vpn) {
    	
        TranslationEntry entry = InvertedPageTable.getEntry(processID, vpn);
        if (entry == null) {

            return;
        }
        if (entry.valid) {

            return;
        }

        
        boolean dirty, used;
        boolean readOnly = false;
        if (lazyLoadPages.containsKey(vpn)) {
        	System.out.println("第一次get"+vpn);
            readOnly = lazyLoad(vpn, ppn).isReadOnly();
            dirty = false;
            used = false;
        } else {
        	System.out.println("不是第一次/不是coff");
            //如果不是首次加载此coff  则将此物理页 从交换文件 复制到主存中
            byte[] memory = Machine.processor().getMemory();
            if(vpn<sectionlength)
            	readOnly = SwapperController.getInstance(SwapFileName).RD(processID, vpn);
            byte[] page = SwapperController.getInstance(SwapFileName).readFromSwapFile(processID, vpn);
            
            
            //SwapperController.getInstance(SwapFileName).deletePosition(processID, vpn);

            //src:源数组 srcPos:源数组要复制的起始位置 dest:目标数组  destPos:目标数组复制的起始位置 length:复制的长度
            
            Machine.processor().setMemory(page, 0, ppn * pageSize);
            //System.arraycopy(page, 0, memory, ppn * pageSize, pageSize);
            //memory = Machine.processor().getMemory();
            
            dirty = false;
            used = false;
            
            
        }
        TranslationEntry newEntry = new TranslationEntry(vpn, ppn, true, readOnly, used, dirty);
        
        //更改反向页表中此页的状态
        //InvertedPageTable.setEntry(processID, newEntry);
        InvertedPageTable.setEntry(processID, newEntry);
        	

        //InvertedPageTable.print(ppn);
    }
    
    protected int swap(int vpn) {
        TranslationEntry entry = InvertedPageTable.getEntry(processID, vpn);
        if (entry.valid)
            return entry.ppn;
        
        int ppn = getFreePage();
        System.out.println(processID+"想访问"+vpn+" 将装载到"+ppn);
        swapIn(ppn, vpn);
        return ppn;
    }
    
    protected CoffSection lazyLoad(int vpn, int ppn) {

        CoffSectionAddress coffSectionAddress = lazyLoadPages.remove(vpn);
        if (coffSectionAddress == null) {

            return null;
        }
        CoffSection section = coff.getSection(coffSectionAddress.getSectionNumber());
        section.loadPage(coffSectionAddress.getPageOffset(), ppn);
        return section;
    }
    
    protected int getFreePage() {
        //获取一个物理页
    	
        int ppn = VMKernel.getFreePage();

        if (ppn == -1) {
            //如果没有物理页了  需要选择一页牺牲掉
            TranslationEntryWithPid victim = InvertedPageTable.getVictimPage();
            ppn = victim.getTranslationEntry().ppn;
            System.out.println("缺页 换"+ppn+"的"+victim.getTranslationEntry().vpn);
            swapOut(victim.getPid(), victim.getTranslationEntry().vpn);
        }else {
        	
        }
        
        return ppn;
    }
    
  //重写readVirtualMemory方法
    public int readVirtualMemory(int vaddr, byte[] data, int offset, int length) {
    	
        pageLock.acquire();
       
        int vpn = Machine.processor().pageFromAddress(vaddr);
        //从反向页表读出对应的TranslationEntry
        //swap(vpn);
        TranslationEntry entry = InvertedPageTable.getEntry(processID, vpn);
        System.out.println("想读取"+vpn+"在"+entry.ppn);
        for(int i = 0; i < 4; i++) {
        	System.out.println(Machine.processor().readTLBEntry(i).vpn+" "+Machine.processor().readTLBEntry(i).ppn);
        }
        if (!entry.valid) {
            //表示 此页还没有被加载到物理页中   需要重新分配物理页
            int ppn = getFreePage();
            //System.out.println("选择了"+ppn);
            System.out.println("读时"+processID+"将"+vpn+" 将装载到"+ppn);
            swapIn(ppn, vpn);
        }
        //entry.used = true;
        //在反向页表中 更新对应的页

        InvertedPageTable.setEntry(processID, entry);
        pageLock.release();
        
    	
        //从虚拟内存地址读出到data中
        //偏移量与长度都要为正数,偏移量+长度<=总的数据长度
        Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length);
        //getMemory会返回主程序的数组
        byte[] memory = Machine.processor().getMemory();      //获得物理数组
        //计算剩下的页表字节个数
        if(length>(pageSize*numPages-vaddr))
           length=pageSize*numPages-vaddr;
        //计算能够传输的数据的大小,如果data数组中存不下length,则减小length(传输字节数)
        if(data.length-offset<length)
           length=data.length-offset;
        //转换成功的字节数
        int transferredbyte=0;
        do{
           //计算页号
           int pageNum=Processor.pageFromAddress(vaddr+transferredbyte);
           //页号大于 页表的长度 或者 为负 是异常情况
           if(pageNum<0||pageNum>=numPages)
              return 0;
           //计算页偏移量
           
           int pageOffset=Processor.offsetFromAddress(vaddr+transferredbyte);
           //计算剩余页的容量
           int leftByte=pageSize-pageOffset;
           //计算下一次传送的数量:剩余页容量和需要转移的字节数中较小者
           int amount=Math.min(leftByte, length-transferredbyte);
           //计算物理内存的地址
           
           
           
           int realAddress=entry.ppn*pageSize+pageOffset;
           
           //将物理内存的东西传输到虚拟内存
           System.arraycopy(memory, realAddress, data,offset+transferredbyte, amount);
           System.out.println(new String(data));
           //修改传输成功的字节数
           transferredbyte=transferredbyte+amount;
        }while(transferredbyte<length);
        return transferredbyte;
        
        //return super.readVirtualMemory(vaddr, data, offset, length);
    }

    public int writeVirtualMemory(int vaddr, byte[] data, int offset, int length) {
        
    	pageLock.acquire();

        int vpn = Processor.pageFromAddress(vaddr);
        //由于进程只能看到TLB 所以需要将需要的页换入
        
        	
        swap(vpn);
        TranslationEntry entry = InvertedPageTable.getEntry(processID, vpn);
        System.out.println("想写入"+vpn+"在"+entry.ppn);
        //entry.dirty = true;
        //entry.used = true;
        InvertedPageTable.setEntry(processID, entry);

        pageLock.release();
        

        Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length);
        //物理内存
        byte[] memory = Machine.processor().getMemory();
        //写内存的长度如果超过页剩余量
        if(length>(pageSize*numPages-vaddr))
           length=pageSize*numPages-vaddr;
        //如果数组中要写的长度比给定的小,则给length减为数组剩余的长度
        if(data.length-offset<length)
           length=data.length-offset;
        int    transferredbyte=0;
        do{
           //此函数返回给定地址的页号
           int pageNum=Processor.pageFromAddress(vaddr+transferredbyte);
           if(pageNum<0||pageNum>=numPages)
              return 0;
           //此函数返回给定地址的页偏移量
           int pageOffset=Processor.offsetFromAddress(vaddr+transferredbyte);
           //页剩余的字节数
           int leftByte=pageSize-pageOffset;
           //设置本次转移的数量 
           int    amount=Math.min(leftByte, length-transferredbyte);
           
           
           int realAddress=entry.ppn*pageSize+pageOffset;
           //从虚拟内存写入到物理内存(判断是否只读)
           if(!entry.readOnly) {
        	   System.arraycopy(data, offset+transferredbyte, memory, realAddress, amount);
           }
           //改变写成功的字节数
           transferredbyte=transferredbyte+amount;
        }while(transferredbyte<length);
        return transferredbyte;
        
        //return super.writeVirtualMemory(vaddr, data, offset, length);
    }
    
    protected TranslationEntry AllocatePageTable(int vpn) {
        return InvertedPageTable.getEntry(processID, vpn);
    }
    
    

    /**
     * Save the state of this process in preparation for a context switch.
     * Called by <tt>UThread.saveState()</tt>.
     */
    /**
     * 为了解决这个问题,您必须保证至少一个进程在上下文切换之前保证有2个TLB翻译以及内存中的2个对应页
     * 。这样,至少有一个进程能够在双误指令上取得进展。通过在上下文切换中保存和恢复TLB的状态,可以减少Live锁的效果,
     * 但是同样的问题可能发生在很少的物理内存页上。在这种情况下,在加载指令页和数据页之前,2个进程可能会以类似的方式被卡住。
     */
    public void saveState() {
	//super.saveState();
    	
    	for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            //保存此进程当前的TLB信息
            tlbBackUp[i] = Machine.processor().readTLBEntry(i);
            //保存此时TLB的状态到 反向页表中
            if (tlbBackUp[i].valid) {
                InvertedPageTable.updateEntry(processID, tlbBackUp[i]);
            }
        }
    }

    /**
     * Restore the state of this process after a context switch. Called by
     * <tt>UThread.restoreState()</tt>.
     */
    public void restoreState() {
	//super.restoreState();
    	
    	for (int i = 0; i < tlbBackUp.length; i++) {
            //如果此进程之前的TLB中有页在内存中  则
            if (tlbBackUp[i].valid) {
                //还原TLB信息
            	
                Machine.processor().writeTLBEntry(i, tlbBackUp[i]);
                TranslationEntry entry = InvertedPageTable.getEntry(processID, tlbBackUp[i].vpn);
                if (entry != null && entry.valid) {
                    Machine.processor().writeTLBEntry(i, entry);
                } else {
                    Machine.processor().writeTLBEntry(i, new TranslationEntry(0, 0, false, false, false, false));
                }
            } else {

                Machine.processor().writeTLBEntry(i, new TranslationEntry(0, 0, false, false, false, false));
            }
        }
    }

    /**
     * Initializes page tables for this process so that the executable can be
     * demand-paged.
     *
     * @return	<tt>true</tt> if successful.
     */
    protected boolean loadSections() {
	//return super.loadSections();
    	
    	// load sections
        //加载coff的  section
        for (int s = 0; s < coff.getNumSections(); s++) {
            CoffSection section = coff.getSection(s);

            for (int i = 0; i < section.getLength(); i++) {
                int virtualPageNum = section.getFirstVPN() + i;
                //将coffsection的加载变为懒加载
                CoffSectionAddress coffSectionAddress = new CoffSectionAddress(s, i);
                    lazyLoadPages.put(virtualPageNum, coffSectionAddress);
                    System.out.println("section: "+virtualPageNum);
            }
        }

        return true;
    }

    /**
     * Release any resources allocated by <tt>loadSections()</tt>.
     */
    protected void unloadSections() {
    	InvertedPageTable.print();
    	releaseResource();
	super.unloadSections();
	
    }    

    /**
     * Handle a user exception. Called by
     * <tt>UserKernel.exceptionHandler()</tt>. The
     * <i>cause</i> argument identifies which exception occurred; see the
     * <tt>Processor.exceptionZZZ</tt> constants.
     *
     * @param	cause	the user exception that occurred.
     */
    public void handleException(int cause) {
	Processor processor = Machine.processor();
	
		switch (cause) {
			case Processor.exceptionTLBMiss:
		        //导致TLB缺失的虚拟地址是通过调用processor.readRegister(processor.regBadVAddr);获得的
		        int address = processor.readRegister(Processor.regBadVAddr);
		        pageLock.acquire();
		        
		        //处理TLB缺页
		        boolean isSuccessful = handleTLBFault(address);
		        if (!isSuccessful) {
		        	
		            UThread.finish();
		        }
		        
		        pageLock.release();
		        
		        break;
			default:
				if(cause == 7) {
					InvertedPageTable.print();
				}
			    super.handleException(cause);
			    
			    break;
		}
    }
    
  //TLB错误  说明没有在TLB中找到对应的 虚拟页
    protected boolean handleTLBFault(int vaddr) {
        //虚拟页数
        int vpn = Processor.pageFromAddress(vaddr);
        //获取到 页错误发生的  反向页表中对应的TranslationEntry
        
        TranslationEntry entry = InvertedPageTable.getEntry(processID, vpn);
        System.out.println(processID+"想访问"+vpn);
        if (entry == null) {
        	
            return false;
        }
        //如果对应的页不在内存中(valid为false) 需要取一个物理页 分配物理页 (将页装入内存中) 然后装入tlb
        if (!entry.valid) {
        	System.out.println("miss");
            int ppn = getFreePage();
            System.out.println(processID+"将"+vpn+" 将装载到"+ppn);
            //Machine.processor().print();
            swapIn(ppn, vpn);
            
            entry = InvertedPageTable.getEntry(processID, vpn);
        }
        //否则直接牺牲TLB中的页 然后 置换
        int victim = getTLBVictim();
        System.out.println(processID+"受害者"+victim);
        replaceTLBEntry(victim, InvertedPageTable.getEntry(processID, vpn));
        
        System.out.println("-----------------全局页表-----------------------");
        InvertedPageTable.print();
        System.out.println("--------------------TLB--------------------");
        Machine.processor().print();
        System.out.println("----------------------------------------");
        System.out.println();
        
        return true;
    }
    
    
    
    protected void replaceTLBEntry(int index, TranslationEntry newEntry) {
    	
        TranslationEntry oldEntry = Machine.processor().readTLBEntry(index);
        if (oldEntry.valid) {
        	
            //InvertedPageTable.updateEntry(processID, newEntry);
        }

        Machine.processor().writeTLBEntry(index, newEntry);
    }

    //选择一个TLB中的页牺牲掉
    protected int getTLBVictim() {
        //如果
        for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            //如果此页现在已经不在主存中  则可以将它直接置换掉
            if (!Machine.processor().readTLBEntry(i).valid) {
                return i;
            }
        }
        //否则 随机置换一个
        int vic = Lib.random(Machine.processor().getTLBSize());
        
        return vic;

    }
    
  //给进程分配逻辑页  先设置页表条目  但不分配物理页
    protected boolean allocate(int vpn, int acquirePagesNum, boolean readOnly) {

        for (int i = 0; i < acquirePagesNum; ++i) {
            InvertedPageTable.insertEntry(processID, new TranslationEntry(vpn + i, -1, false, readOnly, false, false));
            SwapperController.getInstance(SwapFileName).insertUnallocatedPage(processID, vpn + i);
            allocatedPages.add(vpn + i);
            System.out.println("other: "+(vpn+i));
        }

        numPages += acquirePagesNum;

        return true;
    }

    protected void releaseResource() {
        for (int vpn : allocatedPages) {
            pageLock.acquire();

            TranslationEntry entry = InvertedPageTable.deleteEntry(processID, vpn);
            if (entry.valid)
                VMKernel.addFreePage(entry.ppn);

            SwapperController.getInstance(SwapFileName).deletePosition(processID, vpn);

            pageLock.release();
        }
    }

    protected boolean load(String name, String[] args) {
        Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
        
        OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
        if (executable == null) {
            Lib.debug(dbgProcess, "\topen failed");
            return false;
        }
        
        try {
            coff = new Coff(executable);
        } catch (EOFException e) {
            executable.close();
            Lib.debug(dbgProcess, "\tcoff load failed");
            return false;
        }

        // make sure the sections are contiguous and start at page 0
        numPages = 0;
        for (int s = 0; s < coff.getNumSections(); s++) {
            CoffSection section = coff.getSection(s);
            if (section.getFirstVPN() != numPages) {
                coff.close();
                Lib.debug(dbgProcess, "\tfragmented executable");
                
                return false;
            }
            //如果 所有的物理页都被分配 则 释放在主存的的那些页
            if (!allocate(numPages, section.getLength(), section.isReadOnly())) {
                releaseResource();
                
                return false;
            }
        }
        sectionlength = numPages;
        // make sure the argv array will fit in one page
        byte[][] argv = new byte[args.length][];
        int argsSize = 0;
        for (int i = 0; i < args.length; i++) {
            argv[i] = args[i].getBytes();
            // 4 bytes for argv[] pointer; then string plus one for null byte
            argsSize += 4 + argv[i].length + 1;
        }
        if (argsSize > pageSize) {
            coff.close();
            Lib.debug(dbgProcess, "\targuments too long");
            return false;
        }

        // program counter initially points at the program entry point
        initialPC = coff.getEntryPoint();
        
        // 分配堆栈页;堆栈指针最初指向它的顶部
        if (!allocate(numPages, stackPages, false)) {
            releaseResource();
            return false;
        }
        initialSP = numPages * pageSize;
        
        // 最后保留1页作为参数
        if (!allocate(numPages, 1, false)) {
            releaseResource();
            return false;
        }
        
        if (!loadSections())
            return false;
        
        int entryOffset = (numPages - 1) * pageSize;
        int stringOffset = entryOffset + args.length * 4;

        this.argc = args.length;
        this.argv = entryOffset;

        for (int i = 0; i < argv.length; i++) {
            byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
            Lib.assertTrue(writeVirtualMemory(entryOffset, stringOffsetBytes) == 4);
            entryOffset += 4;
            Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) == argv[i].length);
            stringOffset += argv[i].length;
            Lib.assertTrue(writeVirtualMemory(stringOffset, new byte[]{0}) == 1);
            stringOffset += 1;
        }
        
        return true;
    }
    
    public boolean execute(String name, String[] args) {
    	
    	if (!load(name, args)) {
    		return false;
    	}
    	    
    	
    	UThread t = (UThread) new UThread(this).setName(name);
    	pidThreadMap.put(this.processID, t);
    	t.fork();
    	
    	return true;
        }
	
    private static final int pageSize = Processor.pageSize;
    private static final char dbgProcess = 'a';
    private static final char dbgVM = 'v';
    
    //处理器只能看到TLB
    protected TranslationEntry[] tlbBackUp;

    protected static Lock pageLock = new Lock();
    //实现coffsection的懒加载
    protected HashMap<Integer, CoffSectionAddress> lazyLoadPages;//还没有加载的coff
    private static String SwapFileName = "Proj3SwapFile";
    
}
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值