├── .classpath ├── .gitignore ├── .project ├── DISK ├── README.md ├── build.xml ├── mytest.script ├── nbproject ├── build-impl.xml ├── genfiles.properties ├── project.properties └── project.xml ├── src ├── main │ └── Main.java └── minikernel │ ├── Boot.java │ ├── Disk.java │ ├── FastDisk.java │ ├── FileSys.java │ ├── FileTester.java │ ├── Kernel.java │ ├── Library.java │ ├── Makefile.sdx │ └── MyShell.java └── test1.script /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /nbproject/private/ 3 | /build/ 4 | /dist/ -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JavaOS 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /DISK: -------------------------------------------------------------------------------- 1 | 1bar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JavaOS 2 | ====== 3 | 4 | This is a simulation of an operating system in Java. It is based on the paper at http://dl.acm.org/citation.cfm?id=1805734. It is meant to be educational, allowing students to implement core components of an operating system in Java, such as the shell, scheduling algorithms, or a file system, as a supplement to their learning of operating systems concepts. Starter code is located at http://pages.cs.wisc.edu/~solomon/cs537/project5/. 5 | 6 | Importing Into Netbeans 7 | ----- 8 | 1. Register for GitHub. 9 | 2. Download Netbeans. 10 | 3. Open Netbeans. At top, click Team -> Git -> Clone... 11 | 4. Enter https://github.com/badjr/JavaOS.git for repository URL. 12 | 5. Enter GitHub username and password, click next. 13 | 6. Select master, click next. 14 | 7. Click Finish 15 | 16 | Pull Changes 17 | ----- 18 | (***Important***) Before each coding session, pull changes to get the latest updates others have made to the project. 19 | 20 | 1. Right click on the project folder, select Git -> Remote -> Pull 21 | 2. "Select Configured Git Repository Location" should already have the URL in. 22 | i. If not, select "Specify Git Repository Location" and enter https://github.com/badjr/JavaOS.git for repository URL. 23 | ii. Enter GitHub username and password. 24 | 3. Click next, select master, and click finish. 25 | 26 | Commit and Push Changes 27 | ----- 28 | (***Important***) After each coding session, commit and then push your changes to GitHub. 29 | 30 | 1. Right click on the project folder, select Git -> Commit... 31 | 2. The files you changed should be selected. Click "Commit". 32 | If the "Commit" button isn't available, enter a commit message and that should enable it. 33 | 3. Right click on the project folder, select Git -> Remote -> Push... 34 | 4. "Select Configured Git Repository Location" should already have the URL in. 35 | i. If not, select "Specify Git Repository Location" and enter https://github.com/badjr/JavaOS.git for repository URL. 36 | ii. Enter GitHub username and password. 37 | 5. Click next, select master, click next. 38 | 6. Select master, and click finish. 39 | 40 | *Note*: Must commit before pushing, otherwise the project doesn't get updated. 41 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project JavaOS. 12 | 13 | 73 | -------------------------------------------------------------------------------- /mytest.script: -------------------------------------------------------------------------------- 1 | //************** simple test: format, create, read, write, delete, readdir 2 | /*format 3 | /*create foo 4 | /*read foo 5 | /*write 6 | /*delete 7 | /*readdir 8 | /*quit -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=14e8424e 2 | build.xml.script.CRC32=62f6829b 3 | build.xml.stylesheet.CRC32=8064a381@1.74.1.48 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=14e8424e 7 | nbproject/build-impl.xml.script.CRC32=5edde8cb 8 | nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 6 | application.title=JavaOS 7 | application.vendor=Brett 8 | auxiliary.org-netbeans-modules-projectimport-eclipse-core.key=src=src;output=bin; 9 | auxiliary.org-netbeans-modules-projectimport-eclipse-core.project=. 10 | auxiliary.org-netbeans-modules-projectimport-eclipse-core.timestamp=1395304167805 11 | build.classes.dir=${build.dir}/classes 12 | build.classes.excludes=**/*.java,**/*.form 13 | # This directory is removed when the project is cleaned: 14 | build.dir=build 15 | build.generated.dir=${build.dir}/generated 16 | build.generated.sources.dir=${build.dir}/generated-sources 17 | # Only compile against the classpath explicitly listed here: 18 | build.sysclasspath=ignore 19 | build.test.classes.dir=${build.dir}/test/classes 20 | build.test.results.dir=${build.dir}/test/results 21 | # Uncomment to specify the preferred debugger connection transport: 22 | #debug.transport=dt_socket 23 | debug.classpath=\ 24 | ${run.classpath} 25 | debug.test.classpath=\ 26 | ${run.test.classpath} 27 | # This directory is removed when the project is cleaned: 28 | dist.dir=dist 29 | dist.jar=${dist.dir}/JavaOS.jar 30 | dist.javadoc.dir=${dist.dir}/javadoc 31 | endorsed.classpath= 32 | excludes= 33 | file.reference.JavaOS-src=src 34 | includes=** 35 | jar.compress=false 36 | javac.classpath= 37 | # Space-separated list of extra javac options 38 | javac.compilerargs= 39 | javac.deprecation=false 40 | javac.processorpath=\ 41 | ${javac.classpath} 42 | javac.source=1.8 43 | javac.target=1.8 44 | javac.test.classpath=\ 45 | ${javac.classpath}:\ 46 | ${build.classes.dir} 47 | javac.test.processorpath=\ 48 | ${javac.test.classpath} 49 | javadoc.additionalparam= 50 | javadoc.author=false 51 | javadoc.encoding=${source.encoding} 52 | javadoc.noindex=false 53 | javadoc.nonavbar=false 54 | javadoc.notree=false 55 | javadoc.private=false 56 | javadoc.splitindex=true 57 | javadoc.use=true 58 | javadoc.version=false 59 | javadoc.windowtitle= 60 | main.class=minikernel.FileTester 61 | meta.inf.dir=${src.dir}/META-INF 62 | mkdist.disabled=true 63 | platform.active=default_platform 64 | run.classpath=\ 65 | ${javac.classpath}:\ 66 | ${build.classes.dir} 67 | # Space-separated list of JVM arguments used when running the project. 68 | # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. 69 | # To set system properties for unit tests define test-sys-prop.name=value: 70 | run.jvmargs= 71 | run.test.classpath=\ 72 | ${javac.test.classpath}:\ 73 | ${build.test.classes.dir} 74 | source.encoding=UTF-8 75 | src.dir=${file.reference.JavaOS-src} 76 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | JavaOS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on code acquired from http://webhost.bridgew.edu/sattar/os.zip. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package main; 7 | 8 | import java.util.Scanner; 9 | import minikernel.FastDisk; 10 | 11 | /** 12 | * Implementation of the shell based on steps provided from 13 | * http://webhost.bridgew.edu/sattar/os.zip/hw1-shell. 14 | * 15 | * @author Brett 16 | */ 17 | public class Main { 18 | 19 | public static FastDisk disk; 20 | 21 | public static void main(String[] args) { 22 | 23 | //Outline of your main method: 24 | 25 | /* Loop through commands on command line */ 26 | try { 27 | while (true) { 28 | /* Display your Shell cursor */ 29 | System.out.print("> "); 30 | 31 | /* Get new command line input */ 32 | Scanner s = new Scanner(System.in); 33 | String input = s.nextLine(); 34 | 35 | /* Break up commands and store them in an array using '&' as delimeter */ 36 | String commands[] = input.split("&"); 37 | 38 | /* Create a thread array for each command detected */ 39 | CommandExecutorThread commandExecutorThread[] = new CommandExecutorThread[commands.length]; 40 | 41 | /* Loop to allow a new thread per command to be created */ 42 | // for (String command : commands) { 43 | for (int i = 0; i < commands.length; i++) { 44 | commandExecutorThread[i] = new CommandExecutorThread(i, commands[i].trim()); 45 | commandExecutorThread[i].start(); 46 | } 47 | 48 | /* If it is EXIT command then close your Shell by executing System.exit(0)*/ 49 | 50 | /* Add new thread to the thread array and run it */ 51 | /* Join the threads */ 52 | for (int i = 0; i < commandExecutorThread.length; i++) { 53 | commandExecutorThread[i].join(); 54 | } 55 | } //while 56 | 57 | } /* Check for a keyboard interupt or other non-serious system problem */ 58 | 59 | catch (Exception e) { 60 | System.out.println("\n\nInterrupt was detected. my Shell is closing."); 61 | // e.printStackTrace(); 62 | System.exit(0); 63 | } 64 | 65 | } 66 | 67 | } 68 | 69 | class CommandExecutorThread extends Thread { 70 | 71 | private int myID = 0; 72 | private String command; 73 | 74 | public CommandExecutorThread(int myID, String command) { 75 | this.myID = myID; 76 | this.command = command; 77 | } 78 | 79 | public void run() { 80 | System.out.println("myID = " + myID + ". Running command " + command); 81 | 82 | switch (command) { 83 | case "format": 84 | break; 85 | } 86 | 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/minikernel/Boot.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Used by Brett Duncan for the operating systems project. 4 | */ 5 | package minikernel; 6 | 7 | /* $Id: Boot.java,v 1.18 2006/11/09 20:42:29 solomon Exp $ */ 8 | 9 | import java.lang.reflect.*; 10 | import java.util.*; 11 | import static java.lang.System.*; 12 | 13 | /** A bootstrap program for the MiniKernel. 14 | *

15 | * This program creates a Disk and launches the kernel by calling the POWER_ON 16 | * interrupt. When the Kernel returns from the interrupt, we assume it wants 17 | * to shut down. 18 | *

19 | * The program expects four or more command-line arguments: 20 | *

28 | *

29 | * An example invocation is 30 | *

 31 |  *    java Boot 10 Disk 100 Shell
 32 |  * 
33 | * 34 | * @see Kernel 35 | * @see Disk 36 | */ 37 | public class Boot { 38 | /** No public instances. */ 39 | private Boot() {} 40 | 41 | /** Prints a help message and exits. */ 42 | private static void usage() { 43 | err.println("usage: java Boot" 44 | + " " 45 | + " [ ... ]"); 46 | exit(-1); 47 | } // usage 48 | 49 | /** The main program. 50 | * @param args the command-line arguments 51 | */ 52 | public static void main(String args[]) { 53 | if (args.length < 4) { 54 | usage(); 55 | } 56 | 57 | int cacheSize = Integer.parseInt(args[0]); 58 | String diskName = args[1]; 59 | int diskSize = Integer.parseInt(args[2]); 60 | StringBuffer shellCommand = new StringBuffer(args[3]); 61 | for (int i = 4; i < args.length; i++) { 62 | shellCommand.append(" ").append(args[i]); 63 | } 64 | 65 | // Create a Disk drive and start it spinning 66 | Object disk = null; 67 | try { 68 | Class diskClass = Class.forName(diskName); 69 | Constructor ctor 70 | = diskClass.getConstructor(new Class[] { Integer.TYPE }); 71 | disk = ctor.newInstance(new Object[] { new Integer(diskSize) }); 72 | if (! (disk instanceof Disk)) { 73 | err.printf("%s is not a subclass of Disk\n", diskName); 74 | usage(); 75 | } 76 | if (!diskName.equals("FastDisk")) { 77 | new Thread((Disk) disk, "DISK").start(); 78 | } 79 | } catch (ClassNotFoundException e) { 80 | err.printf("%s: class not found\n", diskName); 81 | usage(); 82 | } catch (NoSuchMethodException e) { 83 | err.printf("%s(int): no such constructor\n", diskName); 84 | usage(); 85 | } catch (InvocationTargetException e) { 86 | err.printf("%s: %s\n", diskName, e.getTargetException()); 87 | usage(); 88 | } catch (Exception e) { 89 | err.printf("%s: %s\n", diskName, e); 90 | usage(); 91 | } 92 | out.println("Boot: Starting kernel."); 93 | 94 | Kernel.interrupt(Kernel.INTERRUPT_POWER_ON, 95 | cacheSize, 0, disk, shellCommand.toString(), null); 96 | 97 | out.println("Boot: Kernel has stopped."); 98 | exit(0); 99 | } // main 100 | } // Boot 101 | -------------------------------------------------------------------------------- /src/minikernel/Disk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: Disk.java,v 1.17 2006/11/09 20:42:29 solomon Exp $ */ 9 | 10 | import java.io.*; 11 | import static java.lang.System.*; 12 | 13 | /** A software simulation of a Disk. 14 | *

15 | * You may not change this class. 16 | *

17 | * This disk is slow and ornery. 18 | * It contains a number of blocks, all BLOCK_SIZE bytes long. 19 | * All operations occur on individual blocks. 20 | * You can't modify any more or any less data at a time. 21 | *

22 | * To read or write from the disk, call beginRead() or beginWrite(). 23 | * Each of these functions will start the action and return immediately. 24 | * When the action has been completed, the Disk calls Kernel.interrupt() 25 | * to let you know the Disk is ready for more. 26 | *

27 | * It may take a while for the disk to seek from one block to another. 28 | * Seek time is proportional to the difference in block numbers of the 29 | * blocks. 30 | *

31 | * Warning: Don't call beginRead() or beginWrite() while the 32 | * disk is busy! If you don't treat 33 | * the Disk gently, the system will crash! (Just like a real machine!) 34 | *

35 | * This disk saves its contents in the Unix file DISK between runs. 36 | * Since the file can be large, you should get in the habit of removing it 37 | * before logging off. 38 | * 39 | * @see Kernel 40 | */ 41 | public class Disk implements Runnable { 42 | /////////////////////////////////////////// Disk geometry parameters 43 | 44 | /** The size of a disk block in bytes. */ 45 | public static final int BLOCK_SIZE = 512; 46 | 47 | /** Total size of this disk, in blocks. */ 48 | public final int DISK_SIZE; 49 | 50 | /////////////////////////////////////////// Transient internal state 51 | 52 | /** Current location of the read/write head. */ 53 | protected int currentBlock = 0; 54 | 55 | /** The data stored on the disk. */ 56 | protected byte[] data; 57 | 58 | /** An indication of whether an I/O operation is currently in progress. */ 59 | protected boolean busy; 60 | 61 | /** An indication whether the current I/O operation is a write operation. 62 | * Only meaningful if busy == true. 63 | */ 64 | private boolean isWriting; 65 | 66 | /** The block number to be read/written by the current operation. 67 | * Only meaningful if busy == true. 68 | */ 69 | protected int targetBlock; 70 | 71 | /** Memory buffer to/from which current I/O operation is transferring. 72 | * Only meaningful if busy == true. 73 | */ 74 | private byte[] buffer; 75 | 76 | /** A flag set by beginRead or beginWrite to indicate that a request 77 | * has been submitted. 78 | */ 79 | private boolean requestQueued = false; 80 | 81 | /** A count of read operations performed, for statistics. */ 82 | protected int readCount; 83 | 84 | /** A count of write operations performed, for statistics. */ 85 | protected int writeCount; 86 | 87 | /////////////////////////////////////////// Inner classes 88 | 89 | /** The exception thrown when an illegal operation is attempted on the 90 | * disk. 91 | */ 92 | static protected class DiskException extends RuntimeException { 93 | static final long serialVersionUID = 0; 94 | /** Creates a new exception. 95 | * 96 | * @param s the detail string for the exception. 97 | */ 98 | public DiskException(String s) { 99 | super("*** YOU CRASHED THE DISK: " + s); 100 | } 101 | } 102 | 103 | /////////////////////////////////////////// Constructors 104 | 105 | /** Creates a new Disk. 106 | * If a Unix file named DISK exists in the local Unix directory, the 107 | * simulated disk contents are initialized from the Unix file. 108 | * It is an error if the DISK file exists but its size does not match 109 | * "size". 110 | * If there is no DISK file, the first block of the simulated disk is 111 | * cleared to nulls and the rest is filled with random junk. 112 | * 113 | * @param size the total size of this disk, in blocks. 114 | */ 115 | public Disk(int size) { 116 | File diskName = new File("DISK"); 117 | if (diskName.exists()) { 118 | if (diskName.length() != size * BLOCK_SIZE) { 119 | throw new DiskException( 120 | "File DISK exists but is the wrong size"); 121 | } 122 | } 123 | this.DISK_SIZE = size; 124 | if (size < 1) { 125 | throw new DiskException("A disk must have at least one block!"); 126 | } 127 | // NOTE: the "new" operator always clears the result object to nulls 128 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 129 | int count = BLOCK_SIZE; 130 | try { 131 | FileInputStream is = new FileInputStream("DISK"); 132 | is.read(data); 133 | out.printf("Restored %d bytes from file DISK\n", count); 134 | is.close(); 135 | return; 136 | } catch (FileNotFoundException e) { 137 | out.println("Creating new disk"); 138 | this.format(); 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | exit(1); 142 | } 143 | //*****************Commented out by Brett Duncan 144 | // byte[] junk = new byte[BLOCK_SIZE]; 145 | // for (int i = 0; i < BLOCK_SIZE; ) { 146 | // junk[i++] = 74; 147 | // junk[i++] = 85; 148 | // junk[i++] = 78; 149 | // junk[i++] = 75; 150 | // } 151 | // for (int i = 1; i < DISK_SIZE; i++) { 152 | // arraycopy( 153 | // junk, 0, 154 | // data, i * BLOCK_SIZE, 155 | // BLOCK_SIZE); 156 | // } 157 | } // Disk(int) 158 | 159 | /////////////////////////////////////////// Methods 160 | 161 | /** 162 | * Formats the disk. 163 | */ 164 | public void format() { 165 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 166 | //*****************Commented out by Brett Duncan 167 | // byte[] junk = new byte[BLOCK_SIZE]; 168 | // for (int i = 0; i < BLOCK_SIZE; ) { 169 | // junk[i++] = 74; 170 | // junk[i++] = 85; 171 | // junk[i++] = 78; 172 | // junk[i++] = 75; 173 | // } 174 | // for (int i = 1; i < DISK_SIZE; i++) { 175 | // arraycopy( 176 | // junk, 0, 177 | // data, i * BLOCK_SIZE, 178 | // BLOCK_SIZE); 179 | // } 180 | } 181 | 182 | /** Saves the contents of this Disk. 183 | * The contents of this disk will be forced out to a file named 184 | * DISK so that they can be restored on the next run of this program. 185 | * This file could be quite big, so delete it before you log out. 186 | * Also prints some statistics on disk operations. 187 | */ 188 | public void flush() { 189 | try { 190 | out.println("Saving contents to DISK file..."); 191 | FileOutputStream os = new FileOutputStream("DISK"); 192 | os.write(data); 193 | os.close(); 194 | out.printf( 195 | "%d read operations and %d write operations performed\n", 196 | readCount, writeCount); 197 | } catch(Exception e) { 198 | e.printStackTrace(); 199 | exit(1); 200 | } 201 | } // flush() 202 | 203 | /** Sleeps for a while to simulate the delay in seeking and transferring 204 | * data. 205 | * @param targetBlock the block number to which we have to seek. 206 | */ 207 | protected void delay(int targetBlock) { 208 | int sleepTime = 10 + Math.abs(targetBlock - currentBlock) / 5; 209 | try { 210 | Thread.sleep(sleepTime); 211 | } catch (Exception e) { 212 | e.printStackTrace(); 213 | } 214 | } // delay(int) 215 | 216 | /** Starts a new read operation. 217 | * @param blockNumber The block number to read from. 218 | * @param buffer A data area to hold the data read. This array must be 219 | * allocated by the caller and have length of at least 220 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 221 | * bytes of the array will be modified. 222 | */ 223 | public synchronized void beginRead(int blockNumber, byte[] buffer) { 224 | if ( 225 | blockNumber < 0 226 | || blockNumber >= DISK_SIZE 227 | || buffer == null 228 | || buffer.length < BLOCK_SIZE) 229 | { 230 | throw new DiskException("Illegal disk read request: " 231 | + " block number " + blockNumber 232 | + " buffer " + buffer); 233 | } 234 | 235 | if (busy) { 236 | throw new DiskException("Disk read attempted " 237 | + " while the disk was still busy."); 238 | } 239 | 240 | isWriting = false; 241 | this.buffer = buffer; 242 | targetBlock = blockNumber; 243 | requestQueued = true; 244 | 245 | notify(); 246 | } // beginRead(int, byte[]) 247 | 248 | /** Starts a new write operation. 249 | * @param blockNumber The block number to write to. 250 | * @param buffer A data area containing the data to be written. This array 251 | * must be allocated by the caller and have length of at least 252 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 253 | * bytes of the array will be sent to the disk. 254 | */ 255 | public synchronized void beginWrite(int blockNumber, byte[] buffer) { 256 | if ( 257 | blockNumber < 0 258 | || blockNumber >= DISK_SIZE 259 | || buffer == null 260 | || buffer.length < BLOCK_SIZE) 261 | { 262 | throw new DiskException("Illegal disk write request: " 263 | + " block number " + blockNumber 264 | + " buffer " + buffer); 265 | } 266 | 267 | if (busy) { 268 | throw new DiskException("Disk write attempted " 269 | + " while the disk was still busy."); 270 | } 271 | 272 | isWriting = true; 273 | this.buffer = buffer; 274 | targetBlock = blockNumber; 275 | requestQueued = true; 276 | 277 | notify(); 278 | } // beginWrite(int, byte[]) 279 | 280 | /** Waits for a call to beginRead or beginWrite. */ 281 | protected synchronized void waitForRequest() { 282 | while(!requestQueued) { 283 | try { 284 | wait(); 285 | } catch (Exception e) { 286 | e.printStackTrace(); 287 | } 288 | } 289 | requestQueued = false; 290 | busy = true; 291 | } // waitForRequest() 292 | 293 | /** Indicates to the CPU that the current operation has completed. */ 294 | protected void finishOperation() { 295 | synchronized (this) { 296 | busy = false; 297 | currentBlock = targetBlock; 298 | } 299 | // NOTE: The interrupt needs to be outside the critical section 300 | // to avoid a race condition: The interrupt handler in the kernel 301 | // may wish to call beginRead or beginWrite (perhaps indirectly), 302 | // which would deadlock if the interrupt handler were invoked with 303 | // the disk mutex locked. 304 | Kernel.interrupt(Kernel.INTERRUPT_DISK, 305 | 0,0,null,null,null); 306 | } // finishOperation() 307 | 308 | /** This method simulates the internal microprocessor of the disk 309 | * controler. It repeatedly waits for a start signal, does an I/O 310 | * operation, and sends an interrupt to the CPU. 311 | * This method should not be called directly. 312 | */ 313 | public void run() { 314 | for (;;) { 315 | waitForRequest(); 316 | 317 | // Pause to do the operation 318 | delay(targetBlock); 319 | 320 | // Move the data. 321 | if (isWriting) { 322 | arraycopy( 323 | buffer, 0, 324 | data, targetBlock * BLOCK_SIZE, 325 | BLOCK_SIZE); 326 | writeCount++; 327 | } else { 328 | arraycopy( 329 | data, targetBlock * BLOCK_SIZE, 330 | buffer, 0, 331 | BLOCK_SIZE); 332 | readCount++; 333 | } 334 | 335 | // Signal completion 336 | finishOperation(); 337 | } 338 | } // run() 339 | } // Disk 340 | -------------------------------------------------------------------------------- /src/minikernel/FastDisk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: FastDisk.java,v 1.15 2006/11/22 21:47:00 solomon Exp $ */ 9 | 10 | /** A new and improved Disk. 11 | * You may not change this class. 12 | *

13 | * This disk is so much faster than the previous model that read and write 14 | * operations appear to finish in no time. Because disk is so fast, beginRead 15 | * and beginWrite wait for the operation to finish rather than causing a CPU 16 | * interrupt when they complete. 17 | *

18 | * @see Disk 19 | * @see Kernel 20 | */ 21 | public class FastDisk extends Disk { 22 | /** Creates a new FastDisk. 23 | * @param size the total size of this disk, in blocks. 24 | */ 25 | public FastDisk(int size) { 26 | super(size); 27 | if (size < 0 || size >= (1<<15)) { 28 | throw new DiskException( 29 | String.format( 30 | "Cannot make a FastDisk with %d blocks. Max size is %d.", 31 | size, 1<<15)); 32 | } 33 | } // FastDisk 34 | 35 | /** Performs a read operation. 36 | * When this method returns, the operation is complete. 37 | * @param blockNumber The block number to read from. 38 | * @param buffer a data area to hold the data read. 39 | * @see Disk#beginRead(int, byte[]) 40 | */ 41 | public void read(int blockNumber, byte buffer[]) { 42 | System.arraycopy( 43 | data, blockNumber * BLOCK_SIZE, 44 | buffer, 0, 45 | BLOCK_SIZE); 46 | readCount++; 47 | } // read(int, byte[]) 48 | 49 | /** Performs a write operation. 50 | * When this method returns, the operation is complete. 51 | * @param blockNumber The block number to write to. 52 | * @param buffer a data area to hold the data to be written. 53 | * @see Disk#beginWrite(int, byte[]) 54 | */ 55 | public void write(int blockNumber, byte buffer[]) { 56 | System.arraycopy( 57 | buffer, 0, 58 | data, blockNumber * BLOCK_SIZE, 59 | BLOCK_SIZE); 60 | writeCount++; 61 | } // write(int, byte[]) 62 | 63 | //************Code added by Brett Duncan*********************// 64 | @Override 65 | public void format() { 66 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 67 | for (int i = 1; i < DISK_SIZE; i++) { 68 | data[i] = '0'; 69 | } 70 | } 71 | 72 | public int getBlockSize() { 73 | return BLOCK_SIZE; 74 | } 75 | 76 | //************End code added by Brett Duncan*********************// 77 | 78 | /** Starts a new read operation. 79 | * @param blockNumber The block number to read from. 80 | * @param buffer A data area to hold the data read. This array must be 81 | * allocated by the caller and have length of at least 82 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 83 | * bytes of the array will be modified. 84 | * @deprecated Do not use this method. Use read instead. 85 | */ 86 | @Deprecated 87 | public synchronized void beginRead(int blockNumber, byte buffer[]) { 88 | throw new UnsupportedOperationException( 89 | "Don't use beginRead. Use read"); 90 | } // beginRead(int, byte[]) 91 | 92 | /** Starts a new write operation. 93 | * @param blockNumber The block number to write to. 94 | * @param buffer A data area containing the data to be written. This array 95 | * must be allocated by the caller and have length of at least 96 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 97 | * bytes of the array will be sent to the disk. 98 | * @deprecated Do not use this method. Use read instead. 99 | */ 100 | @Deprecated 101 | public synchronized void beginWrite(int blockNumber, byte buffer[]) { 102 | throw new UnsupportedOperationException( 103 | "Don't use beginWrite. Use write"); 104 | } // beginWrite byte[]) 105 | } // FastDisk 106 | -------------------------------------------------------------------------------- /src/minikernel/FileSys.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: FileSys.java.src,v 1.4 2007/04/25 14:12:25 solomon Exp $ */ 9 | 10 | import java.util.*; 11 | import java.io.*; 12 | import static java.lang.System.*; 13 | 14 | /** A file system. */ 15 | 16 | public class FileSys { 17 | /** The disk holding this file system. */ 18 | private FastDisk disk; 19 | 20 | //************Code added by Brett Duncan*********************// 21 | private String currDir; 22 | private String fileTable[]; 23 | 24 | //************End code added by Brett Duncan*********************// 25 | 26 | /** Initializes a FileSys instance for managing a disk. 27 | * 28 | * @param disk the disk containing the persistent data. 29 | */ 30 | public FileSys(FastDisk disk) { 31 | this.disk = disk; 32 | 33 | //************Code added by Brett Duncan*********************// 34 | currDir = "/"; 35 | 36 | 37 | fileTable = new String[100]; 38 | int startBlock = 1; //The starting block where files begin to be stored. 39 | 40 | byte freeMap[] = new byte[disk.getBlockSize()]; 41 | 42 | disk.read(0, freeMap); 43 | 44 | byte tempBuffer[] = new byte[disk.getBlockSize()]; 45 | for (int i = startBlock; i < fileTable.length; i++) { 46 | if (freeMap[i] == '1') { 47 | disk.read(i, tempBuffer); 48 | 49 | String fileName = ""; 50 | for (int j = 0; j < 32; j++) { 51 | fileName += (char) tempBuffer[j]; 52 | } 53 | fileTable[i] = fileName; 54 | } 55 | } 56 | 57 | /* 58 | for (int i = startBlock; i < fileTable.length; i++) { 59 | byte fileNameBuffer[] = new byte[32]; 60 | disk.read(i, 32, fileNameBuffer); 61 | fileTable[i] = new String(fileNameBuffer); 62 | }*/ 63 | 64 | // For debugging purposes, print out the fileTable[] array. 65 | // for (int i = startBlock; i < fileTable.length; i++) { 66 | // System.out.printf("fileTable[%d] = %s\n", i, fileTable[i]); 67 | // } 68 | 69 | //************End code added by Brett Duncan*********************// 70 | 71 | } // FileSys(FastDisk) 72 | 73 | //************Code added by Brett Duncan*********************// 74 | 75 | public FastDisk getDisk() { 76 | return disk; 77 | } 78 | 79 | public int getBlockSizeOfDisk() { 80 | return disk.getBlockSize(); 81 | } 82 | 83 | public void setDisk(Disk disk) { 84 | this.disk = (FastDisk) disk; 85 | } 86 | 87 | public String[] getFileTable() { 88 | return fileTable; 89 | } 90 | 91 | public void updateFileTable(int targetBlock, String newFileName) { 92 | fileTable[targetBlock] = newFileName; 93 | } 94 | 95 | //************End code added by Brett Duncan*********************// 96 | 97 | } // FileSys 98 | -------------------------------------------------------------------------------- /src/minikernel/FileTester.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | import java.io.*; 9 | import java.util.*; 10 | import static java.lang.System.*; 11 | 12 | /** Basic driver program to be used as a shell for the MiniKernel for project 5. 13 | * It can be run in two modes: 14 | *

15 | *
Interactive:
java Boot ... FileTester 16 | *
With a test script file:
java Boot ... FileTester script 17 | *
18 | * To get a list of supported commands, type 'help' at the command prompt. 19 | *

20 | * The testfile consists of commands to the driver program (one per line) as 21 | * well as comments. Comments beginning with /* will be ignored completely by 22 | * the driver. Comments beginning with // will be echoed to the output. 23 | *

24 | * See the test files test*.data for examples. 25 | * 26 | * Revised, May 7, 2007. 27 | * $Id: FileTester.java,v 1.21 2007/05/07 15:04:02 solomon Exp $ 28 | */ 29 | public class FileTester { 30 | /** Synopsis of commands. */ 31 | private static String[] helpInfo = { 32 | "help", 33 | "quit", 34 | "format", 35 | "cd pathname", 36 | "pwd", 37 | "create pathname", 38 | "read pathname", 39 | "write pathname data", 40 | "writeln pathname", 41 | "rm pathname", 42 | "mkdir pathname", 43 | "rmdir pathname", 44 | "ln oldpath newpath", 45 | "readlink pathname", 46 | "ls [ dirname ]", 47 | }; 48 | 49 | /** Disk block size, as retrieved from the Disk (cheating!). */ 50 | private static int blockSize; 51 | 52 | /** Main program. 53 | * @param args command-line arguments (there should be at most one: 54 | * the name of a test file from which to read commands). 55 | */ 56 | public static void main(String [] args) { 57 | 58 | //************Code added by Brett Duncan*********************// 59 | 60 | //Power on the disk 61 | Kernel.interrupt(Kernel.INTERRUPT_POWER_ON, 10, 0, new FastDisk(100), null, null); 62 | //XXX: Script to test commands 63 | // args = new String[1]; 64 | //// args[0] = "test1.script"; 65 | // args[0] = "mytest.script"; 66 | 67 | //************End code added by Brett Duncan*********************// 68 | 69 | // NB: This program is designed only to test the file system support 70 | // of the kernel, so it "cheats" in using non-kernel operations to 71 | // read commands and write diagnostics. 72 | if (args.length > 1) { 73 | System.err.println("usage: FileTester [ script-file ]"); 74 | System.exit(0); 75 | } 76 | 77 | // blockSize = Disk.BLOCK_SIZE; 78 | // This is a bit of a cheat. We really should have a Kernel call 79 | // to get this information. 80 | blockSize = Library.getBlockSizeOfDisk(); 81 | //XXX: Fixed it. 82 | 83 | // Is the input coming from a file? 84 | boolean fromFile = (args.length == 1); 85 | 86 | // Create a stream for input 87 | BufferedReader input = null; 88 | 89 | // Open our input stream 90 | if (fromFile) { 91 | try { 92 | input = new BufferedReader(new FileReader(args[0])); 93 | } catch (FileNotFoundException e) { 94 | System.err.println("Error: Script file " 95 | + args[0] + " not found."); 96 | System.exit(1); 97 | } 98 | } else { 99 | input = new BufferedReader(new InputStreamReader(System.in)); 100 | } 101 | 102 | // Cycle through user or file input 103 | for (;;) { 104 | String cmd = null; 105 | try { 106 | // Print out the prompt for the user 107 | if (!fromFile) { 108 | out.printf("--> "); 109 | System.out.flush(); 110 | } 111 | 112 | // Read in a line 113 | String line = input.readLine(); 114 | 115 | // Check for EOF and empty lines 116 | if (line == null) { 117 | // End of file (Ctrl-D for interactive input) 118 | return; 119 | } 120 | line = line.trim(); 121 | if (line.length() == 0) { 122 | continue; 123 | } 124 | 125 | // Handle comments and echoing 126 | if (line.startsWith("//")) { 127 | if (fromFile) { 128 | out.printf("%s\n", line); 129 | } 130 | continue; 131 | } 132 | if (line.startsWith("/*")) { 133 | continue; 134 | } 135 | 136 | // Echo the command line 137 | if (fromFile) { 138 | out.printf("--> %s\n", line); 139 | } 140 | 141 | // Parse the command line 142 | StringTokenizer st = new StringTokenizer(line); 143 | cmd = st.nextToken(); 144 | 145 | // Call the function that corresponds to the command 146 | int result = 0; 147 | if (cmd.equalsIgnoreCase("quit")) { 148 | Library.shutdown(); 149 | return; 150 | } else if (cmd.equalsIgnoreCase("help") || cmd.equals("?")) { 151 | help(); 152 | continue; 153 | } else if (cmd.equalsIgnoreCase("format")) { 154 | result = Library.format(); 155 | } else if (cmd.equalsIgnoreCase("cd")) { 156 | result = Library.chdir(st.nextToken()); 157 | } else if (cmd.equalsIgnoreCase("pwd")) { 158 | result = pwd(); 159 | } else if (cmd.equalsIgnoreCase("create")) { 160 | result = Library.create(st.nextToken()); 161 | } else if (cmd.equalsIgnoreCase("read")) { 162 | result = readTest(st.nextToken(), false); 163 | } else if (cmd.equalsIgnoreCase("write")) { 164 | result = writeTest(st.nextToken(), line); 165 | } else if (cmd.equalsIgnoreCase("writeln")) { 166 | result = writeLines(st.nextToken(), input); 167 | } else if (cmd.equalsIgnoreCase("rm")) { 168 | result = Library.delete(st.nextToken()); 169 | } else if (cmd.equalsIgnoreCase("mkdir")) { 170 | result = Library.mkdir(st.nextToken()); 171 | } else if (cmd.equalsIgnoreCase("rmdir")) { 172 | result = Library.rmdir(st.nextToken()); 173 | } else if (cmd.equalsIgnoreCase("ln")) { 174 | String oldName = st.nextToken(); 175 | String newName = st.nextToken(); 176 | result = Library.symlink(oldName, newName); 177 | } else if (cmd.equalsIgnoreCase("readlink")) { 178 | result = readTest(st.nextToken(), true); 179 | } else if (cmd.equalsIgnoreCase("ls")) { 180 | if (st.hasMoreTokens()) { 181 | result = dumpDir(st.nextToken()); 182 | } else { 183 | result = dumpDir("."); 184 | } 185 | } else { 186 | out.printf("unknown command\n"); 187 | continue; 188 | } 189 | 190 | // Print out the result of the function call 191 | if (result != 0) { 192 | if (result == -1) { 193 | out.printf("*** System call failed\n"); 194 | } else { 195 | out.printf("*** Bad result %d from system call\n", 196 | result); 197 | } 198 | } 199 | } catch (NoSuchElementException e) { 200 | // Handler for nextToken() 201 | out.printf("Incorrect number of arguments\n"); 202 | help(cmd); 203 | } catch (IOException e) { 204 | e.printStackTrace(); 205 | return; 206 | } 207 | } // for (;;) 208 | } // main(String[]) 209 | 210 | /** Prints a list of available commands. */ 211 | private static void help() { 212 | out.printf("Commands are:\n"); 213 | for (int i = 0; i < helpInfo.length; i++) { 214 | out.printf(" %s\n", helpInfo[i]); 215 | } 216 | } // help() 217 | 218 | /** Prints help for command "cmd". 219 | * @param cmd the name of the command. 220 | */ 221 | private static void help(String cmd) { 222 | for (int i = 0; i < helpInfo.length; i++) { 223 | if (helpInfo[i].startsWith(cmd)) { 224 | out.printf("usage: %s\n", helpInfo[i]); 225 | return; 226 | } 227 | } 228 | out.printf("unknown command '%s'\n", cmd); 229 | } // help(String) 230 | 231 | /** Reads data from a (simulated) file or symlink using Library.read 232 | * or Library.readlink and displays the results. 233 | * @param fname the name of the file or symlink. 234 | * @param isLink true to read a symlink, false to read an ordinary file. 235 | * @return the result of the Library.read call. 236 | */ 237 | private static int readTest(String fname, boolean isLink) { 238 | byte[] buf = new byte[blockSize]; 239 | int n = isLink 240 | ? Library.readlink(fname, buf) 241 | : Library.read(fname, buf); 242 | boolean needNewline = false; 243 | if (n < 0) { 244 | return n; 245 | } 246 | for (int i = 0; i < buf.length; i++) { 247 | if (buf[i] != 0) { 248 | showChar(buf[i] & 0xff); 249 | needNewline = (buf[i] != '\n'); 250 | } 251 | } 252 | if (needNewline) { 253 | out.printf("\n"); 254 | } 255 | return n; 256 | } // readTest(String,boolean) 257 | 258 | /** Writes data to a (simulated) file using Library.write. 259 | * @param fname the name of the file. 260 | * @param info a source of data. 261 | * @return the result of the Library.write call. 262 | */ 263 | private static int writeTest(String fname, String info) { 264 | // Info has the format 'write fname one two three ... 265 | int p; 266 | p = info.indexOf(' '); 267 | if (p >= 0) { 268 | p = info.indexOf(' ', p + 1); 269 | if (p < 0) { 270 | p = info.length(); 271 | } else { 272 | p++; 273 | } 274 | } else { 275 | p = 0; 276 | } 277 | byte[] buf = new byte[Math.max(blockSize, info.length() - p)]; 278 | int i = 0; 279 | while (p < info.length()) { 280 | buf[i++] = (byte) info.charAt(p++); 281 | } 282 | return Library.write(fname, buf); 283 | } // writeTest(String, byte[]) 284 | 285 | /** Write data to a (simulated) file using Library.write. 286 | * Data comes from the following lines in the input stream. 287 | * @param fname the name of the file. 288 | * @param in the input stream. 289 | * @return the result of the Library.write call. 290 | */ 291 | private static int writeLines(String fname, BufferedReader in) { 292 | try { 293 | byte[] buf = new byte[blockSize]; 294 | int i = 0; 295 | for (;;) { 296 | String line = in.readLine(); 297 | if (line == null || line.equals(".")) { 298 | break; 299 | } 300 | for (int j = 0; j < line.length(); j++) { 301 | if (i >= buf.length) { 302 | byte[] newBuf = new byte[buf.length * 2]; 303 | System.arraycopy(buf, 0, newBuf, 0, buf.length); 304 | buf = newBuf; 305 | } 306 | buf[i++] = (byte) line.charAt(j); 307 | } 308 | if (i >= buf.length) { 309 | break; 310 | } 311 | buf[i++] = '\n'; 312 | } 313 | return Library.write(fname, buf); 314 | } catch (IOException e) { 315 | e.printStackTrace(); 316 | return -1; 317 | } 318 | } // writeLines(String, BufferedReader) 319 | 320 | /** Display a readable representation of a byte. 321 | * @param b the byte to display as a number in the range 0..255. 322 | */ 323 | private static void showChar(int b) { 324 | if (b >= ' ' && b <= '~') { 325 | out.printf("%c", (char)b); 326 | return; 327 | } 328 | if (b == '\n') { 329 | out.printf("\\n\n"); 330 | return; 331 | } 332 | if (b == '\\') { 333 | out.printf("\\\\"); 334 | return; 335 | } 336 | out.printf("\\%03o", b); 337 | } // showChar(int) 338 | 339 | /** Displays the contents of a directory. 340 | * @param dirname the name of the directory. 341 | * @return the result of the readdir call. 342 | */ 343 | private static int dumpDir(String dirname) { 344 | byte[] buf = new byte[blockSize]; 345 | int n = Library.readdir(dirname, buf); 346 | if (n < 0) { 347 | return n; 348 | } 349 | for (int i = 0; i < buf.length; i += 16) { 350 | int block = ((buf[i] & 0xff) << 8) + (buf [i+1] & 0xff); 351 | if (block == 0) { 352 | continue; 353 | } 354 | StringBuffer sb = new StringBuffer(); 355 | for (int j = 3; j < 16; j++) { 356 | if (buf[i + j] == 0) { 357 | break; 358 | } 359 | sb.append((char) buf[i + j]); 360 | } 361 | String fname = sb.toString(); 362 | out.printf("%s %s", block, fname); 363 | switch (buf[i + 2]) { 364 | case 'O': 365 | break; 366 | case 'D': 367 | out.printf("/"); 368 | break; 369 | case 'L': 370 | out.printf(" -> "); 371 | byte[] buf1 = new byte[blockSize]; 372 | n = Library.readlink(dirname + "/" + fname, buf1); 373 | if (n < 0) { 374 | return n; 375 | } 376 | for (int j = 0; j < buf1.length; j++) { 377 | if (buf1[j] == 0) { 378 | break; 379 | } 380 | out.printf("%c", (char) buf1[j]); 381 | } 382 | break; 383 | default: 384 | out.printf("?type \\%03o?", buf[i + 2]); 385 | //out.printf("", buf[i + 2]); 386 | } 387 | out.printf("\n"); 388 | } 389 | return n; 390 | } // dumpDir(String) 391 | 392 | /** Prints the current working directory, as determined from inspecting 393 | * various directories with readdir(). Prints an error message if 394 | * something is wrong. 395 | * 396 | * @return 0 if everything is ok, or -1 if there is an error. 397 | */ 398 | private static int pwd() { 399 | int rc, dot, dotdot; 400 | int child = 0; 401 | String relPath = "."; 402 | List path = new LinkedList(); 403 | byte[] buf = new byte[blockSize]; 404 | for (;;) { 405 | rc = Library.readdir(relPath, buf); 406 | if (rc != 0) { 407 | out.printf("pwd: cannot read directory \"%s\"\n", relPath); 408 | return -1; 409 | } 410 | dot = dirSearch(buf, "."); 411 | if (dot == 0) { 412 | out.printf("pwd: bad directory \"%s\": no . entry\n", 413 | relPath); 414 | return -1; 415 | } 416 | if (child != 0) { 417 | String cname = dirSearch(buf, child); 418 | if (cname == null) { 419 | out.printf("pwd: bad directory \"%s\": " 420 | + " no entry for %d\n", relPath, child); 421 | return -1; 422 | } 423 | path.add(0, cname); 424 | } 425 | dotdot = dirSearch(buf, ".."); 426 | if (dotdot == 0) { 427 | out.printf("pwd: bad directory \"%s\": no .. entry\n", 428 | relPath); 429 | return -1; 430 | } 431 | if (dot == dotdot) { 432 | break; 433 | } 434 | child = dot; 435 | relPath += "/.."; 436 | } 437 | if (path.size() == 0) { 438 | out.printf("/"); 439 | } else { 440 | for (String s : path) { 441 | out.printf("/%s", s); 442 | } 443 | } 444 | out.printf("\n"); 445 | return 0; 446 | } // pwd() 447 | 448 | /** Searches a directory for a particular name. 449 | * 450 | * @param buf the raw contents of the directory 451 | * @param s the name to look for 452 | * @return the corresponding block number, or 0 for errors. 453 | */ 454 | private static int dirSearch(byte[] buf, String s) { 455 | for (int offset = 0; offset < buf.length; offset += 16) { 456 | int j; 457 | for (j = 0; j < 13; j++) { 458 | if (j >= s.length() 459 | || buf[offset + j + 3] != (byte) s.charAt(j)) 460 | { 461 | break; 462 | } 463 | } 464 | if (j == s.length() && buf[offset + j + 3] == 0) { 465 | return (((buf[offset] & 0xff) << 8) + (buf[offset+1] & 0xff)); 466 | } 467 | } 468 | return 0; 469 | } // dirSearch(byte[],String) 470 | 471 | /** Searches a directory for an entry with a particular block number 472 | * 473 | * @param buf the raw contents of the directory 474 | * @param n the block num to look for 475 | * @return the corresponding name, or null for errors. 476 | */ 477 | private static String dirSearch(byte[] buf, int n) { 478 | for (int offset = 0; offset < buf.length; offset += 16) { 479 | int blk = (((buf[offset] & 0xff) << 8) + (buf[offset+1] & 0xff)); 480 | if (blk == n) { 481 | int j; 482 | for (j = 0; j < 13; j++) { 483 | if (buf[offset + j + 3] == 0) { 484 | break; 485 | } 486 | } 487 | return new String(buf, offset + 3, j); 488 | } 489 | } 490 | return null; 491 | } // dirSearch(byte[],String) 492 | } // FileTester 493 | -------------------------------------------------------------------------------- /src/minikernel/Kernel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: Kernel.java.src,v 1.4 2007/04/25 14:20:12 solomon Exp $ */ 9 | 10 | import java.util.*; 11 | import java.io.*; 12 | import java.lang.reflect.*; 13 | import static java.lang.System.*; 14 | 15 | /** A simple kernel simulation. 16 | * 17 | *

18 | * There is only one public interface to this class: interrupt(). 19 | * System calls, disk notification, and power on messages all arrive 20 | * by way of this function. 21 | *

22 | * See the list of SYSCALL_XXX constants to learn what 23 | * system calls are currently supported. 24 | * 25 | * @see Disk 26 | * @see Library 27 | */ 28 | public class Kernel { 29 | 30 | /** No public instances. */ 31 | private Kernel() {} 32 | 33 | //////////////// Values for the first parameter ("kind") to interrupt() 34 | 35 | /** An interrupt kind indicating that a user program caused the interrupt. 36 | *

    37 | *
  • Parameter i1 -- a valid system call number. 38 | *
39 | * Other parameters depend on the call number. 40 | */ 41 | public static final int INTERRUPT_USER = 0; 42 | 43 | /** An interrupt kind indicating that a disk caused the interrupt. 44 | * All other parameters will be null or zero. 45 | */ 46 | public static final int INTERRUPT_DISK = 1; 47 | 48 | /** An interrupt kind indicating that the system just started. 49 | * The Kernel should set up any internal state and 50 | * begin executing the first program. 51 | *
    52 | *
  • Parameter i1 -- the number of blocks to use in the 53 | * disk cache. 54 | *
  • Parameter o1 -- an instance of Disk to use as the disk. 55 | *
  • Parameter o2 -- a String containing the name of the shell. 56 | *
57 | */ 58 | public static final int INTERRUPT_POWER_ON = 2; 59 | 60 | //////////////// Values for the second parameter ("i1") for USER interrupts 61 | 62 | /** System call to output text on the console. 63 | *
    64 | *
  • Parameter o1 -- A string to display 65 | *
  • Returns -- zero. 66 | *
67 | */ 68 | public static final int SYSCALL_OUTPUT = 0; 69 | 70 | /** System call to read text from the console. 71 | * This function returns when the user presses [Enter]. 72 | *
    73 | *
  • Parameter o1 -- A StringBuffer to fill with input text. 74 | *
  • Returns -- zero, ERROR_BAD_ARGUMENT, ERROR_END_OF_FILE, 75 | * or ERROR_IO. 76 | *
77 | */ 78 | public static final int SYSCALL_INPUT = 1; 79 | 80 | /** System call to execute a new program. 81 | * The new program will run in parallel to the current program. 82 | *
    83 | *
  • Parameter o1 - The name of a Java class to execute. 84 | *
  • Parameter o2 - An array for String arguments. 85 | *
  • Returns - A non-negative process id or ERROR_BAD_ARGUMENT, 86 | * ERROR_NO_CLASS, ERROR_NO_MAIN, or ERROR_BAD_COMMAND. 87 | *
88 | */ 89 | public static final int SYSCALL_EXEC = 2; 90 | 91 | /** System call to wait for a process to terminate. 92 | * This call will not return until the indicated process has 93 | * run to completion. 94 | *
    95 | *
  • Parameter i2 - the process id to wait for. 96 | *
  • Returns -- zero or ERROR_NO_SUCH_PROCESS. 97 | *
98 | */ 99 | public static final int SYSCALL_JOIN = 3; 100 | 101 | //************Code added by Brett Duncan*********************// 102 | //XXX: SYSCALL constants 103 | 104 | /** 105 | * System call to format the disk. 106 | */ 107 | public static final int SYSCALL_FORMAT = 4; 108 | 109 | /** System call to create a new file. 110 | * 111 | */ 112 | public static final int SYSCALL_CREATE = 5; 113 | 114 | /** 115 | * System call to read the contents of a file. 116 | */ 117 | public static final int SYSCALL_READ = 6; 118 | 119 | /** 120 | * System call to write to the disk. 121 | */ 122 | public static final int SYSCALL_WRITE = 7; 123 | 124 | /** 125 | * System call to delete a file from the disk. 126 | */ 127 | public static final int SYSCALL_DELETE = 8; 128 | 129 | /** 130 | * System call to display contents of the current directory. 131 | */ 132 | public static final int SYSCALL_READDIR = 9; 133 | 134 | /** 135 | * System call to perform safe shutdown of the disk. 136 | */ 137 | public static final int SYSCALL_SHUTDOWN = 10; 138 | 139 | public static final int SYSCALL_GET_BLOCK_SIZE = 11; 140 | 141 | //************End code added by Brett Duncan*****************// 142 | 143 | //////////////// Error codes returned by interrupt() 144 | 145 | /** An error code indicating that one of the system call parameters made no 146 | * sense. 147 | */ 148 | public static final int ERROR_BAD_ARGUMENT = -1; 149 | 150 | /** An error code indicating that the class name passed to SYSCALL_EXEC 151 | * could not be found. 152 | */ 153 | public static final int ERROR_NO_CLASS = -2; 154 | 155 | /** An error code indicating that the class name passed to SYSCALL_EXEC 156 | * named a class with no appropriate main() method. 157 | */ 158 | public static final int ERROR_NO_MAIN = -3; 159 | 160 | /** An error code indicating some unspecified problem running the class 161 | * passed SYSCALL_EXEC. 162 | */ 163 | public static final int ERROR_BAD_COMMAND = -4; 164 | 165 | /** An error code indicating that one parameter was too big or too small. */ 166 | public static final int ERROR_OUT_OF_RANGE = -5; 167 | 168 | /** An error code indicating that end of file was reached. */ 169 | public static final int ERROR_END_OF_FILE = -6; 170 | 171 | /** An error code indicating that something went wrong during an I/O 172 | * operation. 173 | */ 174 | public static final int ERROR_IO = -7; 175 | 176 | /** An error code indicating that a child program caused an exception and 177 | * crashed. 178 | */ 179 | public static final int ERROR_IN_CHILD = -8; 180 | 181 | /** An error code indicating an attempt to join with a non-existant 182 | * process. 183 | */ 184 | public static final int ERROR_NO_SUCH_PROCESS = -9; 185 | 186 | //////////////// Transient state of the kernel 187 | 188 | /** The disk to be used */ 189 | // private static Disk disk; 190 | private static FastDisk disk; 191 | 192 | 193 | /** The file system. */ 194 | private static FileSys filesys; 195 | 196 | /** The size of the disk cache */ 197 | private static int cacheSize; 198 | 199 | //////////////// Methods 200 | 201 | /** This is the only entry into the kernel. 202 | *

A user may call this function to perform a system call. 203 | * In that case, set kind to INTERRUPT_USER 204 | * and set i1 to the system call number. Other 205 | * parameters should be set as the system call requires. 206 | *

207 | * A disk may call this function to indicate the current operation 208 | * has completed. In that case, kind will be 209 | * INTERRUPT_DISK and all parameters will be zero or null. 210 | *
211 | * Important: If the Disk calls interrupt(), the 212 | * Kernel should take care of business and return from the interrupt 213 | * as soon as possible. All Disk I/O is halted while the interrupt is 214 | * being processed. 215 | *

216 | * The boot code may call this function to indicate that the computer 217 | * has been turned on and it is time to start the first program 218 | * and use the disk. In that case, kind will be 219 | * INTERRUPT_POWER_ON, o1 will point to the Disk to be 220 | * used, o2 will be a String containing the name of the shell to use, 221 | * i1 will indicate the size of the buffer cache, 222 | * and all other parameters will be zero or null. 223 | *

224 | * Since different system calls require different parameters, this 225 | * method has a variety of arguments of various types. Any one 226 | * system call will use at most a few of them. The others should be 227 | * zero or null. 228 | * 229 | * @param kind the kind of system call, one of the 230 | * INTERRUPT_XXX codes. 231 | * @param i1 the first integer parameter. If kind == 232 | * INTERRUPT_USER, i1 should be one of the 233 | * SYSTEM_XXX codes to indicate which system call is being 234 | * invoked. 235 | * @param i2 another integer parameter. 236 | * @param o1 a parameter of some object type. 237 | * @param o2 another parameter of some object type. 238 | * @param a a byte-array parameter (generally used for binary input/output). 239 | * 240 | * @return a negative number indicating an error code, or other 241 | * values depending on the system call. 242 | */ 243 | public static int interrupt(int kind, int i1, int i2, 244 | Object o1, Object o2, byte a[]) 245 | { 246 | try { 247 | switch (kind) { 248 | case INTERRUPT_USER: 249 | switch (i1) { 250 | case SYSCALL_OUTPUT: 251 | return doOutput((String)o1); 252 | 253 | case SYSCALL_INPUT: 254 | return doInput((StringBuffer)o1); 255 | 256 | case SYSCALL_EXEC: 257 | return doExec((String)o1,(String[])o2); 258 | 259 | case SYSCALL_JOIN: 260 | return doJoin(i2); 261 | 262 | //************Code added by Brett Duncan*********************// 263 | //XXX: SYSCALL cases 264 | 265 | case SYSCALL_FORMAT: 266 | return doFormat(); 267 | 268 | case SYSCALL_CREATE: 269 | return doCreateFile(a); 270 | 271 | case SYSCALL_READ: 272 | return doRead(a); 273 | 274 | case SYSCALL_WRITE: 275 | return doWrite(((String) o1).getBytes(), a); 276 | 277 | case SYSCALL_DELETE: 278 | return doDelete(((String) o1).getBytes()); 279 | 280 | case SYSCALL_READDIR: 281 | return doReadDir(); 282 | 283 | case SYSCALL_SHUTDOWN: 284 | doShutdown(); 285 | break; 286 | 287 | case SYSCALL_GET_BLOCK_SIZE: 288 | return doGetBlockSize(); 289 | 290 | //************End code added by Brett Duncan*********************// 291 | 292 | default: 293 | return ERROR_BAD_ARGUMENT; 294 | } 295 | 296 | case INTERRUPT_DISK: 297 | break; 298 | 299 | case INTERRUPT_POWER_ON: 300 | doPowerOn(i1, o1, o2); 301 | // doShutdown(); 302 | break; 303 | 304 | default: 305 | return ERROR_BAD_ARGUMENT; 306 | } // switch (kind) 307 | } catch (Exception e) { 308 | // Most likely, we arrived here due to a bad cast. 309 | e.printStackTrace(); 310 | return ERROR_BAD_ARGUMENT; 311 | } 312 | return 0; 313 | } // interrupt(int, int, int, Object, Object, byte[]) 314 | 315 | /** Performs the actions associated with a POWER_ON interrupt. 316 | * @param i1 the first int parameter to the interrupt (the disk cache size) 317 | * @param o1 the first Object parameter to the interrupt (the Disk). 318 | * @param o2 the second Object parameter to the interrupt (the shell 319 | * command-line). 320 | */ 321 | private static void doPowerOn(int i1, Object o1, Object o2) { 322 | cacheSize = i1; 323 | 324 | //************Code added by Brett Duncan*********************// 325 | // disk = (Disk)o1; 326 | disk = (FastDisk)o1; 327 | //Create new file system object, which makes managing the file system 328 | //easier. 329 | filesys = new FileSys(disk); 330 | //************End code added by Brett Duncan*********************// 331 | 332 | String shellCommand = (String) o2; 333 | 334 | doOutput("Kernel: Disk is " + filesys.getBlockSizeOfDisk() + " blocks\n"); 335 | doOutput("Kernel: Disk cache size is " + i1 + " blocks\n"); 336 | 337 | //Commented this out because we're just gonna use the default shell 338 | //in FileTester.java. 339 | /*doOutput("Kernel: Loading initial program.\n"); 340 | 341 | StringTokenizer st = new StringTokenizer(shellCommand); 342 | int n = st.countTokens(); 343 | if (n < 1) { 344 | doOutput("Kernel: No shell specified\n"); 345 | exit(1); 346 | } 347 | 348 | String shellName = st.nextToken(); 349 | String[] args = new String[n - 1]; 350 | for (int i = 1; i < n; i++) { 351 | args[i - 1] = st.nextToken(); 352 | } 353 | 354 | if (doExecAndWait(shellName, args) < 0) { 355 | doOutput("Kernel: Unable to start " + shellCommand + "!\n"); 356 | exit(1); 357 | } else { 358 | doOutput("Kernel: " + shellCommand + " has terminated.\n"); 359 | } 360 | */ 361 | Launcher.joinAll(); 362 | } // doPowerOn(int, Object, Object) 363 | 364 | /** Does any "shutdown" activities required after all activities started by 365 | * a POWER_ON interrupt have completed. 366 | */ 367 | private static void doShutdown() { 368 | 369 | // disk.flush(); 370 | //************Code added by Brett Duncan*********************// 371 | //XXX: Shutdown method 372 | filesys.getDisk().flush(); 373 | //************End code added by Brett Duncan*********************// 374 | } // doShutdown() 375 | 376 | /** Displays a message on the console. 377 | * @param msg the message to display 378 | */ 379 | private static int doOutput(String msg) { 380 | out.print(msg); 381 | return 0; 382 | } // doOutput(String) 383 | 384 | private static BufferedReader br 385 | = new BufferedReader(new InputStreamReader(in)); 386 | 387 | /** Reads a line from the console into a StringBuffer. 388 | * @param sb a place to put the line of input. 389 | */ 390 | private static int doInput(StringBuffer sb) { 391 | try { 392 | String s = br.readLine(); 393 | if (s==null) { 394 | return ERROR_END_OF_FILE; 395 | } 396 | sb.append(s); 397 | return 0; 398 | } catch (IOException t) { 399 | t.printStackTrace(); 400 | return ERROR_IO; 401 | } 402 | } // doInput(StringBuffer) 403 | 404 | /** Loads a program and runs it. 405 | * Blocks the caller until the program has terminated. 406 | * @param command the program to run. 407 | * @param args command-line args to pass to the program. 408 | * @return the program's return code on success, ERROR_NO_CLASS, 409 | * ERROR_NO_MAIN, or ERROR_BAD_COMMAND if the command cannot be run, or 410 | * ERROR_IN_CHILD if the program throws an uncaught exception. 411 | */ 412 | private static int doExecAndWait(String command, String args[]) { 413 | Launcher l; 414 | try { 415 | l = new Launcher(command, args); 416 | } catch (ClassNotFoundException e) { 417 | return ERROR_NO_CLASS; 418 | } catch (NoSuchMethodException e) { 419 | return ERROR_NO_MAIN; 420 | } catch (Exception e) { 421 | e.printStackTrace(); 422 | return ERROR_BAD_COMMAND; 423 | } 424 | try { 425 | l.run(); 426 | l.delete(); 427 | return l.returnCode; 428 | } catch (Exception e) { 429 | e.printStackTrace(); 430 | return ERROR_IN_CHILD; 431 | } 432 | } // doExecAndWait(String, String[]) 433 | 434 | /** Loads a program and runs it in the background. 435 | * Does not wait for the program to terminate. 436 | * @param command the program to run. 437 | * @param args command-line args to pass to the program. 438 | * @return a process id on success or ERROR_NO_CLASS, ERROR_NO_MAIN, or 439 | * ERROR_BAD_COMMAND if the command cannot be run. 440 | */ 441 | private static int doExec(String command, String args[]) { 442 | try { 443 | Launcher l = new Launcher(command, args); 444 | l.start(); 445 | return l.pid; 446 | } catch (ClassNotFoundException e) { 447 | return ERROR_NO_CLASS; 448 | } catch (NoSuchMethodException e) { 449 | return ERROR_NO_MAIN; 450 | } catch (Exception e) { 451 | e.printStackTrace(); 452 | return ERROR_BAD_COMMAND; 453 | } 454 | } // doExec(String, String[]) 455 | 456 | /** Waits for a program previous started by doExec to terminate. 457 | * @param pid the process id of the program. 458 | * @return the return code returned by the program. 459 | */ 460 | private static int doJoin(int pid) { 461 | return Launcher.joinOne(pid); 462 | } // doJoin(int) 463 | 464 | //************Code added by Brett Duncan*********************// 465 | //XXX: New doXXX methods 466 | 467 | /** 468 | * Libary function to get the block size of the file system's disk. 469 | * @return The block size, in bytes, of the disk. 470 | */ 471 | private static int doGetBlockSize() { 472 | return filesys.getBlockSizeOfDisk(); 473 | } 474 | 475 | /** 476 | * This method initializes the contents of the disk with any data structures 477 | * necessary to represent an "empty" file system. This method should create 478 | * an "empty" root directory "/". 479 | * @return 0 if successful, -1 if there was an error. 480 | */ 481 | private static int doFormat() { 482 | //Create new file system object. 483 | // filesys = new FileSys(new FastDisk(100)); 484 | 485 | filesys.getDisk().format(); 486 | 487 | doOutput("Kernel: Disk formatted.\n"); 488 | 489 | return 0; 490 | } 491 | 492 | /** 493 | * Creates a new file with the indicated filename. The initial contents of 494 | * the file are all null (zero) bytes. 495 | * @param pathName The bytes containing the file name. 496 | * @return 0 if successful, -1 if there was an error. 497 | */ 498 | private static int doCreateFile(byte pathName[]) { 499 | 500 | //Check if file name length is > 32. 501 | if (pathName.length > 32) { 502 | doOutput("Kernel: User error: File name too long!\n"); 503 | return -1; 504 | } 505 | 506 | //A 1 block byte array that will hold the free map 507 | byte freeMap[] = new byte[filesys.getBlockSizeOfDisk()]; 508 | 509 | //Read block 0 into freeMap 510 | filesys.getDisk().read(0, freeMap); 511 | 512 | //Check if the file specified by pathName already exists. 513 | String pathNameString = new String(pathName); 514 | for (int i = 1; i < 100; i++) { 515 | if (freeMap[i] == '1' 516 | && pathNameString.equals(filesys.getFileTable()[i].trim())) { 517 | 518 | doOutput("Kernel: User error: File name already exists at" 519 | + "block " + i + "!\n"); 520 | return -1; 521 | } 522 | } 523 | 524 | //The target block to create the file to. 525 | int targetBlock = 1; 526 | //Search freeMap for the next 0, which will be the target block. 527 | for (int i = 1; i < filesys.getDisk().DISK_SIZE; i++) { 528 | if (freeMap[i] == '0') { 529 | targetBlock = i; 530 | break; 531 | } 532 | } 533 | 534 | //Byte array that will hold the path name and file contents. 535 | byte pathAndContents[] = new byte[filesys.getBlockSizeOfDisk()]; 536 | //Copy path name to pathAndContents 537 | arraycopy(pathName, 0, pathAndContents, 0, pathName.length); 538 | 539 | //Write path and contents to the target block. 540 | filesys.getDisk().write(targetBlock, pathAndContents); 541 | 542 | //Update the free map at targetBlock indicating that block is occupied. 543 | freeMap[targetBlock] = '1'; 544 | 545 | //Write free map back to block 0 of the disk. 546 | filesys.getDisk().write(0, freeMap); 547 | 548 | //Set the file system's fileTable at targetBlock to the file name so 549 | //we can look it up and other methods can use it. 550 | filesys.getFileTable()[targetBlock] = new String(pathName); 551 | 552 | //Indicate that file was created successfully. 553 | doOutput("Kernel: Created file "); 554 | for (int i = 0; i < pathName.length; i++) { 555 | doOutput((char) pathName[i] + ""); 556 | } 557 | doOutput(" at block " + targetBlock + ". \n"); 558 | 559 | return 0; 560 | } 561 | 562 | /** 563 | * Reads and displays the contents of the file specified by pathName[]. 564 | * @param pathName The file name to read the contents of. 565 | * @return 0 if successful, -1 if there was an error. 566 | */ 567 | private static int doRead(byte pathName[]) { 568 | 569 | //Buffer holding the data read from a block. 570 | byte tempBuffer[] = new byte[FastDisk.BLOCK_SIZE]; 571 | 572 | int targetBlock = findTargetBlock(pathName); 573 | 574 | if (targetBlock == -1) { 575 | doOutput("Kernel: User error: File not found.\n"); 576 | return -1; 577 | } 578 | 579 | filesys.getDisk().read(targetBlock, tempBuffer); 580 | 581 | doOutput("Kernel: File name: "); 582 | for (int i = 0; i < 32; i++) { 583 | doOutput((char) tempBuffer[i] + ""); 584 | } 585 | doOutput(" at block " + targetBlock + ". "); 586 | doOutput("Contents: \n"); 587 | for (int i = 32; i < 512; i++) { 588 | doOutput((char) tempBuffer[i] + ""); 589 | } 590 | doOutput("\n"); 591 | return 0; 592 | } 593 | 594 | /** 595 | * Writes the contents of buffer[] into the file specified by pathName[]. 596 | * @param pathName The file to write the buffer to. 597 | * @param buffer The contents to be written to the file. 598 | * @return 0 if successful, -1 if there was an error. 599 | */ 600 | private static int doWrite(byte pathName[], byte buffer[]) { 601 | 602 | int targetBlock = findTargetBlock(pathName); 603 | 604 | if (targetBlock == -1) { 605 | doOutput("Kernel: User error: File not found.\n"); 606 | return -1; 607 | } 608 | 609 | //The byte array that will hold the file name and buffer (contents to 610 | //write). Do this because in FileTester, it strips off the file name. 611 | byte fileNameAndBuffer[] = new byte[filesys.getBlockSizeOfDisk()]; 612 | 613 | //Copy buffer (the contents to write) into fileNameAndBuffer, starting 614 | //at position 32 because in FileTester, it strips off the file name. 615 | arraycopy(buffer, 0, 616 | fileNameAndBuffer, 32, 617 | filesys.getBlockSizeOfDisk() - 32); 618 | 619 | //Add the file name to the beginning of fileNameAndBuffer. 620 | System.arraycopy(pathName, 0, 621 | fileNameAndBuffer, 0, 622 | pathName.length); 623 | 624 | //Now we can write, keeping the file name at the beginning instead of 625 | //it being overwritten by the contents. 626 | filesys.getDisk().write(targetBlock, fileNameAndBuffer); 627 | 628 | return 0; 629 | } 630 | 631 | /** 632 | * Deletes a file from the disk, making the block occupied by the file null. 633 | * @param pathName The file to be deleted. 634 | * @return 0 if successful, -1 if there was an error. 635 | */ 636 | private static int doDelete(byte pathName[]) { 637 | //Find target block to delete. 638 | int targetBlock = findTargetBlock(pathName); 639 | 640 | if (targetBlock == -1) { 641 | doOutput("Kernel: User error: File not found.\n"); 642 | return -1; 643 | } 644 | 645 | //Create a null byte array of size block size. 646 | byte nullByteArray[] = new byte[filesys.getBlockSizeOfDisk()]; 647 | for (int i = 0; i < nullByteArray.length; i++) { 648 | nullByteArray[i] = '\0'; 649 | } 650 | 651 | //Write the null byte array to the disk at the target block. 652 | filesys.getDisk().write(targetBlock, nullByteArray); 653 | 654 | //Set the file table at the targetBlock's index to 0, indicating the 655 | //file was deleted. 656 | filesys.getFileTable()[targetBlock] = null; 657 | 658 | //A 1 block byte array that will hold the free map 659 | byte freeMap[] = new byte[filesys.getBlockSizeOfDisk()]; 660 | 661 | //Read block 0 into freeMap 662 | filesys.getDisk().read(0, freeMap); 663 | 664 | //Change the bit at the free map to 0, indicating the block is now free. 665 | freeMap[targetBlock] = '0'; 666 | 667 | //Write the updated freeMap back to block 0. 668 | filesys.getDisk().write(0, freeMap); 669 | 670 | return 0; 671 | 672 | } 673 | 674 | /** 675 | * Displays the contents of the current directory. 676 | * @return 0 if successful, -1 if there was an error. 677 | */ 678 | private static int doReadDir() { 679 | 680 | doOutput("Kernel: "); 681 | for (int i = 1; i < filesys.getFileTable().length; i++) { 682 | 683 | doOutput( 684 | filesys.getFileTable()[i] == null ? 685 | "" : 686 | /*"Block " + i + ": " +*/ 687 | filesys.getFileTable()[i].trim() + " " 688 | ); 689 | 690 | } 691 | doOutput("\n"); 692 | 693 | return 0; 694 | } 695 | 696 | /** 697 | * Helper method for finding the block indicated by pathName[]. 698 | * @param pathName The file name to find the block index of. 699 | * @return 0 if successful, -1 if the file was not found. 700 | */ 701 | private static int findTargetBlock(byte pathName[]) { 702 | 703 | int targetBlock = -1; 704 | 705 | String pathNameString = new String(pathName); 706 | for (int i = 1; i < filesys.getDisk().DISK_SIZE; i++) { 707 | 708 | //Check if null so .trim() doesn't throw null pointer exception. 709 | String fileTableString = 710 | filesys.getFileTable()[i] == null ? 711 | "" : 712 | filesys.getFileTable()[i].trim(); 713 | 714 | if (pathNameString.equals(fileTableString)) { 715 | targetBlock = i; 716 | break; 717 | } 718 | } 719 | 720 | return targetBlock; 721 | 722 | } 723 | 724 | //************End code added by Brett Duncan*********************// 725 | 726 | /** A Launcher instance represents one atomic command being run by the 727 | * Kernel. It has associated with it a process id (pid), a Java method 728 | * to run, and a list of arguments to the method. 729 | * Do not modify any part of this class. 730 | */ 731 | static private class Launcher extends Thread { 732 | /** Mapping of process ids to Launcher instances. */ 733 | static Map pidMap = new HashMap(); 734 | 735 | /** Source of unique ids for Launcher instances. */ 736 | static private int nextpid = 1; 737 | 738 | /** The method being run by this command. */ 739 | private Method method; 740 | 741 | /** The list of arguments to this command. */ 742 | private Object arglist[]; 743 | 744 | /** The process id of this command. */ 745 | private int pid; 746 | 747 | /** Return code returned by this command (0 if the command has not yet 748 | * completed. 749 | */ 750 | private int returnCode = 0; 751 | 752 | /** Creates a new Launcher for a program. 753 | * @param command the name of the program (new name of a class with 754 | * a main(String[]) method. 755 | * @param args command-line arguments to the program. 756 | */ 757 | public Launcher(String command, String args[]) 758 | throws ClassNotFoundException, NoSuchMethodException 759 | { 760 | /* If the user supplied no args, make a dummy. */ 761 | if (args==null) { 762 | args = new String[0]; 763 | } 764 | 765 | /* Create an array of the method types */ 766 | Class params[] = new Class[] { args.getClass() }; 767 | 768 | /* Find the program and look up its main method */ 769 | Class programClass = Class.forName(command); 770 | method = programClass.getMethod("main",params); 771 | 772 | /* Assemble an argument list for the method. */ 773 | arglist = new Object[] { args }; 774 | 775 | pid = nextpid++; 776 | synchronized (pidMap) { 777 | pidMap.put(pid, this); 778 | } 779 | } // Launcher.Launcher(String, String[]) 780 | 781 | /** Main loop of the Launcher */ 782 | public void run() { 783 | /* Launch the method using the arglist */ 784 | try { 785 | method.invoke(null,arglist); 786 | } catch (InvocationTargetException e) { 787 | /* Give the user a message */ 788 | out.println("Kernel: User error:"); 789 | e.getTargetException().printStackTrace(); 790 | 791 | returnCode = ERROR_IN_CHILD; 792 | } catch (Exception e) { 793 | out.printf("Kernel: %s\n", e); 794 | returnCode = ERROR_IN_CHILD; 795 | } 796 | } // Launcher.run() 797 | 798 | /** Waits for all existing Launchers to complete. */ 799 | static public void joinAll() { 800 | for (Launcher l : pidMap.values()) { 801 | try { 802 | l.join(); 803 | } catch (InterruptedException ex) { 804 | out.printf("Kernel: join: %s\n", ex); 805 | ex.printStackTrace(); 806 | } 807 | } 808 | } // Launcher.joinAll() 809 | 810 | /** Waits for a particular Launcher to complete. 811 | * @param pid the process id of the desired process. 812 | * @return the return code of the indicated process, or 813 | * ERROR_NO_SUCH_PROCESS if the pid is invalid. 814 | */ 815 | static public int joinOne(int pid) { 816 | Launcher l; 817 | synchronized (pidMap) { 818 | l = pidMap.remove(pid); 819 | } 820 | if (l == null) { 821 | return ERROR_NO_SUCH_PROCESS; 822 | } 823 | try { 824 | l.join(); 825 | } catch (InterruptedException e) { 826 | out.printf("Kernel: join: %s\n", e); 827 | } 828 | return l.returnCode; 829 | } // Launcher.joinOne(int) 830 | 831 | /** Removes this Launcher from the set of all active Launchers. */ 832 | public void delete() { 833 | synchronized (pidMap) { 834 | pidMap.remove(pid); 835 | } 836 | } // Launcher.delete() 837 | } // class Launcher 838 | } // class Kernel 839 | -------------------------------------------------------------------------------- /src/minikernel/Library.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | import main.*; 9 | 10 | /* $Id: Library.java.src,v 1.4 2007/04/25 14:20:12 solomon Exp $ */ 11 | import static java.lang.System.*; 12 | 13 | /** Convenience calls for using the Kernel. 14 | * Each function in this class makes a system call. Sometimes, the arguments 15 | * are manipulated to make their user representation more convenient. 16 | * Note that this class contains only static methods. 17 | * All methods return integers. Negative return values are error codes. 18 | * Some methods return positive values; others simply return 0 to mean "ok". 19 | * 20 | * @see Kernel 21 | */ 22 | public class Library { 23 | /** This private constructor ensures that no instances of Library are 24 | * ever created. 25 | */ 26 | private Library() {} 27 | 28 | /** A table of error messages corresponding to Kernel error return codes. 29 | * This table should be indexed by the negative of rc, where 30 | *

 31 |      *          rc = Kernel.interrupt(Kernel.INTERRUPT_USER, ... )
 32 |      * 
33 | * and rc is less than 0. 34 | */ 35 | public static final String[] errorMessage = { 36 | "OK", // 0 37 | "Invalid argument", // ERROR_BAD_ARGUMENT = -1 38 | "No such class", // ERROR_NO_CLASS = -2 39 | "Class has no main method", // ERROR_NO_MAIN = -3 40 | "Command aborted", // ERROR_BAD_COMMAND = -4 41 | "Argument out of range", // ERROR_OUT_OF_RANGE = -5 42 | "End of file on console input", // ERROR_END_OF_FILE = -6 43 | "I/O error on console input", // ERROR_IO = -7 44 | "Exception in user program", // ERROR_IN_CHILD = -8 45 | "No such process" // ERROR_NO_SUCH_PROCESS = -9 46 | }; 47 | 48 | /** Performs SYSCALL_OUTPUT. 49 | * Displays text on the console. 50 | * @param s a String to display 51 | * @return zero 52 | */ 53 | public static int output(String s) { 54 | return Kernel.interrupt(Kernel.INTERRUPT_USER, 55 | Kernel.SYSCALL_OUTPUT, 0, s, null, null); 56 | } // output 57 | 58 | /** Performs SYSCALL_INPUT. 59 | * Waits for the user to type some text and hit [return]. 60 | * The input line is returned in the supplied StringBuffer 61 | * @param result a place to put the result 62 | * @return zero on success, or one of the error codes Kernel.END_OF_FILE or 63 | * Kernel.ERROR_IO. 64 | */ 65 | public static int input(StringBuffer result) { 66 | result.setLength(0); 67 | return Kernel.interrupt(Kernel.INTERRUPT_USER, 68 | Kernel.SYSCALL_INPUT, 0, result, null, null); 69 | } // input 70 | 71 | /** Performs SYSCALL_EXEC. 72 | * Launches the named program, and lets it run in parallel 73 | * to the current program. 74 | * @param command The name of a Java class to execute. 75 | * @param args The arguments to give the new program 76 | * @return a non-negative process id, or ERROR_BAD_COMMAND. 77 | */ 78 | public static int exec(String command, String args[]) { 79 | return Kernel.interrupt(Kernel.INTERRUPT_USER, 80 | Kernel.SYSCALL_EXEC, 0, command, args, null); 81 | } // exec 82 | 83 | /** Performs SYSCALL_JOIN. 84 | * Waits for a process to terminate 85 | * @param pid a process id returned by a previous call to exec. 86 | * @return zero or ERROR_NO_SUCH_PROCESS 87 | */ 88 | public static int join(int pid) { 89 | return Kernel.interrupt(Kernel.INTERRUPT_USER, 90 | Kernel.SYSCALL_JOIN, pid, null, null, null); 91 | } // join 92 | 93 | //************Code added by Brett Duncan*********************// 94 | /** 95 | * Gets block size of the disk. 96 | * @return The block size of the disk. 97 | */ 98 | public static int getBlockSizeOfDisk() { 99 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_GET_BLOCK_SIZE, 0, null, null, null); 100 | } 101 | //************End code added by Brett Duncan*********************// 102 | 103 | //XXX: Implementing methods 104 | 105 | /** Formats the disk. If the disk is already formatted, this system call 106 | * will destroy all data on it. 107 | * @return 0 on success and -1 on failure. 108 | */ 109 | public static int format() { 110 | // err.println("format system call not implemented yet"); 111 | // return -1; 112 | 113 | //************Code added by Brett Duncan*********************// 114 | 115 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_FORMAT, 0, null, null, null); 116 | 117 | //************End code added by Brett Duncan*********************// 118 | 119 | } // format(int) 120 | 121 | /** Changes the current working directory. 122 | * @param pathname the name of the directory to go to. If it is a relative 123 | * pathname (does not start with '/'), it is relative to the current 124 | * working directory. 125 | * @return 0 on success and -1 on failure. 126 | */ 127 | public static int chdir(String pathname) { 128 | err.println("chdir system call not implemented yet"); 129 | return -1; 130 | } // chdir(String) 131 | 132 | /** Creates a new "ordinary" file. 133 | * @param pathname the name of the new file being created. 134 | * @return 0 on success and -1 on failure. 135 | */ 136 | public static int create(String pathname) { 137 | // err.println("create system call not implemented yet"); 138 | // return -1; 139 | 140 | //************Code added by Brett Duncan*********************// 141 | 142 | return Kernel.interrupt( 143 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_CREATE, 0, null, null, pathname.getBytes()); 144 | //************End code added by Brett Duncan*********************// 145 | 146 | } // create(String) 147 | 148 | /** Reads from a file. 149 | * @param pathname the name of the file to read from. 150 | * @param buffer the destination for the data. 151 | * @return 0 on success and -1 on failure. 152 | */ 153 | public static int read(String pathname, byte[] buffer) { 154 | 155 | // err.println("read system call not implemented yet"); 156 | // return -1; 157 | 158 | //************Code added by Brett Duncan*********************// 159 | return Kernel.interrupt( 160 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_READ, 0, null, null, pathname.getBytes()); 161 | //************End code added by Brett Duncan*********************// 162 | 163 | } // read(String, byte[]) 164 | 165 | /** Writes to a file. 166 | * @param pathname the name of the file to write to. 167 | * @param buffer the source of the data. 168 | * @return 0 on success and -1 on failure. 169 | */ 170 | public static int write(String pathname, byte[] buffer) { 171 | // err.println("write system call not implemented yet"); 172 | //************Code added by Brett Duncan*********************// 173 | return Kernel.interrupt( 174 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_WRITE, 0, pathname, null, buffer); 175 | //************End code added by Brett Duncan*********************// 176 | // return -1; 177 | } // write(String, byte[]) 178 | 179 | /** Deletes an "ordinary" file. 180 | * @param pathname the name of the file to delete. 181 | * @return 0 on success and -1 on failure. 182 | */ 183 | public static int delete(String pathname) { 184 | return Kernel.interrupt( 185 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_DELETE, 0, pathname, null, null); 186 | // return -1; 187 | } // delete(String) 188 | 189 | /** Creates an empty directory. 190 | * @param pathname the name of the new directory being created 191 | * @return 0 on success and -1 on failure. 192 | */ 193 | public static int mkdir(String pathname) { 194 | err.println("mkdir system call not implemented yet"); 195 | return -1; 196 | } // mkdir(String) 197 | 198 | /** Removes a directory. The directory must be empty. 199 | * @param pathname the name of the directory to remove. 200 | * @return 0 on success and -1 on failure. 201 | */ 202 | public static int rmdir(String pathname) { 203 | err.println("rmdir system call not implemented yet"); 204 | return -1; 205 | } // rmdir(String) 206 | 207 | /** Creates a symbolic link. 208 | * @param oldName a pathname that will be target of the symlink. It need 209 | * not exist. 210 | * @param newName the name of the new symlink. 211 | * @return 0 on success and -1 on failure. 212 | */ 213 | public static int symlink(String oldName, String newName) { 214 | err.println("symlink system call not implemented yet"); 215 | return -1; 216 | } // symlink(String,String) 217 | 218 | /** Reads the contents of a symbolic link. 219 | * @param pathname the name of the symbolic link. 220 | * @param buffer the destination for its pathname. 221 | * @return 0 on success and -1 on failure. 222 | */ 223 | public static int readlink(String pathname, byte[] buffer) { 224 | err.println("readlink system call not implemented yet"); 225 | return -1; 226 | } // readlink(String, byte[]) 227 | 228 | /** Reads the contents of a directory. 229 | * @param pathname the name of the directory. 230 | * @param buffer the destination for its contents. 231 | * @return 0 on success and -1 on failure. 232 | */ 233 | public static int readdir(String pathname, byte[] buffer) { 234 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_READDIR, 235 | 0, null, null, null); 236 | // return -1; 237 | } // readdir(String, byte[]) 238 | 239 | //************Code added by Brett Duncan*********************// 240 | public static void shutdown() { 241 | Kernel.interrupt( 242 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_SHUTDOWN, 0, null, null, null); 243 | } 244 | //************End code added by Brett Duncan*********************// 245 | 246 | } // Library 247 | -------------------------------------------------------------------------------- /src/minikernel/Makefile.sdx: -------------------------------------------------------------------------------- 1 | SRC = Boot.java Disk.java FastDisk.java FileTester.java Kernel.java Library.java FileSys.java 2 | 3 | all: compile run 4 | 5 | compile: 6 | javac -Xlint:all $(SRC) 7 | 8 | run: 9 | java -enableassertions Boot 10 FastDisk 100 FileTester test1.script 10 | 11 | clean: 12 | $(RM) *.class DISK 13 | -------------------------------------------------------------------------------- /src/minikernel/MyShell.java: -------------------------------------------------------------------------------- 1 | //package minikernel; 2 | // 3 | //import java.util.Scanner; 4 | //import minikernel.Disk; 5 | //import minikernel.FastDisk; 6 | // 7 | //public class MyShell { 8 | // 9 | // public static FastDisk disk; 10 | // 11 | // public static void main(String[] args) { 12 | // // TODO Auto-generated method stub 13 | //// System.out.println("Hello, world!"); 14 | //// disk = new FastDisk(100); 15 | // 16 | // //Outline of your main method: 17 | // 18 | // /* Loop through commands on command line */ 19 | // try { 20 | // while (true) { 21 | // /* Display your Shell cursor */ 22 | // System.out.print("> "); 23 | // 24 | // /* Get new command line input */ 25 | // Scanner s = new Scanner(System.in); 26 | // String input = s.nextLine(); 27 | // 28 | // /* Break up commands and store them in an array using '&' as delimeter */ 29 | // String commands[] = input.split("&"); 30 | // 31 | // /* Create a thread array for each command detected */ 32 | // CommandExecutorThread commandExecutorThread[] = new CommandExecutorThread[commands.length]; 33 | // 34 | // /* Loop to allow a new thread per command to be created */ 35 | //// for (String command : commands) { 36 | // for (int i = 0; i < commands.length; i++) { 37 | // commandExecutorThread[i] = new CommandExecutorThread(i, commands[i].trim()); 38 | // commandExecutorThread[i].start(); 39 | // } 40 | // 41 | // /* If it is EXIT command then close your Shell by executing System.exit(0)*/ 42 | // 43 | // /* Add new thread to the thread array and run it */ 44 | // /* Join the threads */ 45 | // for (int i = 0; i < commandExecutorThread.length; i++) { 46 | // commandExecutorThread[i].join(); 47 | // } 48 | // } //while 49 | // 50 | // } /* Check for a keyboard interupt or other non-serious system problem */ 51 | // 52 | // catch (Exception e) { 53 | // System.out.println("\n\nInterrupt was detected. my Shell is closing."); 54 | //// e.printStackTrace(); 55 | // System.exit(0); 56 | // } 57 | // 58 | // } 59 | // 60 | //} 61 | // 62 | //class CommandExecutorThread extends Thread { 63 | // 64 | // private int myID = 0; 65 | // private String command; 66 | // 67 | // public CommandExecutorThread(int myID, String command) { 68 | // this.myID = myID; 69 | // this.command = command; 70 | // } 71 | // 72 | // public void run() { 73 | // System.out.println("myID = " + myID + ". Running command " + command); 74 | // 75 | // switch (command) { 76 | // case "format": 77 | // break; 78 | // } 79 | // 80 | // } 81 | // 82 | //} 83 | -------------------------------------------------------------------------------- /test1.script: -------------------------------------------------------------------------------- 1 | //************** simple test: format, create, write, read, readdir 2 | format 3 | pwd 4 | create /junk 5 | ls / 6 | write /junk this is file junk 7 | read /junk 8 | ls . 9 | read junk 10 | writeln junk 11 | two 12 | lines 13 | . 14 | read junk 15 | //*************** mkdir, chdir, symlink, readlink 16 | mkdir dir1 17 | mkdir dir1/subdir 18 | ls / 19 | create dir1/subdir/foo 20 | cd dir1 21 | pwd 22 | ls subdir 23 | cd subdir 24 | pwd 25 | cd .. 26 | pwd 27 | writeln subdir/foo 28 | This is foo 29 | . 30 | read subdir/foo 31 | // Absolute symlink 32 | ln /dir1/subdir/foo link1 33 | readlink link1 34 | read link1 35 | readlink /dir1/link1 36 | read /dir1/link1 37 | // Relative symlink 38 | ln ../dir1 link2 39 | readlink link2 40 | ls ../dir1 41 | ls link2 42 | ls link2/subdir 43 | read link2/subdir/foo 44 | read /dir1/link2/subdir/foo 45 | //**************** rmdir delete 46 | ls . 47 | rmdir subdir 48 | rm subdir/foo 49 | rmdir subdir 50 | rm link1 51 | ls 52 | pwd 53 | //**************** Examples from the FAQ 54 | 55 | //Q9 56 | format 57 | mkdir /dir 58 | cd /dir 59 | pwd 60 | rmdir /dir 61 | cd .. 62 | rmdir /dir 63 | 64 | //Q10 65 | format 66 | mkdir /foo 67 | create /foo/bar 68 | write /foo/bar This is file /foo/bar 69 | read /foo/bar 70 | read foo/bar 71 | read foo/bar/ 72 | read foo//bar 73 | read //foo///bar//// 74 | 75 | //Q11 76 | format 77 | mkdir /tmp 78 | cd /tmp 79 | ln /tmp/bad bad 80 | cd bad 81 | rm bad 82 | --------------------------------------------------------------------------------