Exercise
In this exercise, we study how user program is loaded into the process’ address
space. It will be helpful to the implementation of the multi-programming
project.
1 private boolean load(String name, String[] args) {
2 Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
3
4 OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
5 if (executable == null) {
6 Lib.debug(dbgProcess, "\topen failed");
7 return false;
8 }
9
10 try {
11 coff = new Coff(executable);
12 } catch (EOFException e) {
13 executable.close();
14 Lib.debug(dbgProcess, "\tcoff load failed");
15 return false;
16 }
17
18 // make sure the sections are contiguous and start at page 0
19 numPages = 0;
20 for (int s = 0; s < coff.getNumSections(); s++) {
21 CoffSection section = coff.getSection(s);
22 if (section.getFirstVPN() != numPages) {
23 coff.close();
24 Lib.debug(dbgProcess, "\tfragmented executable");
25 return false;
26 }
27 numPages += section.getLength();
28 }
29
30 // make sure the argv array will fit in one page
31 byte[][] argv = new byte[args.length][];
32 int argsSize = 0;
33 for (int i = 0; i < args.length; i++) {
34 argv[i] = args[i].getBytes();
35 // 4 bytes for argv[] pointer; then string plus one for null byte
36 argsSize += 4 + argv[i].length + 1;
37 }
38 if (argsSize > pageSize) {
39 coff.close();
40 Lib.debug(dbgProcess, "\targuments too long");
41 return false;
42 }
43
44 // program counter initially points at the program entry point
45 initialPC = coff.getEntryPoint();
46
47 // next comes the stack; stack pointer initially points to top of it
48 numPages += stackPages;
49 initialSP = numPages ⋆ pageSize;
50
51 // and finally reserve 1 page for arguments
52 numPages++;
53
54 if (!loadSections())
55 return false;
56
57 // store arguments in last page
58 int entryOffset = (numPages - 1) ⋆ pageSize;
59 int stringOffset = entryOffset + args.length ⋆ 4;
60
61 this.argc = args.length;
62 this.argv = entryOffset;
63
64 for (int i = 0; i < argv.length; i++) {
65 byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
66 Lib.assert(writeVirtualMemory(entryOffset, stringOffsetBytes) == 4);
67 entryOffset += 4;
68 Lib.assert(writeVirtualMemory(stringOffset, argv[i]) == argv[i].length);
69 stringOffset += argv[i].length;
70 Lib.assert(writeVirtualMemory(stringOffset, new byte[] { 0 }) == 1);
71 stringOffset += 1;
72 }
73
74 return true;
75 }
Recall that UserProcess.load() load the executable with the specified name
into the user process. In Line 4 – 8, it first opens the executable from the
stubFileSystem. Note that ThreadedKernel.fileSystem.open does not
invoke any Nachos system calls, instead, it utilizes the file IO routines from
Java.
The Coff constructor takes one argument, an OpenFile referring to the MIPS
binary file. If there is any error parsing the headers of the specified binary, an
EOFException is thrown. Note that if this constructor succeeds, the file belongs to
the Coff object; it should not be closed or accessed anymore, except through Coff
operations.
There are four Coff methods that operate on the Coff object:
- getNumSections() returns the number of sections in this binary.
- getSection() takes a section number, between 0 and getNumSections()
- 1, and returns a CoffSection object representing the section. This
class is described below.
- getEntryPoint() returns the value with which to initialize the program
counter.
- close() releases any resources allocated by the loader. This includes
closing the file passed to the constructor.
The CoffSection class allows Nachos to access a single section within a COFF
executable. Note that while the MIPS cross-compiler generates a variety of sections,
the only important distinction to the Nachos kernel is that some sections are
read-only (i.e. the program should never write to any byte in the section), while some
sections are read-write (i.e. non-const data). There are four methods for accessing
COFF sections:
- getFirstVPN() returns the first virtual page number occupied by the
section.
- getLength() returns the number of pages occupied by the section.
This section therefore occupies pages getFirstVPN() through
getFirstVPN() + getLength() - 1. Sections should never overlap.
- isReadOnly() returns true if and only if the section is read-only (i.e. it
only contains code or constant data).
- loadPage() reads a page of the section into main memory. It takes two
arguments, the page within the section to load (in the range 0 through
getLength() - 1) and the physical page of memory to write.
Line 20 - 28 read each section of the COFF executable and ensures the sections
do not overlap. Line 31 – 38 extract the arguments of the user programs and check if
the argument array fits in one page. Line 44 sets the program pointer to point at the
program entry point. Line 49 sets the stack point to the top of the stack. Line 54 – 55
load the COFF sections into memory.
Finally, the arguments are then stored at the last page in Line 57 – 72.
Questions:
- What do the variables this.argc and this.argv?
- In Line 64 – 72, how exactly are the arguments stored? [Hints: not all
arguments are of the same length.]
Next, we run the Nachos in debug mode with config file in
nachos/proj2 .
The shell program is halt.coff, which is compiled from halt.c.
1 #include "syscall.h"
2
3 int main()
4 {
5 halt();
6 /⋆ not reached ⋆/
7 }
We can see from the source code halt.c that it does nothing but invokes the system
call halt(). Set an appropriate breakpoints in UserProcess and answer the following
questions.
Questions
- How many sections are there in halt.coff? What are they?
- How many pages do the sections occupy?
- How many page does the stack occupy?
- What are the values of the initial PC and SP?
- Change the shell program to test/matmult.coff. Answer the above
questions when Dim = 20 and Dim = 2000.