/** * Handle a syscall exception. Called by <tt>handleException()</tt>. The <i>syscall</i> argument * identifies which syscall the user executed: * * <table> * <tr><td>syscall#</td><td>syscall prototype</td></tr> * <tr><td>0</td><td><tt>void halt();</tt></td></tr> * <tr><td>1</td><td><tt>void exit(int status);</tt></td></tr> * <tr><td>2</td><td><tt>int exec(char *name, int argc, char **argv); * </tt></td></tr> * <tr><td>3</td><td><tt>int join(int pid, int *status);</tt></td></tr> * <tr><td>4</td><td><tt>int creat(char *name);</tt></td></tr> * <tr><td>5</td><td><tt>int open(char *name);</tt></td></tr> * <tr><td>6</td><td><tt>int read(int fd, char *buffer, int size); * </tt></td></tr> * <tr><td>7</td><td><tt>int write(int fd, char *buffer, int size); * </tt></td></tr> * <tr><td>8</td><td><tt>int close(int fd);</tt></td></tr> * <tr><td>9</td><td><tt>int unlink(char *name);</tt></td></tr> * </table> * * @param syscall the syscall number. * @param a0 the first syscall argument. * @param a1 the second syscall argument. * @param a2 the third syscall argument. * @param a3 the fourth syscall argument. * @return the value to be returned to the user. */ public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) { switch (syscall) { case syscallHalt: return handleHalt(); case syscallCreate: return handleCreate(a0); case syscallOpen: return handleOpen(a0); case syscallRead: return handleRead(a0, a1, a2); case syscallWrite: return handleWrite(a0, a1, a2); case syscallClose: return handleClose(a0); case syscallUnlink: return handleUnlink(a0); case syscallExit: handleExit(a0); case syscallExec: return handleExec(a0, a1, a2); case syscallJoin: return handleJoin(a0, a1); default: Lib.debug(dbgProcess, "Unknown syscall " + syscall); Lib.assertNotReached("Unknown system call!"); } return 0; }
private int handleJoin(int pid, int status) { if (pid < 0 || status < 0) { return -1; } // get the child process from our hashmap childProcess childData; if (map.containsKey(pid)) { childData = map.get(pid); } else { return -1; } // join it childData.child.thread.join(); // remove from hashmap map.remove(pid); // write the exit # to the address status if (childData.status != -999) { byte exitStatus[] = new byte[4]; exitStatus = Lib.bytesFromInt(childData.status); int byteTransfered = writeVirtualMemory(status, exitStatus); if (byteTransfered == 4) { return 1; } else { return 0; } } return 0; }
/** * Transfer data from the specified array to this process's virtual memory. This method handles * address translation details. This method must <i>not</i> destroy the current process if an * error occurs, but instead should return the number of bytes successfully copied (or zero if no * data could be copied). * * @param vaddr the first byte of virtual memory to write. * @param data the array containing the data to transfer. * @param offset the first byte to transfer from the array. * @param length the number of bytes to transfer from the array to virtual memory. * @return the number of bytes successfully transferred. */ public int writeVirtualMemory(int vaddr, byte[] data, int offset, int length) { Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length); byte[] memory = Machine.processor().getMemory(); int transferred = 0; while (length > 0 && offset < data.length) { int addrOffset = vaddr % 1024; int virtualPage = vaddr / 1024; if (virtualPage >= pageTable.length || virtualPage < 0) { break; } TranslationEntry pte = pageTable[virtualPage]; if (!pte.valid || pte.readOnly) { break; } pte.used = true; pte.dirty = true; int physPage = pte.ppn; int physAddr = physPage * 1024 + addrOffset; int transferLength = Math.min(data.length - offset, Math.min(length, 1024 - addrOffset)); System.arraycopy(data, offset, memory, physAddr, transferLength); vaddr += transferLength; offset += transferLength; length -= transferLength; transferred += transferLength; } return transferred; }
/** * Initializes page tables for this process so that the executable can be demand-paged. * * @return <tt>true</tt> if successful. */ protected boolean loadSections() { UserKernel.memoryLock.acquire(); pageTable = new TranslationEntry[numPages]; for (int vpn = 0; vpn < numPages; vpn++) { pageTable[vpn] = new TranslationEntry(vpn, -1, false, false, false, false); } UserKernel.memoryLock.release(); // load sections for (int s = 0; s < coff.getNumSections(); s++) { CoffSection section = coff.getSection(s); Lib.debug( dbgProcess, "\tinitializing " + section.getName() + " section (" + section.getLength() + " pages)"); for (int i = 0; i < section.getLength(); i++) { int vpn = section.getFirstVPN() + i; pageTable[vpn].readOnly = section.isReadOnly(); // section.loadPage(i, pinVirtualPage(vpn, false)); } } return true; }
/** * Allocates memory for this process, and loads the COFF sections into memory. If this returns * successfully, the process will definitely be run (this is the last step in process * initialization that can fail). * * @return <tt>true</tt> if the sections were successfully loaded. */ protected boolean loadSections() { if (numPages > Machine.processor().getNumPhysPages()) { coff.close(); Lib.debug(dbgProcess, "\tinsufficient physical memory"); return false; } pageTable = new TranslationEntry[numPages]; for (int i = 0; i < numPages; i++) { int physPage = UserKernel.allocatePage(); if (physPage < 0) { Lib.debug(dbgProcess, "\tunable to allocate pages; tried " + numPages + ", did " + i); for (int j = 0; j < i; j++) { if (pageTable[j].valid) { UserKernel.deallocatePage(pageTable[j].ppn); pageTable[j].valid = false; } } coff.close(); return false; } pageTable[i] = new TranslationEntry(i, physPage, true, false, false, false); } // load sections for (int s = 0; s < coff.getNumSections(); s++) { CoffSection section = coff.getSection(s); Lib.debug( dbgProcess, "\tinitializing " + section.getName() + " section (" + section.getLength() + " pages)"); for (int i = 0; i < section.getLength(); i++) { int vpn = section.getFirstVPN() + i; // for now, just assume virtual addresses=physical addresses int ppn = pageTable[vpn].ppn; section.loadPage(i, ppn); if (section.isReadOnly()) { pageTable[vpn].readOnly = true; } } } coff.close(); return true; }
/** Handle the halt() system call. */ private int handleHalt() { if (this.process_id != ROOT) { return 0; } Machine.halt(); Lib.assertNotReached("Machine.halt() did not halt machine!"); return 0; }
/** * Handle a user exception. Called by <tt>UserKernel.exceptionHandler()</tt>. The <i>cause</i> * argument identifies which exception occurred; see the <tt>Processor.exceptionZZZ</tt> * constants. * * @param cause the user exception that occurred. */ public void handleException(int cause) { Processor processor = Machine.processor(); switch (cause) { case Processor.exceptionSyscall: int result = handleSyscall( processor.readRegister(Processor.regV0), processor.readRegister(Processor.regA0), processor.readRegister(Processor.regA1), processor.readRegister(Processor.regA2), processor.readRegister(Processor.regA3)); processor.writeRegister(Processor.regV0, result); processor.advancePC(); break; default: Lib.debug(dbgProcess, "Unexpected exception: " + Processor.exceptionNames[cause]); handleExit(-1); Lib.assertNotReached("Unexpected exception"); } }
/** * Read a null-terminated string from this process's virtual memory. Read at most <tt>maxLength + * 1</tt> bytes from the specified address, search for the null terminator, and convert it to a * <tt>java.lang.String</tt>, without including the null terminator. If no null terminator is * found, returns <tt>null</tt>. * * @param vaddr the starting virtual address of the null-terminated string. * @param maxLength the maximum number of characters in the string, not including the null * terminator. * @return the string read, or <tt>null</tt> if no null terminator was found. */ public String readVirtualMemoryString(int vaddr, int maxLength) { Lib.assertTrue(maxLength >= 0); byte[] bytes = new byte[maxLength + 1]; int bytesRead = readVirtualMemory(vaddr, bytes); for (int length = 0; length < bytesRead; length++) { if (bytes[length] == 0) return new String(bytes, 0, length); } return null; }
private void handleExit(int status) { if (myChildProcess != null) { myChildProcess.status = status; } // close all the opened files for (int i = 0; i < 16; i++) { handleClose(i); } // part2 implemented this.unloadSections(); if (this.process_id == ROOT) { Kernel.kernel.terminate(); } else { KThread.finish(); Lib.assertNotReached(); } }
/* * if the input arguments are negative, return -1 * @return the child process Id */ private int handleExec(int file, int argc, int argv) { if (file < 0 || argc < 0 || argv < 0) { return -1; } String fileName = readVirtualMemoryString(file, 256); if (fileName == null) { return -1; } String args[] = new String[argc]; int byteReceived, argAddress; byte temp[] = new byte[4]; for (int i = 0; i < argc; i++) { byteReceived = readVirtualMemory(argv + i * 4, temp); if (byteReceived != 4) { return -1; } argAddress = Lib.bytesToInt(temp, 0); args[i] = readVirtualMemoryString(argAddress, 256); if (args[i] == null) { return -1; } } UserProcess child = UserProcess.newUserProcess(); childProcess newProcessData = new childProcess(child); child.myChildProcess = newProcessData; if (child.execute(fileName, args)) { map.put(child.process_id, newProcessData); return child.process_id; } return -1; }
/** * Allocate and return a new process of the correct class. The class name is specified by the * <tt>nachos.conf</tt> key <tt>Kernel.processClassName</tt>. * * @return a new process of the correct class. */ public static UserProcess newUserProcess() { return (UserProcess) Lib.constructObject(Machine.getProcessClassName()); }
/** * Load the executable with the specified name into this process, and prepare to pass it the * specified arguments. Opens the executable, reads its header information, and copies sections * and arguments into this process's virtual memory. * * @param name the name of the file containing the executable. * @param args the arguments to pass to the executable. * @return <tt>true</tt> if the executable was successfully loaded. */ private boolean load(String name, String[] args) { Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")"); OpenFile executable = ThreadedKernel.fileSystem.open(name, false); if (executable == null) { Lib.debug(dbgProcess, "\topen failed"); return false; } try { coff = new Coff(executable); } catch (EOFException e) { executable.close(); Lib.debug(dbgProcess, "\tcoff load failed"); return false; } // make sure the sections are contiguous and start at page 0 numPages = 0; for (int s = 0; s < coff.getNumSections(); s++) { CoffSection section = coff.getSection(s); if (section.getFirstVPN() != numPages) { coff.close(); Lib.debug(dbgProcess, "\tfragmented executable"); return false; } numPages += section.getLength(); } // make sure the argv array will fit in one page byte[][] argv = new byte[args.length][]; int argsSize = 0; for (int i = 0; i < args.length; i++) { argv[i] = args[i].getBytes(); // 4 bytes for argv[] pointer; then string plus one for null byte argsSize += 4 + argv[i].length + 1; } if (argsSize > pageSize) { coff.close(); Lib.debug(dbgProcess, "\targuments too long"); return false; } // program counter initially points at the program entry point initialPC = coff.getEntryPoint(); // next comes the stack; stack pointer initially points to top of it numPages += stackPages; initialSP = numPages * pageSize; // and finally reserve 1 page for arguments numPages++; if (!loadSections()) return false; // store arguments in last page int entryOffset = (numPages - 1) * pageSize; int stringOffset = entryOffset + args.length * 4; this.argc = args.length; this.argv = entryOffset; for (int i = 0; i < argv.length; i++) { byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset); Lib.assertTrue(writeVirtualMemory(entryOffset, stringOffsetBytes) == 4); entryOffset += 4; Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) == argv[i].length); stringOffset += argv[i].length; Lib.assertTrue(writeVirtualMemory(stringOffset, new byte[] {0}) == 1); stringOffset += 1; } return true; }