<os.java
>=
/**
A simple operating system without virtual storage and with almost no
scheduling.
*/
import java.util.Arrays
class os
implements OperatingSystem
<Handle a system call>
<Handle an interrupt>
/**
Create an instance of this operating system.
hw: The hardware on which this os will be running.
*/
public
os(Hardware hw)
this.hw = hw
idleProcess =
new ProcessDescriptor(
Hardware.Address.idleStart, Hardware.Address.idleEnd, hw)
/**
Run a process loaded into primary store.
process: The process to run.
*/
private void
runProcess(ProcessDescriptor process)
process.restoreRegisters()
<The disk-index interrupt handler>
<The program-load interrupt handler>
<Operating system instance variables>
/**
A reference to the hardware on which this os runs.
*/
private final Hardware hw
Definesos
,os.java
(links are to index).
Interrupt Handling
Figure out what the interrupt is, and handle it.
<Handle an interrupt>= (<-U) /** Handle an interrupt. it: The interrupt to handle. */ public void interrupt(Hardware.Interrupt it) switch it <Handle disk interrupt> <Handle reboot interrupt> case systemCall doSystemCall(hw.fetch(0)) break default SystemSim.panic("Unhandled " + it + " interrupt!")
Definesinterrupt
(links are to index).
Implementing System Calls
Figure out what the system call is, and implement it.
<Handle a system call>= (<-U) /** Handle a system-call interrupt sc: The system-call identifier. */ private void doSystemCall(int sc) switch sc case OperatingSystem.SystemCall.exit <Handle an exit system call> break default SystemSim.panic( "Oh no! " + OperatingSystem.SystemCall.toString(sc) + " (= " + sc + ") is an unhandled system call!")
DefinesdoSystemCall
(links are to index).
Stopping the system on an exit
system call is absolutely the wrong thing
to do, but it works for the first set of batch disks, so go with it for now.
<Handle an exit system call>= (<-U) hw.store(Hardware.Address.haltRegister, 0)
The second reason can be further divided into
- The disk-index block has been read.
- A block from the first program to run has been read.
- A program block that's not the last block needed has been read.
- The last program block needed has been read.
In addition, thinking ahead, later programming assignments are going to add more reasons for causing disk interrupts.
Each of these reasons requires interrupt handling different from the interrupt handling required by the other reasons. Without careful management, keeping track of the differences could get messy fast.
Fortunately, object-oriented design provides a technique for managing the different ways of handling disk interrupts: define an abstract class encapsulating the common activities needed by all disk-interrupt handlers. Each particular disk-interrupt handler establishes its particular requirements by implementing concrete children of the disk-interrupt handler abstract class. For this assignment, only two children are needed to handle block-index interrupts and interrupts resulting from program loads.
<Disk-interrupt handler abstract class>= (U->) abstract class DiskInterruptHandler /** The method called to handle an interrupt. */ abstract void handleInterrupt() <Disk-interrupt handler common methods> <Disk-interrupt handler instance variables>
DefinesDiskInterruptHandler
(links are to index).
A particular disk-interrupt handler defines a child of
DiskInterruptHandler
, filling in the handleInterrupt()
method with the
code needed to handle the specific disk interrupt.
DiskInterruptHandler
abstract class, the operating system defines
an instance variable of type DiskInterruptHandler
to represent the action
to be taken when the next disk interrupt arrives.
<Operating system instance variables>= (<-U) [D->] /** How to handle the next disk interrupt. */ private DiskInterruptHandler diskInterruptHandler /** The user process of last resort. */ private final ProcessDescriptor idleProcess
DefinesdiskInterruptHandler
,idleProcess
(links are to index).
When a disk interrupt arrives, the operating system calls handleInterrupt()
on the currently defined disk-interrupt handler to deal with it.
<Handle disk interrupt>= (<-U) case disk: diskInterruptHandler.handleInterrupt() break
DefinesdiskInterruptHandler
(links are to index).
In addition to defining the abstract method handleInterrupt()
, the
disk-interrupt abstract class also defines a bunch of common disk-manipulation
routines useful to disk-interrupt-handling child classes.
<Disk-interrupt handler common methods>= (<-U) /** Check the status of the most recent disk command, dying if it's not ok. */ protected void checkDiskStatus() final int r = primaryStore.fetch(Hardware.Address.diskStatusRegister) if Hardware.Status.ok ≠ r SystemSim.panic( "disk operation status is " + Hardware.Status.toString(r)) /** Read a disk block into primary store. */ protected void diskBlockLoad() assert blockCount > 0 primaryStore.store( Hardware.Address.diskBlockRegister, firstBlock + --blockCount) primaryStore.store( Hardware.Address.diskAddressRegister, startAddress + Hardware.Disk.blockSize*blockCount) primaryStore.store( Hardware.Address.diskCommandRegister, Hardware.Disk.readCommand) /** Create an instance of a disk interrupt handler. ps: A reference to primary storage. fbi: The index of the first block in the program. sa: The primary-store address at which the program will be loaded. bc: The program size in disk blocks. */ protected DiskInterruptHandler(PrimaryStore ps, int fbi, int sa, int bc) primaryStore = ps firstBlock = fbi startAddress = sa blockCount = bc /** Determine if this program load is done. Returns: True iff there are no more blocks to load. */ protected boolean loadComplete() return blockCount ≡ 0
DefinescheckDiskStatus
,diskBlockLoad
,loadComplete
(links are to index).
The state needed by every DiskInterruptHandler
child class.
<Disk-interrupt handler instance variables>= (<-U) /** The number of blocks read from the disk. */ private int blockCount /** The index of the program's first block. */ private final int firstBlock /** A reference to the hardware's primary store. */ protected final PrimaryStore primaryStore /** The primary-store address into which the first word of the disk block will be read. */ protected final int startAddress
DefinesblockCount
,firstBlock
,startAddress
(links are to index).
And finally, the file containing the DiskInterruptHandler
abstract class.
<DiskInterruptHandler.java
>=
/**
An abstract disk-interrupt handler.
*/
<Disk-interrupt handler abstract class>
DefinesDiskInterruptHandler
,DiskInterruptHandler.java
(links are to index).
Index-Block Disk-Interrupt Handling
The first disk interrupt that occurs after reboot results from reading the
batch disk's index block. The DiskIndexInterruptHandler
reads the
disk-index block and turns it into a data-structure the rest of the operating
system can use.
<The disk-index interrupt handler>= (<-U) /** Handle the interrupt that comes back from the disk-read request for the disk-index block. */ private class DiskIndexInterruptHandler extends DiskInterruptHandler /** Create a handler to read the index block from the disk. ps: A reference to primary store. */ DiskIndexInterruptHandler(PrimaryStore ps) super(ps, 0, Hardware.Address.userBase, 1) /** Handle the interrupt marking the end of the index-block read. */ public void handleInterrupt() checkDiskStatus() // Copy the non-zero entries in the index block into an array of // size-in-block values for programs. programBlockCount = new int [Hardware.Disk.blockSize] for int i = 0; i < Hardware.Disk.blockSize; i++ final int r = primaryStore.fetch(Hardware.Address.userBase + i) if r < 0 SystemSim.panic("disk index has a negative block count.") if r ≥ Hardware.Disk.blockCount SystemSim.panic( "disk index has a block count greater than the disk size.\n" + "disk index " + i + " is " + r + ".") if r ≡ 0 programBlockCount = Arrays.copyOf(programBlockCount, i) break programBlockCount[i] = r // If there are no programs, quit. if programBlockCount.length ≡ 0 primaryStore.store(Hardware.Address.haltRegister, 0) // Compute the first-block indices for the programs on the disk. programFirstBlock = new int [programBlockCount.length] programFirstBlock[0] = 1 for int i = 0; i < programBlockCount.length - 1; i++ programFirstBlock[i + 1] = programFirstBlock[i] + programBlockCount[i] // Issue a load request for the first program. diskInterruptHandler = new ProgramLoadInterruptHandler(primaryStore, 0, startAddress) diskInterruptHandler.diskBlockLoad()
DefinesDiskIndexInterruptHandler
(links are to index).
The disk index is made available to the rest of the operating system in an unsophisticated data structure: a pair of private instance-variable arrays giving the programs' start location and size. It should probably be gussied up with at least a class, but for this assignment even a pair of arrays isn't really necessary.
<Operating system instance variables>+= (<-U) [<-D] /** An array of program sizes. The ith element in the array gives the size in disk blocks of the i>th program on the disk. */ private int programBlockCount [] /** An array of program locations. The ith element in the array gives the location on disk of the ith program. */ private int programFirstBlock []
DefinesprogramBlockCount
,programFirstBlock
(links are to index).
When the reboot interrupt arrives, the operating system handles it by creating and then kicking-off a handler to read the program-index block off the batch disk.
<Handle reboot interrupt>= (<-U) case reboot: diskInterruptHandler = new DiskIndexInterruptHandler(hw) diskInterruptHandler.diskBlockLoad() idleProcess.restoreRegisters() break
The second source of disk interrupts in the first assignment is programs being
loaded as the result of exec
system calls. Loading a program requires
reading a sequence of disk blocks into user space and executing the associated
process once the program's been loaded. The ProgramLoadInterruptHandler
child concrete class of the DiskIndexInterruptHandler
abstract class is
responsible for loading a program and then executing the resulting process.
ProgramLoadInterruptHandler
's implementation leans heavily on the property
that the first set of batch disk only ever load one program. This property is
not true for subsequent batch disks, which may load several programs at the
same time. The code shown here will not handle concurrent program loads and
will have to be rewritten, but for now it's good enough.
<The program-load interrupt handler>= (<-U) /** Handle a disk interrupt from a program load. */ private class ProgramLoadInterruptHandler extends DiskInterruptHandler /** Handle a disk interrupt from a request to load a program block. */ public void handleInterrupt() checkDiskStatus() if loadComplete() // The last program block just got read. Turn the program into a // process and run it. runProcess( new ProcessDescriptor( startAddress, startAddress + Hardware.Disk.blockSize*programBlockCount[programIndex], primaryStore)) else // Read in the program's next block. diskBlockLoad() /** Create a new program-load interrupt handler. ps: A reference to the hardware. pi: The index of the program to load. sa: The starting (lowest, leftmost) primary-store address of the loaded program. requester: The process making the load request. */ ProgramLoadInterruptHandler( PrimaryStore ps, int pi, int sa) super(ps, programFirstBlock[pi], sa, programBlockCount[pi]) programIndex = pi /** The disk-index block index of the program being loaded. */ private final int programIndex
DefinesProgramLoadInterruptHandler
(links are to index).
Process Descriptors
Process descriptors aren't needed for the first assignment. Having a
process-descriptor class available for use makes the code neater and sets the
stage for later assignments, when process descriptors are necessary.
<ProcessDescriptor.java
>=
/**
Describe a process's resources and state.
*/
class ProcessDescriptor
/**
Represent a process.
start: The process's lowest (leftmost) address.
end: One past the process's highest (rightmost) address.
ps: A reference to primary store.
*/
ProcessDescriptor(int start, int end, PrimaryStore ps)
if start ≥ end
SystemSim.panic(
"Program segment has start (" + start + ") ≥ end (" + end + ").")
registers[Hardware.Address.baseRegister] = start
registers[Hardware.Address.topRegister] = end
registers[Hardware.Address.PCRegister] = start
primaryStore = ps
/**
Move the process's register set to the hardware registers.
*/
void
restoreRegisters()
for int i = 0; i < Hardware.Address.registerSetSize; i++
primaryStore.store(i, registers[i])
/**
A place to save registers.
*/
private final int registers[] = new int [Hardware.Address.registerSetSize]
/**
A reference to primary store.
*/
private final PrimaryStore primaryStore
DefinesProcessDescriptor
,ProcessDescriptor.java
(links are to index).
DiskInterruptHandler.java
>: D1
os.java
>: D1
ProcessDescriptor.java
>: D1