-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUserProcess.java
581 lines (501 loc) · 17.7 KB
/
UserProcess.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
package nachos.userprog;
import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import java.io.EOFException;
/**
* Encapsulates the state of a user process that is not contained in its
* user thread (or threads). This includes its address translation state, a
* file table, and information about the program being executed.
*
* <p>
* This class is extended by other classes to support additional functionality
* (such as additional syscalls).
*
* @see nachos.vm.VMProcess
* @see nachos.network.NetProcess
*/
public class UserProcess {
/**
* Allocate a new process.
*/
final int MAX_NAME_LENGTH = 256;
public UserProcess() {
int numPhysPages = Machine.processor().getNumPhysPages();
pageTable = new TranslationEntry[numPhysPages];
for (int i=0; i<numPhysPages; i++)
pageTable[i] = new TranslationEntry(i,i, true,false,false,false);
}
/**
* 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());
}
/**
* Execute the specified program with the specified arguments. Attempts to
* load the program, and then forks a thread to run it.
*
* @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 program was successfully executed.
*/
public boolean execute(String name, String[] args) {
if (!load(name, args))
return false;
new UThread(this).setName(name).fork();
return true;
}
/**
* Save the state of this process in preparation for a context switch.
* Called by <tt>UThread.saveState()</tt>.
*/
public void saveState() {
}
/**
* Restore the state of this process after a context switch. Called by
* <tt>UThread.restoreState()</tt>.
*/
public void restoreState() {
Machine.processor().setPageTable(pageTable);
}
/**
* 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;
}
/**
* Transfer data from this process's virtual memory to all of the specified
* array. Same as <tt>readVirtualMemory(vaddr, data, 0, data.length)</tt>.
*
* @param vaddr the first byte of virtual memory to read.
* @param data the array where the data will be stored.
* @return the number of bytes successfully transferred.
*/
public int readVirtualMemory(int vaddr, byte[] data) {
return readVirtualMemory(vaddr, data, 0, data.length);
}
/**
* Transfer data from this process's virtual memory to the specified array.
* 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 read.
* @param data the array where the data will be stored.
* @param offset the first byte to write in the array.
* @param length the number of bytes to transfer from virtual memory to
* the array.
* @return the number of bytes successfully transferred.
*/
public int readVirtualMemory(int vaddr, byte[] data, int offset,
int length) {
Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);
byte[] memory = Machine.processor().getMemory();
// for now, just assume that virtual addresses equal physical addresses
if (vaddr < 0 || vaddr >= memory.length)
return 0;
int amount = Math.min(length, memory.length-vaddr);
System.arraycopy(memory, vaddr, data, offset, amount);
return amount;
}
/**
* Transfer all data from the specified array to this process's virtual
* memory.
* Same as <tt>writeVirtualMemory(vaddr, data, 0, data.length)</tt>.
*
* @param vaddr the first byte of virtual memory to write.
* @param data the array containing the data to transfer.
* @return the number of bytes successfully transferred.
*/
public int writeVirtualMemory(int vaddr, byte[] data) {
return writeVirtualMemory(vaddr, data, 0, data.length);
}
/**
* 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();
// for now, just assume that virtual addresses equal physical addresses
if (vaddr < 0 || vaddr >= memory.length)
return 0;
int amount = Math.min(length, memory.length-vaddr);
System.arraycopy(data, offset, memory, vaddr, amount);
return amount;
}
/**
* 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;
}
/**
* 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;
}
// 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
section.loadPage(i, vpn);
}
}
return true;
}
/**
* Release any resources allocated by <tt>loadSections()</tt>.
*/
protected void unloadSections() {
}
/**
* Initialize the processor's registers in preparation for running the
* program loaded into this process. Set the PC register to point at the
* start function, set the stack pointer register to point at the top of
* the stack, set the A0 and A1 registers to argc and argv, respectively,
* and initialize all other registers to 0.
*/
public void initRegisters() {
Processor processor = Machine.processor();
// by default, everything's 0
for (int i=0; i<processor.numUserRegisters; i++)
processor.writeRegister(i, 0);
// initialize PC and SP according
processor.writeRegister(Processor.regPC, initialPC);
processor.writeRegister(Processor.regSP, initialSP);
// initialize the first two argument registers to argc and argv
processor.writeRegister(Processor.regA0, argc);
processor.writeRegister(Processor.regA1, argv);
}
/**
* Handle the halt() system call.
*/
private int handleHalt() {
Machine.halt();
Lib.assertNotReached("Machine.halt() did not halt machine!");
return 0;
}
private int creat(String name) {
int fd = 0, i;
boolean flag = false;
//find available file descriptor
for (i = 0; i < numPages; i++) {
if (pageTable[i] == null && !flag) {
fd = i;
flag = true;
}
}
//returns -1 if no file descriptors are available
if (flag) {
OpenFile file = ThreadedKernel.fileSystem.open(name, true);
//checks for invalid file name
if (file == null) {
return -1;
} else {
//pageTable[fd] = file; must bind fd to file
return fd;
}
} else {
Lib.assertNotReached("CREATE FAILED");
return -1;
}
}
private int open(String name) {
int fd = 0, i;
boolean flag = false;
//find available file descriptor
for (i = 0; i < numPages; i++) {
if (pageTable[i] == null && !flag) {
fd = i;
flag = true;
}
}
//returns -1 if no file descriptors are available
if (flag) {
OpenFile file = ThreadedKernel.fileSystem.open(name, false); //only difference from creat
//checks for invalid file name
if (file == null) {
return -1;
} else {
//pageTable[fd] = file; must bind fd to file (replace pageTable with fd table)
return fd;
}
} else {
Lib.assertNotReached("CREATE FAILED");
return -1;
}
}
private int read(int fileDescriptor, void *buffer, int count) {
Byte toRead = new Byte[count];
//Get File
OpenFile pls = new OpenFile();
Lib.assertNotReached("READ FAILED");
return 0;
}
private int write(int fileDescriptor, void *buffer, int count) {
Lib.assertNotReached("WRITE FAILED");
return 0;
}
private int close(int fd) {
//check if fd is out of bounds
if (fd < 16) { //needs a helper fn
//pageTable[fd].close(); replace pageTable with fd table
//pageTable[fd] = null; replace pageTable with fd table
return 0;
} else {
return -1;
}
}
private int unlink(String name) {
boolean flag;
flag = ThreadedKernel.fileSystem.remove(name);
if (flag) {
return 0;
} else {
return 1;
}
}
private static final int
syscallHalt = 0,
syscallExit = 1,
syscallExec = 2,
syscallJoin = 3,
syscallCreate = 4,
syscallOpen = 5,
syscallRead = 6,
syscallWrite = 7,
syscallClose = 8,
syscallUnlink = 9;
/**
* 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) {
String name;
switch (syscall) {
case syscallHalt:
return handleHalt();
case syscallExit:
break; //will most likely be replaced by a return
case syscallExec:
break; //will most likely be replaced by a return
case syscallJoin:
break; //will most likely be replaced by a return
case syscallCreate:
name = readVirtualMemoryString(a0, MAX_NAME_LENGTH);
//check for invalid file name
if (name == null) {
return -1;
} else {
return creat(name);
}
case syscallOpen:
name = readVirtualMemoryString(a0, MAX_NAME_LENGTH);
//check for invalid file name
if (name == null) {
return -1;
} else {
return open(name);
}
case syscallRead:
//return read(a0, buffer, a2); //unsure where to get buffer from
case syscallWrite:
//return write(a0, buffer, a2); //unsure where to get buffer from
case syscallClose:
return close(a0);
case syscallUnlink:
name = readVirtualMemoryString(a0, MAX_NAME_LENGTH);
//check for invalid file name
if (name == null) {
return -1;
} else {
return unlink(name);
}
default:
Lib.debug(dbgProcess, "Unknown syscall " + syscall);
Lib.assertNotReached("Unknown system call!");
}
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]);
Lib.assertNotReached("Unexpected exception");
}
}
/** The program being run by this process. */
protected Coff coff;
/** This process's page table. */
protected TranslationEntry[] pageTable;
/** The number of contiguous pages occupied by the program. */
protected int numPages;
/** The number of pages in the program's stack. */
protected final int stackPages = 8;
private int initialPC, initialSP;
private int argc, argv;
private static final int pageSize = Processor.pageSize;
private static final char dbgProcess = 'a';
}