// Solution to Project 3 in Java import java.text.*; import java.util.*; import java.io.*; // Element of the Inverse Page Table class InvTableEntry { int page, process, // page and process currently in frame holdingPage, holdingProcess; // page and process that will be loaded // into frame public InvTableEntry(int pg, int pr) { page = pg; process = pr; holdingPage = -1; holdingProcess = -1; } } public class Project3 { private static STM stm; private static final int MAXPROCS = 20; private static int NFRAMES= 8; // Number of frames defaults to 8 private Process procTable[]; // Process table private CircularArray readyq; // Ready queue of processes private Disk myDisk; // Simulated disk for paging. private InvTableEntry InvTable[]; // Global variables for the simulated architecture --- DO NOT CHANGE // Other global variables private int maxInst = -1, // Maximum number of instructions procIndex = 0; // index of running process private String procName; private boolean terminate = false, // Termination flag finished = false, // Flag that all processes are done idle = false; // Flag that all processes are blocked private int quantum = 5, // quantum qleft, // Time left in quantum nprocs, debug = 0; // Number of processes private String replaceAlg = "GN"; private boolean freeList[]; // free list of frames as bit vector: true if occupied private InputStreamReader isr = new InputStreamReader(System.in); private BufferedReader inReader = new BufferedReader(isr); // freeFrame returns an index of the first free frame and marks it // as no longer free. // If not frames are free, it returns -1. private int freeFrame() { int i; for (i = 0; i < stm.NFRAMES; i++) if (!freeList[i]) { freeList[i] = true; return(i); } return(-1); } // Extract the flags from the command line: quantum, debug, and maxInst. // The index of the first STM filename in the command is returned as the value. private int parseFlags(String args[]) { int iquantum= 0, idebug = 0, imax=0, ifilename=0, ialg = 0, iframe = 0; // Positions of arguments in command line for (int i=0; i<8; i=i+2) { if (args[i].charAt(0) == '-') switch (args[i].charAt(1)) { case 'q': iquantum= i+1; break; case 'd': idebug = i+1; break; case 'm': imax = i+1; break; case 'a': ialg = i+1; break; case 'f': iframe = i+1; break; } else { ifilename = i; break; } } if (iquantum != 0) quantum= Integer.parseInt(args[iquantum]); if (idebug != 0) debug=Integer.parseInt(args[idebug]); if (imax != 0) maxInst=Integer.parseInt(args[imax]); if (ialg != 0) replaceAlg = args[ialg]; if (iframe != 0) NFRAMES= Integer.parseInt(args[iframe]); return(ifilename); } // Convert hexadecimal or decimal string to integer. // No doubt there's something that does this somewhere in // the Java library, but it's easier to write it than to search for it. // Note: This doesn't handle negative numbers. int parseNumber(String line) { StringTokenizer st = new StringTokenizer(line); line = st.nextToken(); if ((line.length() > 1) && (line.charAt(0) == '0') && ((line.charAt(1) == 'x') || (line.charAt(1) == 'X'))) return(Integer.parseInt(line.substring(2,line.length()), 16)); else return(Integer.parseInt(line)); } // Load the STML code into memory // It is assumed that STML files are correctly formatted; therefore, // this does no error checking // NOTE: You are allowed to change this function for project 1; however, // there is no need to do so. // I/O in Java is extraordinarily clumsy. void loadStm(String filename) { int frame, // current frame page, offset = stm.PAGESIZE, // offset within current page address = 0, // memory address inst; // Instruction * int i, va = 0; String line; try { FileReader inputfile = new FileReader(filename); BufferedReader reader = new BufferedReader(inputfile); procName = reader.readLine(); reader.readLine(); // 2nd line of STML file not used in paging system. if (debug == 2) { System.out.println("Process " + procName + " index " + nprocs); System.out.println("Physical address / Virtual address / Instruction"); } page = 0; while ((line = reader.readLine()) != null) { if (line.length() > 0) if (Character.isDigit(line.charAt(0))) { if (offset == stm.PAGESIZE) { // Start new page for program text frame = freeFrame(); address = frame * stm.PAGESIZE; offset = 0; procTable[nprocs].pageTable[page].frame = frame; procTable[nprocs].pageTable[page].mbit = true; InvTable[frame] = new InvTableEntry(page,nprocs); if (debug == 2) System.out.println("Loading to frame " + frame); page++; } stm.mem[address] = parseNumber(line); if (debug == 2) System.out.println("0x" + Integer.toString(address,16) + " 0x" + Integer.toString(va,16) + " 0x" + Integer.toString(stm.mem[address],16)); va++; address++; offset++; } } inputfile.close(); } catch (FileNotFoundException fnf) {System.out.println("File not found");} catch (IOException ioe) {System.out.println("IO Error");} } // Load the STM files into memory, and initialize process table private void loadProcs(int ifilename, String args[]) { int i,j; readyq = new CircularArray(MAXPROCS); nprocs=0; for (i=ifilename; i < args.length; i++) { procTable[nprocs] = new Process(stm.NPAGES); loadStm(args[i]); procTable[nprocs].name = procName; readyq.AddQueue(new Integer(nprocs)); nprocs++; } } /* resumes a new process by resetting registers, page table, other global variables */ private void activateProc(int p) { stm.regs = procTable[p].regs; procName = procTable[p].name; qleft = quantum; stm.pageTable = procTable[p].pageTable; procIndex = p; } // Set global flags, load the STM files, initialize the STM process private void initialize(String args[]) { int ifilename; ifilename = parseFlags(args); stm = new STM(NFRAMES, debug); myDisk = new Disk(MAXPROCS * stm.VASPACE, debug); freeList = new boolean[NFRAMES]; InvTable = new InvTableEntry[NFRAMES]; procTable = new Process[MAXPROCS]; loadProcs(ifilename, args); activateProc(((Integer) readyq.PopQueue()).intValue()); } /* Execute a trap. */ private void execTrap() { int iread; if (debug != 0) System.out.print("Process " + procName + " index " + procIndex + " Trap: "); switch(stm.regs[15]) { case 0: terminate=true; if (debug != 0) System.out.println(" Terminate"); // release frames held by terminating process for (int i = 0; i < NFRAMES; i++) if (InvTable[i] != null) if (InvTable[i].process == procIndex) { freeList[i] = false; InvTable[i].process = -1; InvTable[i].page= -1; } break; case 1: if (debug != 0) System.out.println(" Read"); try { stm.regs[14] = Integer.parseInt(inReader.readLine()); if (debug != 0) System.out.println(" READ " + stm.regs[14]); stm.regs[13]=1; } catch(EOFException eof) {stm.regs[13]=0;} catch(IOException ioe) {System.out.println("IO Error");} break; case 2: if (debug != 0) System.out.print("\n WRITE: "); System.out.println(stm.regs[14]); break; default: System.out.println("Invalid trap code"); stm.error=true; break; } } // Boolean: Should there be a context switch? private boolean shouldSwitch(boolean trap) { qleft--; return ((qleft==0) || trap || stm.error); } // Check if all jobs are terminated private boolean isFinished() { boolean terminated = true; for (int i = 0; i < nprocs; i++) terminated = terminated && (procTable[i].status == Process.TERMINATED); return(terminated); } // Carry out a context switch. Returns 1 if there is a new process, 0 if // all processes have terminated private void contextSwitch(boolean block) { int newp; int i; procTable[procIndex].regs = stm.regs; // Save current registers procTable[procIndex].pageTable = stm.pageTable; // Save current page table if (terminate || stm.error) procTable[procIndex].status = Process.TERMINATED; else if (block) { procTable[procIndex].status = Process.BLOCKED; if (debug == 1) System.out.println("Blocking process " + procIndex); } else readyq.AddQueue(new Integer(procIndex)); terminate = false; stm.error = false; if (isFinished()) // All processes have terminated { finished = true; if (debug != 0) System.out.println("All processes terminated"); } else if (readyq.EmptyQueue()) // All processes are blocked idle = true; else { newp = ((Integer) readyq.PopQueue()).intValue(); if (debug != 0) System.out.println("\nCONTEXT SWITCH from process " + procIndex + " to process " + newp); activateProc(newp); } } int pageReplace() { int first = 0, tfirst, page, proc, time; tfirst = stm.clock; if (debug == 2) { System.out.println(); System.out.println("Calling page replacement algorithm"); } for (int i=0; i < NFRAMES; i++) { if (InvTable[i].holdingPage == -1) { proc = InvTable[i].process; page = InvTable[i].page; if (proc == procIndex) { if (debug == 2) System.out.println("Frame " + i + " Process " + proc + " Page " + page + " Time " + stm.pageTable[page].timestamp); if (stm.pageTable[page].timestamp < tfirst) { tfirst = stm.pageTable[page].timestamp; first = i; } } else if (replaceAlg == "GN") { if (debug == 2) System.out.println("Frame " + i + " Process " + proc + " Page " + page + " Time " + procTable[proc].pageTable[page].timestamp); if (procTable[proc].pageTable[page].timestamp < tfirst) { tfirst = procTable[proc].pageTable[page].timestamp; first = i; } } } } return(first); } int diskAddress(int proc, int page) { return(proc*stm.VASPACE + page*stm.PAGESIZE); } /* There are six cases: The cross product of { 1. Frame is free, 2. Frame is clean, 3. Frame is dirty with A. Page is new B. Page is on disk. } */ void execPageFault() { int page = stm.PageFault, // Page to be read in frame, address, // Frame to be used oldpage = 0, oldprocess = 0, // Page, process to overwrite da = 0; // disk address boolean mbit = false; // mbit on frame to be used. if (debug != 0) System.out.println("Page fault: process " + procIndex + " page " + page); frame = freeFrame(); if (frame != -1) { stm.pageTable[page].frame = frame; procTable[procIndex].pageTable[page].frame = frame; InvTable[frame] = new InvTableEntry(page, procIndex); if (debug != 0) System.out.println("Found free frame " + frame); } else { frame = pageReplace(); if (debug != 0) System.out.println("Using frame " + frame); oldpage = InvTable[frame].page; oldprocess= InvTable[frame].process; InvTable[frame].holdingPage = page; InvTable[frame].holdingProcess = procIndex; if (oldprocess == procIndex) procTable[oldprocess].pageTable[oldpage] = stm.pageTable[oldpage]; mbit = procTable[oldprocess].pageTable[oldpage].mbit; } address = frame * stm.PAGESIZE; if (mbit) // Old page is dirty { if (debug != 0) System.out.println("Writing out dirty page " + oldpage + " of process " + oldprocess + " in frame " + frame); da = diskAddress(oldprocess,oldpage); myDisk.PostRequest(da, address, stm.PAGESIZE, true); procTable[oldprocess].pageTable[oldpage].disk = da; procTable[oldprocess].pageTable[oldpage].frame= -1; contextSwitch(true); } else if (procTable[procIndex].pageTable[page].disk != -1) // page on disk { if (debug != 0) System.out.println("Reading in page " + page + " of process " + procIndex + " to clean frame " + frame); InvTable[frame].holdingPage = page; InvTable[frame].holdingProcess = procIndex; da = diskAddress(procIndex,page); myDisk.PostRequest(da, address, stm.PAGESIZE, false); contextSwitch(true); } } // fetch and execute an instruction of STM code public void execStm() { int opcode, rega, ad, regb, regc, regd; // Components of STML instruction boolean trap; // flag that code executes a trap int padd; // physical address int i, inst; padd = stm.physAddress(stm.regs[0], false); if (stm.PageFault != -1) execPageFault(); else { if (debug == 2) { System.out.println("Registers: "); for (i=0; i<16; i++) System.out.print(" " + stm.regs[i]); System.out.print("\n"); System.out.println("address 0x" + Integer.toString(stm.regs[0],16) + " instruction 0x" + Integer.toString(stm.mem[padd],16)); } inst = stm.mem[padd]; // Get instruction opcode = inst % 16; // Extract fields of instruction inst = inst / 16; rega = inst % 16; inst = inst / 16; ad = inst % stm.RAMSIZE; regb = inst % 16; inst = inst / 16; regc = inst % 16; inst = inst / 16; regd = inst % 16; if (debug == 2) stm.showInst(opcode, rega, ad, regb, regc, regd); trap = stm.execInst(opcode, rega, ad, regb, regc, regd); //execute if (stm.PageFault != -1) execPageFault(); else { if (trap) execTrap(); // handle trap if (shouldSwitch(trap)) contextSwitch(false); } } } public void HandleDiskReport(DiskTask Task) { int page, process; boolean done = true; int frame = Task.memAddress / stm.PAGESIZE; page = InvTable[frame].holdingPage; process= InvTable[frame].holdingProcess; if (debug != 0) System.out.print("Disk task complete: frame " + frame); if (Task.write) { // write completed if (debug != 0) System.out.println(" written out"); if (procTable[process].pageTable[page].disk != -1) // page on disk { if (debug != 0) System.out.println("Reading in page " + page + " of process " + process + " to now-clear frame " + frame); myDisk.PostRequest(diskAddress(process,page), Task.memAddress, stm.PAGESIZE, false); done = false; } } else if (debug != 0) System.out.println(" proc " + process + " page " + page + " read in "); if (done) { // either if read is complete or write is complete and // no read needed procTable[process].status = Process.READY; procTable[process].pageTable[page].frame = frame; procTable[process].pageTable[page].timestamp = stm.clock; procTable[process].pageTable[page].rbit = true; procTable[process].pageTable[page].mbit = false; InvTable[frame].holdingPage = -1; InvTable[frame].holdingProcess = -1; InvTable[frame].page = page; InvTable[frame].process= process; if (idle) { // unblock process idle = false; activateProc(process); if (debug != 0) System.out.println("Unblocking and running process " + process); } else { readyq.AddQueue(new Integer(process)); if (debug != 0) System.out.println("Unblocking process " + process + " and putting on ready queue."); } } } public void execIdle() { stm.clock++; if (debug == 2) System.out.println("IDLE Time: " + stm.clock); } // run: initialize, load, execute. public void run(String[] args) { initialize(args); for (int i=0; (!finished && (i != maxInst)); i++) { if (debug == 2) System.out.println("\nMachine cycle: " + stm.clock); DiskTask Task = myDisk.DiskAction(stm.mem); if (Task != null) HandleDiskReport(Task); // corresponds to disk interrupt if (idle) execIdle(); else execStm(); } } // main: run an instance of Project3. public static void main(String[] args) { Project3 Proj3= new Project3(); Proj3.run(args); } }