//////////////////////////////////////////////////////////////////////////////// // Author: Julie Petrusa (991339) // // Due date: March 22, 2002 // // Project description: Small Operating System Simulator // // -Console: Provides reading and writing ability. The user specifies the // // source of input, and the destination of output.  // // -CPU: Responsible for keeping track of the currently running process and // // whether or not the CPU is idle. // // -Interrupt: Responsible for converting a string of input into an interrupt // // number plus an array of arguments. // // -IODevice: Maintains the waiting queue for a particular device. It also // // looks to the waiting queue each time the device become idle to // // see if a request must be initiated. // // -Memory: Memory management system that uses a page table. Maintains 64 // // frames of size 1k. // // -OS: Sets up system. Processes interrupts until end of file or shut down // // request. Shuts down system. Contains main. // // -PCB: Responsible for creating and maintaining Process Control Blocks. The // // state, priority, burst time, and page table are set and read here. // // -Queue: Uses Java's linked list to implement FIFO queues. // // -Scheduler: Responsible for maintaining the ready queue for the CPU, and // // for dispatching jobs to the CPU. The ready queue is a multiple // // level feedback queue with three levels: Q0, Q1, and Q2. Q0 has // // the highest priority and Q2 has the lowest. // // Known bugs: none // //////////////////////////////////////////////////////////////////////////////// import java.io.*; import java.util.*; import java.util.Iterator; import java.util.StringTokenizer; import java.lang.Integer; /******************************************************************************/ /******************************************************************************/ class Console { //--------------------------------------------------------------------------- // fields // this reader is used to read from the keyboard private InputStreamReader reader = new InputStreamReader(System.in); private BufferedReader keyboardReader = new BufferedReader(reader); // reader for the input file private FileReader fr = null; // reader for keyboard or file private BufferedReader stdin = null; private int fromKeyboard = 0; // similar setup for the output private FileOutputStream fw = null; private PrintStream stdout = null; private int toScreen = 0; //--------------------------------------------------------------------------- // constructor // -sets up the input and output streams // -determines from where the user wants the input read // -determines to where the user wants the output to go public Console() { String inputString; System.out.println("\nInitializing console ..."); do // does user want console bound to keyboard or file for input { System.out.print("To read from: "); System.out.print("the keyboard enter 0; a file enter 1: "); try { inputString = keyboardReader.readLine(); fromKeyboard = Integer.parseInt(inputString); } catch (IOException e) { System.err.println("Oops! Expecting an integer"); } if ((fromKeyboard < 0) || (fromKeyboard > 1)) { System.err.println("Expecting a 0 or a 1"); } } while ((fromKeyboard < 0) && (fromKeyboard > 1)); if (fromKeyboard == 1) // user wants to read from a file { System.out.print("Enter the name of the input file: "); inputString = ""; try { inputString = keyboardReader.readLine(); } catch (IOException e) { System.err.println("Problems reading input file name."); System.exit(-1); } System.out.println("Opening file "+inputString+" for input."); try { fr = new FileReader(inputString); } catch (FileNotFoundException e) { System.err.println("File not found."); System.exit(-1); } stdin = new BufferedReader(fr); } else // user wants to read from the keyboard stdin = keyboardReader; do // setting up the output { // does user want console bound to keyboard or file for input System.out.print("To write to: the screen enter 0; "); System.out.print("a file enter 1; both enter 2: "); inputString = ""; try { inputString = keyboardReader.readLine(); toScreen = Integer.parseInt(inputString); } catch (IOException e) { System.err.println("Oops! Expecting an integer"); } if ((toScreen < 0) || (toScreen > 2)) { System.err.println("Expecting 0, 1, or 2"); } } while ((toScreen < 0) && (toScreen > 2)); if (toScreen > 0) // wants to write to file (possibly screen too) { System.out.print("Enter the name of the output file: "); inputString = ""; try { inputString = keyboardReader.readLine(); } catch (IOException e) { System.err.println("Problems reading output file name."); System.exit(-1); } System.out.println("Opening file "+inputString+" for output."); try { fw = new FileOutputStream(inputString); } catch (IOException e) { System.err.println("Problems opening file for output."); System.exit(-1); } stdout = new PrintStream(fw); } else // user wants to write to the screen stdout = System.out; } //--------------------------------------------------------------------------- // method readLine // -reads a line from the input device public String readLine() { String s = null; try { s = stdin.readLine(); } catch (IOException e) { } return s; } //--------------------------------------------------------------------------- // method print // -prints s on output device(s) public void print(String s) { stdout.print(s); if (toScreen == 2) System.out.print(s); } //--------------------------------------------------------------------------- // method println // -prints s plus a line feed on output device(s) public void println(String s) { stdout.println(s); if (toScreen == 2) System.out.println(s); } //--------------------------------------------------------------------------- } // end Console /******************************************************************************/ /******************************************************************************/ class CPU { //--------------------------------------------------------------------------- // fields private PCB runningProcess; private boolean idle; //--------------------------------------------------------------------------- // constructor // -initially the CPU is idle and no process is running public CPU() { idle = true; runningProcess = null; } //--------------------------------------------------------------------------- // method currentProcess // -returns PCB of currently running process // -it could be null public PCB currentProcess() { return runningProcess; } //--------------------------------------------------------------------------- // method isIdle // -returns true if and only if CPU is idle public boolean isIdle() { return idle; } //--------------------------------------------------------------------------- // method runProcess // -makes p the running process and CPU non idle // -if something else is running, p will overwrite it (should probably call // stopProcess first) // -if p is not null, set its state appropriately, and set idle to indicate // the CPU is running a process public void runProcess(PCB p) { runningProcess = p; if (p != null) { p.setStateRunning(); idle = false; } } //--------------------------------------------------------------------------- // method stopProcess // -stops the current process // -current process is removed from CPU // -CPU becomes idle // -running process become null // -returns process that was running public PCB stopProcess() { PCB currentProcess = runningProcess; runningProcess = null; idle = true; return currentProcess; } //--------------------------------------------------------------------------- } // end CPU /******************************************************************************/ /******************************************************************************/ class Interrupt { //--------------------------------------------------------------------------- // fields private int value; private ArrayList args = new ArrayList(); //--------------------------------------------------------------------------- // default constructor // -initializes interrupt to the 0 interrupt // -should call string2Interrupt before using the interrupt public Interrupt() { value = 0; } //--------------------------------------------------------------------------- // method argument // -returns (as a string) argument number i (count from 1) // -must call string2Interrupt before this can be used // -throws outOfBoundsException public String argument(int i) { return (String)args.get(i-1); } //--------------------------------------------------------------------------- // method numberOfArguments // -returns number of arguments after the interrupt number // -must call string2Interrupt before this can be used public int numberOfArguments() { return args.size(); } //--------------------------------------------------------------------------- // method string2Interrupt // -converts a string of the form "iterrupt_number argument_1 argument_2 ... // argument_k" to an integer interrupt number (value), and an array of // arguments stored as strings public void string2Interrupt(String s) { StringTokenizer tokenizer = new StringTokenizer(s); String token; if (tokenizer.hasMoreTokens()) // try to read the first integer { token = tokenizer.nextToken(); try { value = Integer.parseInt(token); System.out.println(value); } catch (NumberFormatException e) { System.out.print("Expecting an integer value for interrupt"); System.out.println(); System.exit(-1); } } else { System.out.println("No interrupt on this input line."); System.exit(-1); } args.clear(); // read the arguments and put them in the array while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); args.add(token); } } //--------------------------------------------------------------------------- // method value // -returns interrupt number as an integer // -must be called after string2Interrupt public int value() { return value; } //--------------------------------------------------------------------------- } // end Interrupt /******************************************************************************/ /******************************************************************************/ class IODevice { //--------------------------------------------------------------------------- // fields private Console console; private boolean idle = true; private Queue requestQueue = new Queue(); private int whoAmI; //--------------------------------------------------------------------------- // constructor // -creates a device with number deviceNumber // -sets up the console for message printing public IODevice(int deviceNumber, Console con) { whoAmI = deviceNumber; console = con; } //--------------------------------------------------------------------------- // method addToRequestQueue // -adds the process p to the request queue for this device // -if the device is idle, a request for p is initiated on the device // -sets the state of p to waiting // -prints a status message to console public void addToRequestQueue(PCB p) { if (p == null) { return; // don't add null process } console.print(" Add process "+p+" to queue for device "); console.println(whoAmI+"."); requestQueue.put(p); p.setStateWaiting(); if (idle) this.initiate(); } //--------------------------------------------------------------------------- // method currentRequest // -returns the PCB of the process whose request is currently being processed public PCB currentRequest() { return (PCB)requestQueue.front(); } //--------------------------------------------------------------------------- // method initiate // -initiates IO on the device // -selects a process to service from its wait queue // -the queue is FIFO, and the device is assumed to be idle when the method // is called // -if a process is waiting, the device is not idle after the call // -prints a status message to console public void initiate() { if (!requestQueue.isEmpty()) { console.print(" IO initiated on device "+whoAmI); console.println(" for process "+(PCB)requestQueue.front()+"."); idle=false; } else { console.print(" IO device "+whoAmI+" has no requests. "); console.println("Device idle."); } } //--------------------------------------------------------------------------- // method isIdle // -returns true if and only if the device is idle public boolean isIdle() { return idle; } //--------------------------------------------------------------------------- // method iterator // -returns an iterator for the request queue public Iterator iterator() { return requestQueue.iterator(); } //--------------------------------------------------------------------------- // method removeRequest // -removes the current request and initiates a new one // -returns the PCB of the process currently being serviced // -assumes a nonnull process is being serviced, i.e. idle = false // -will be idle after the call // -initiates IO public PCB removeRequest() { PCB currentRequest = (PCB)requestQueue.get(); console.print(" Request for IO device "+whoAmI); console.println(" by process "+currentRequest+" is terminated."); idle = true; this.initiate(); // start the next request return currentRequest; } //--------------------------------------------------------------------------- } // end IODevice /******************************************************************************/ /******************************************************************************/ class Memory { //--------------------------------------------------------------------------- // fields private int totalFrames; private int freeFrames; private String[] frames; //--------------------------------------------------------------------------- // constructor // -system has 64 frames of size 1K public Memory() { totalFrames = 64; freeFrames = 64; frames = new String[totalFrames]; } //--------------------------------------------------------------------------- // method allocate // -allocates memory // -creates and returns a page table // -decrements the number of free frames public int[] allocate(PCB p, int nPages) { int[] pageTable = new int[nPages]; for (int i=0; i 0); } //--------------------------------------------------------------------------- // method getFramesStr // -returns a string for printing public String getFramesStr() { String str = ""; for (int i=0; i 3 && current.getPriority() == 0) current.setPriority(1); if (current.getBurstTime() > 9 && current.getPriority() == 1) current.setPriority(2); int priority = current.getPriority(); if (priority != temp) { console.println(" Context change."); for (int i=0; i numberOfIODevices)) { System.err.println("Bad device number."); System.exit(-1); } return device; } //--------------------------------------------------------------------------- // method requestIO // -gets the device number // -passes the (not null) request to the ioDevice // -dispatches a new process to run private void requestIO() { console.println("IO request:"); int device = getDeviceNumber(); PCB runningProcess = theCPU.stopProcess(); if (runningProcess != null) // stop process that requested IO ioDevice[device].addToRequestQueue(runningProcess); else console.println(" No running process made this request!"); for (int i=0; i 0) { console.println(" Context change."); theScheduler[0].dispatch(); theScheduler[current.getPriority()].addToReadyQueue(current); } } } //--------------------------------------------------------------------------- // method abortIO // -same as finishIO except the request is forced by OS, and thus the // device must be named private void abortIO(int device) { console.println("Aborting IO on device "+device+":"); if (ioDevice[device].isIdle()) { console.print(" No process was waiting for IO on device "); console.println(device+"."); } else { PCB doneProcess = ioDevice[device].removeRequest(); doneProcess.resetBurstTime(); theScheduler[0].addToReadyQueue(doneProcess); } } //--------------------------------------------------------------------------- // method getNPages // -is used by createProcess and requestPages to read the number of pages // from the interrupt arguments private int getNPages() { if (event.numberOfArguments() < 1) { System.err.println("Expecting number of pages."); System.exit(-1); } int nPages = -1; try { nPages = Integer.parseInt(event.argument(1)); } catch (NumberFormatException e) { System.err.println("Expecting a number (integer)."); System.exit(-1); } if ((nPages < 0) || (nPages > 64)) { System.err.println("Bad number of pages."); System.exit(-1); } return nPages; } //--------------------------------------------------------------------------- // method createProcess // -creates a new PCB and gives it to the scheduler // -processes added to the ready queue always enter Q0 // -the system is preemptive // -if a process of higher priority than the running process becomes ready, // then the currently running process is preempted // -prints a message that the process was successfully created or that the // creation failed // -in the case of successful creation, also prints the page table created // for the process // -creation will succeed if there is enough free memory private void createProcess() { console.println("Create process:"); int nPages = getNPages(); if (memory.enoughFree(nPages)) { PCB newProcess = new PCB(); newProcess.setPageTable(memory.allocate(newProcess, nPages)); console.println(" Creating process "+newProcess+"."); console.println(" Page Table: "+newProcess.getPageTableStr()); theScheduler[0].addToReadyQueue(newProcess); PCB current = theCPU.currentProcess(); if (current.getPriority() > 0) { console.println(" Context change."); theScheduler[0].dispatch(); theScheduler[current.getPriority()].addToReadyQueue(current); } } else console.println(" Creation failed, not enough free memory."); } //--------------------------------------------------------------------------- // method terminateProcess // -terminates process and schedules something new // -releases the process' memory private void terminateProcess() { PCB runningProcess = theCPU.stopProcess(); if (runningProcess != null) { console.println("Terminating process "+runningProcess+":"); runningProcess.setStateTerminated(); runningProcess.setPageTable(memory.release(runningProcess)); } for (int i=0; i