jsos
college code for operating system fundamentals in js
git clone https://9o.is/git/jsos.git
commit 76a7e8938387866ba6993f0b194ff3b3dfa590b2 parent 4ebd5a4494cfcb829787b19994a665cd8c7aed93 Author: Jul <jul@9o.is> Date: Wed, 7 Nov 2012 12:30:35 -0500 Almost complete with iProject 3. Pulled third all nighter. yay Diffstat:
| M | globals.js | | | 6 | ++++++ |
| M | index.html | | | 1 | + |
| M | scripts/host/cpu.js | | | 71 | +++++++++++++++++++++-------------------------------------------------- |
| M | scripts/host/memory.js | | | 6 | +++--- |
| M | scripts/os/kernel.js | | | 35 | ++++++++++++++++++++++------------- |
| M | scripts/os/memoryManager.js | | | 2 | +- |
| M | scripts/os/pcb.js | | | 15 | ++++++++++++++- |
| A | scripts/os/scheduler.js | | | 84 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | scripts/os/shell.js | | | 113 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
| M | scripts/utils.js | | | 9 | +++++++-- |
10 files changed, 268 insertions(+), 74 deletions(-)
diff --git a/globals.js b/globals.js @@ -57,6 +57,7 @@ var _KernelBuffers = null; var _KernelInputQueue = null; var _KernelMemoryManager = null; var _KernelReadyQueue = null; +var _KernelCPUScheduler = null; // Standard input and output var _StdIn = null; @@ -89,3 +90,8 @@ var PCB_STATE_TERMINATED = "terminated"; var _ERROR = "ERROR: "; var _ERROR_INVALID_SYSTEM_CALL = _ERROR+"INVALID SYSTEM CALL"; var _ERROR_INVALID_OPERATOR = _ERROR+"INVALID OPERATOR"; +var _ERROR_STACK_OVERFLOW = _ERROR+"STACK OVERFLOW"; +var _ERROR_INVALID_BYTE_VALUE = _ERROR+"INVALID BYTE VALUE"; + +// Round Robin Quantum +var RR_QUANTUM = 6; diff --git a/index.html b/index.html @@ -34,6 +34,7 @@ <script type="text/javascript" src="scripts/os/kernel.js"></script> <script type="text/javascript" src="scripts/os/pcb.js"></script> <script type="text/javascript" src="scripts/os/memoryManager.js"></script> + <script type="text/javascript" src="scripts/os/scheduler.js"></script> <!-- Other Routines --> <script type="text/javascript" src="scripts/utils.js"></script> diff --git a/scripts/host/cpu.js b/scripts/host/cpu.js @@ -24,8 +24,6 @@ function cpu() { this.X = 0; this.Y = 0; this.Z = 0; - this.isExecuting = false; - this.running = undefined; // process currently running this.init = function() { this.output(); @@ -34,34 +32,15 @@ function cpu() { /* Executes a CPU cycle. */ this.cycle = function() { krnTrace("CPU cycle"); - - // if process is not running, and ready queue is empty - // stop cpu cycles - if(this.proccessTerminated() && _KernelReadyQueue.isEmpty()) - this.isExecuting = false; - - // if ready is not empty, dequeue - else if(this.proccessTerminated()) { - this.setRunningProcess(_KernelReadyQueue.dequeue()); - this.execute(); - } - - // or continue executing the current process - else this.execute(); - - // output current changes in the registry + this.execute(); this.output(); - this.running.updateRegisters( - this.PC, this.ACC, this.X, this.Y, this.Z); } /* * Executes the byte in location PC. */ this.execute = function() { - this.isExecuting = true; var operator = _Memory.getByte(this.PC++); - switch(operator) { // LDA: Load the accumulator with a constant case 0xA9: @@ -75,7 +54,7 @@ function cpu() { // STA: Store the accumulator in memory case 0x8D: - _Memory.setByte(this.ACC, this.immediateOperand()); + _Memory.setByte(this.ACC, this.immediateOperand() + this.base()); this.PC += 1; break; @@ -123,7 +102,10 @@ function cpu() { case 0xD0: if(this.Z == 0) { this.PC += this.immediateOperand()+1; - this.PC -= this.PC > 0xFF ? 0x100 : 0; + if(this.PC > this.limit()) { + this.PC -= this.limit()+1; + this.PC += this.base(); + } } else this.PC += 1; @@ -131,7 +113,7 @@ function cpu() { // INC: Increment the value of a byte case 0xEE: - var location = _Memory.getByte(this.PC); + var location = _Memory.getByte(this.PC) + this.base(); _Memory.setByte(this.directOperand() + 1, location); break; @@ -140,18 +122,21 @@ function cpu() { krnInterrupt(SYSCALL_IRQ, 0xFF); break; - // unknown operator - default: - krnInterrupt(SYSCALL_IRQ, -1); - break; } +console.log( +"PID: "+_KernelCPUScheduler.running.PID+ +" PC: "+pad(this.PC.toString(16),2)+ +" AC: "+pad(this.ACC.toString(16),2)+ +" X: "+pad(this.X.toString(16),2)+ +" Y: "+pad(this.Y.toString(16),2)+ +" Z: "+this.Z); } /* * Gets operand from memory in location PC. */ this.directOperand = function() { - var location = _Memory.getByte(this.PC); + var location = _Memory.getByte(this.PC) + this.base(); var value = _Memory.getByte(location); this.PC += 2; return value; @@ -166,28 +151,14 @@ function cpu() { return value; } - /* - * Sets a process as the currently executing process - * transfers PCB registers to the CPU. - */ - this.setRunningProcess = function(process) { - this.running = process; - process.setState(PCB_STATE_RUNNING); - this.PC = process.PC; - this.ACC = process.ACC; - this.X = process.X; - this.Y = process.Y; - this.Z = process.Z; + /* Base is the lowest value PC can be. */ + this.base = function() { + return _KernelCPUScheduler.running.base; } - /* - * Even though CPU may be in execution mode - * (or isExecuting is true), the process in running mode - * may not be in executing mode. - */ - this.proccessTerminated = function() { - return this.running == undefined || - this.running.state == PCB_STATE_TERMINATED; + /* Limit is the highest value PC can be. */ + this.limit = function() { + return _KernelCPUScheduler.running.limit; } /* diff --git a/scripts/host/memory.js b/scripts/host/memory.js @@ -56,7 +56,7 @@ function memory(maxBytes) { */ this.getByte = function(location) { if(!this.isValidLocation(location)) - krnTrapError("STACK OVERFLOW"); + krnTrapError(_ERROR_STACK_OVERFLOW); return this.memory[location]; } @@ -65,9 +65,9 @@ function memory(maxBytes) { */ this.setByte = function(byte, location) { if(!isByte(byte)) - krnTrapError("INVALID BYTE VALUE"); + krnTrapError(_ERROR_INVALID_BYTE_VALUE); if(!this.isValidLocation(location)) - krnTrapError("STACK OVERFLOW"); + krnTrapError(_ERROR_STACK_OVERFLOW); this.memory[location] = byte; if(showOuput) this.output(); diff --git a/scripts/os/kernel.js b/scripts/os/kernel.js @@ -27,6 +27,7 @@ function krnBootstrap() // Page 8. _KernelInputQueue = new Queue(); // Where device input lands before being processed out somewhere. _KernelMemoryManager = new MemoryManager(); _KernelReadyQueue = new Queue(); // the ready queue for waiting processes to execute. + _KernelCPUScheduler = new scheduler(); // Initialize standard input and output to the _Console. _StdIn = _Console; @@ -82,10 +83,12 @@ function krnOnCPUClockPulse() var interrput = _KernelInterruptQueue.dequeue(); krnInterruptHandler(interrput.irq, interrput.params); } - // If there are no interrupts then run a CPU cycle if there is anything being processed. - else if (_CPU.isExecuting || _KernelReadyQueue.getSize() > 0) + // If there are no interrupts then run a CPU cycle if there is anything scheduled. + else if (_KernelCPUScheduler.ready()) { - _CPU.cycle(); + MODE = 1; + _KernelCPUScheduler.run(); + MODE = 0; } else // If there are no interrupts and there is nothing being executed then just be idle. { @@ -171,10 +174,10 @@ function krnSystemCall(operator) { // a process has been terminated case 0x00: - krnTrace("Process "+_CPU.running.pid+ + krnTrace("Process "+_KernelCPUScheduler.running.pid+ " terminated successfully."); - _CPU.running.setState(PCB_STATE_TERMINATED); - _CPU.isExecuting = false; + _KernelCPUScheduler. + running.setState(PCB_STATE_TERMINATED); _Console.advanceLine(); _OsShell.putPrompt(); break; @@ -187,17 +190,23 @@ function krnSystemCall(operator) { // print the 00-terminated string stored at // the address in the Y register. else if(_CPU.X == 2) { - var ascii = hex2ASCII(_Memory.getByte(_CPU.Y)); + var ascii = hex2ASCII(_Memory.getByte(_CPU.Y + _KernelCPUScheduler.running.base)); do { _StdOut.putText(ascii); - ascii = hex2ASCII(_Memory.getByte(++_CPU.Y)); + ascii = hex2ASCII(_Memory.getByte(++_CPU.Y + _KernelCPUScheduler.running.base)); } while (ascii != null) } break; - // Error - case -1: - krnTrapError(_ERROR_INVALID_OPERATOR); + // Context Switch + case 0xFFFF: + MODE = 1; + var old = _KernelCPUScheduler.running; + krnTrace("Removing PID "+old.PID+" by context switch."); + old.setState(PCB_STATE_READY); + _KernelReadyQueue.enqueue(old); + _KernelCPUScheduler.running = undefined; + MODE = 0; break; // Invalid system call @@ -232,7 +241,7 @@ function krnTrace(msg) function krnTrapError(msg) { - simLog("OS ERROR - TRAP: " + msg); - _Console.bsod(msg); + simLog("OS ERROR - TRAP: " + msg); krnShutdown(); + _Console.bsod(msg); } diff --git a/scripts/os/memoryManager.js b/scripts/os/memoryManager.js @@ -22,7 +22,7 @@ function MemoryManager() { this.output(); return pid; - }; + } /* * Gets the next available page in memory. Returns -1 if diff --git a/scripts/os/pcb.js b/scripts/os/pcb.js @@ -19,7 +19,7 @@ function pcb(pid, state, base, limit, pc) { this.state = state; this.base = base; this.limit = limit; - this.PC = pc; + this.PC = this.assureValidPC(pc); this.ACC = acc; this.X = x; this.Y = y; @@ -52,4 +52,17 @@ function pcb(pid, state, base, limit, pc) { this.limit, pc, acc, x, y, z); } + + /* + * Enforce the memory boundaries all the time. If it + * crosses its boundaries, it will terminate. + */ + this.assureValidPC = function(pc) { + if(pc >= this.base && pc <= this.limit) + return pc; + else { + this.state = PCB_STATE_TERMINATED; + return this.base; + } + } } diff --git a/scripts/os/scheduler.js b/scripts/os/scheduler.js @@ -0,0 +1,84 @@ +/* + * CPU scheduler module in the client OS using Round Robin + * (RR) scheduling with the user specified + * (or default) quantum. + */ +function scheduler() { + + /* + * Keeps count of the current running process's quantum. + */ + this.quantumCount = 0; + + /* + * Process currently running. + */ + this.running = undefined; + + this.run = function() { + // if process is not running, and ready queue is empty + // don't do anything + if(this.processTerminated() && _KernelReadyQueue.isEmpty()) { + return; + } + + // if ready queue is not empty, dequeue + if(this.processTerminated()) { + + var p = _KernelReadyQueue.dequeue(); + while(p.state == PCB_STATE_TERMINATED) + p = _KernelReadyQueue.dequeue(); + this.setRunningProcess(p); + krnTrace("Adding PID "+p.PID+" by context switch."); + } + + _CPU.cycle(); + + + // update current changes in the PCB registry + this.running.updateRegisters( + _CPU.PC, _CPU.ACC, _CPU.X, _CPU.Y, _CPU.Z); + + this.updateQuantumCount(); + } + + /* + * Sets a process as the currently executing process + * transfers PCB registers to the CPU. + */ + this.setRunningProcess = function(process) { + this.running = process; + process.setState(PCB_STATE_RUNNING); + _CPU.PC = process.PC; + _CPU.ACC = process.ACC; + _CPU.X = process.X; + _CPU.Y = process.Y; + _CPU.Z = process.Z; + } + + /* + * Checks if the running process is done or empty. + */ + this.processTerminated = function() { + return this.running == undefined || + this.running.state == PCB_STATE_TERMINATED; + } + + /* + * Whether the scheduler is ready to run. + */ + this.ready = function() { + return (!this.processTerminated() || _KernelReadyQueue.getSize() > 0); + } + + /* + * Updates quantum count + */ + this.updateQuantumCount = function() { + if(++this.quantumCount % RR_QUANTUM == 0) { + this.quantumCount = 0; + krnInterrupt(SYSCALL_IRQ, 0xFFFF); + } + } + +} diff --git a/scripts/os/shell.js b/scripts/os/shell.js @@ -124,8 +124,40 @@ function shellInit() sc.function = shellRun; this.commandList[this.commandList.length] = sc; - // processes - list the running processes and their IDs - // kill <id> - kills the specified process id. + // runall + sc = new ShellCommand(); + sc.command = "runall"; + sc.description = "- Runs all resident processes."; + sc.function = shellRunAll; + this.commandList[this.commandList.length] = sc; + + // reset + sc = new ShellCommand(); + sc.command = "reset"; + sc.description = "- Resets the OS."; + sc.function = shellReset; + this.commandList[this.commandList.length] = sc; + + // rrquantum + sc = new ShellCommand(); + sc.command = "rrquantum"; + sc.description = "- Sets the Round-Robin Quantum."; + sc.function = shellRRQuantum; + this.commandList[this.commandList.length] = sc; + + // processes + sc = new ShellCommand(); + sc.command = "processes"; + sc.description = "- Shows all active processes' ID."; + sc.function = shellProcesses; + this.commandList[this.commandList.length] = sc; + + // kill + sc = new ShellCommand(); + sc.command = "kill"; + sc.description = "- Kills the specified process ID."; + sc.function = shellKill; + this.commandList[this.commandList.length] = sc; // // Display the initial prompt. @@ -426,8 +458,7 @@ function shellWhereami(args) { }.bind(this), function () {}.bind(this)); //failure } else { - _StdIn.putText("Cybertron. WIll self-destruct in 3... 2... 1..."); - setTimeout(function(){_Console.bsod("Self Destructed.")},3500); + _StdIn.putText("Geolocation not found."); } return false; } @@ -501,3 +532,77 @@ function shellRun(args) { } } } + +/* + * Runs all resident processes. + */ +function shellRunAll(args) { + var ps = _KernelMemoryManager.processes; + var ran = 0; + for(var i=0; i<ps.length; i++) { + if(ps[i].state == PCB_STATE_RESIDENT) { + _KernelReadyQueue.enqueue(ps[i]); + ps[i].setState(PCB_STATE_READY); + ran++; + } + } + _StdOut.putText("Running "+ran+" processes in total."); +} + +/* + * Resets the entire operating system. + */ +function shellReset(args) { + simBtnReset_click(null); +} + +/* + * Sets the Round-Robin quantum. + */ +function shellRRQuantum(args) { + var newQuantum = args[0].trim(); + console.log(newQuantum); + if(newQuantum == undefined) + _StdOut.putText("Usage: rrquantum <quantum number>"); + else if(!isInt(newQuantum)) + _StdOut.putText("Invalid quantum value."); + else { + RR_QUANTUM = parseInt(newQuantum); + _StdOut.putText("Round-Robin Quantum set to "+RR_QUANTUM); + } +} + +/* + * Shows all active processes' ID. + */ +function shellProcesses(args) { + var ps = _KernelMemoryManager.processes; + _StdOut.putText("Total Active PIDs: "+ps.length); + _Console.advanceLine(); + for(var i=0; i<ps.length; i++) { + _StdOut.putText(ps[i].PID+" - "+ps[i].state); + _Console.advanceLine(); + } +} + +/* + * Kills an active process in ready, resident + * or running mode. + */ +function shellKill(args) { + var pid = args[0]; + if(pid == undefined) { + _StdIn.putText('Usage: kill <pid>'); + } else { + var process = _KernelMemoryManager.processes[pid]; + if(process == undefined) { + _StdOut.putText("No such process."); + } else { + if(process.state == PCB_STATE_TERMINATED) + _StdOut.putText("Process is already terminated."); + else { + process.setState(PCB_STATE_TERMINATED); + } + } + } +} diff --git a/scripts/utils.js b/scripts/utils.js @@ -78,5 +78,10 @@ function hex2ASCII(num) { return str; } - - +/* + * Checks if the value is an integer. + */ +function isInt(value){ + return ((parseFloat(value) == parseInt(value)) + && !isNaN(value)) +}