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
|
<html>
<head>
<title>Lab: xv6</title>
<link rel="stylesheet" href="homework.css" type="text/css" />
</head>
<body>
<h1>Lab: xv6</h1>
This lab makes you familiar with xv6 and its system calls.
<h2>Boot xv6</h2>
<p>Login to Athena (e.g., ssh -X athena.dialup.mit.edu) and attach the course
locker: (You must run this command every time you log in; or add it to your
~/.environment file.)
<pre>
$ add -f 6.828
</pre>
<p>Fetch the xv6 source:
<pre>
$ mkdir 6.828
$ cd 6.828
$ git clone git://github.com/mit-pdos/xv6-riscv.git
Cloning into 'xv6-riscv'...
...
$
</pre>
<p>XXX pointer to an update tools page
<p>Build xv6 on Athena:
<pre>
$ cd xv6-public
$ makeriscv64-linux-gnu-gcc -c -o kernel/entry.o kernel/entry.S
riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/start.o kernel/start.c
...
$ make qemu
...
mkfs/mkfs fs.img README user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_wc user/_zombie user/_cow
nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 954 total 1000
balloc: first 497 blocks have been allocated
balloc: write bitmap block at sector 45
qemu-system-riscv64 -machine virt -kernel kernel/kernel -m 3G -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
hart 0 starting
hart 2 starting
hart 1 starting
init: starting sh
$
</pre>
<p>
If you type <tt>ls</tt> at the prompt, you should output similar to the following:
<pre>
$ ls
. 1 1 1024
.. 1 1 1024
README 2 2 2181
cat 2 3 21024
echo 2 4 19776
forktest 2 5 11456
grep 2 6 24512
init 2 7 20656
kill 2 8 19856
ln 2 9 19832
ls 2 10 23280
mkdir 2 11 19952
rm 2 12 19936
sh 2 13 38632
stressfs 2 14 20912
usertests 2 15 106264
wc 2 16 22160
zombie 2 17 19376
cow 2 18 27152
console 3 19 0
</pre>
These are the programs/files that <tt>mkfs</tt> includes in the
initial file system. You just ran one of them: <tt>ls</tt>.
<h2>sleep</h2>
<p>Implement the UNIX program sleep for xv6; your sleep should pause
for a user-specified number of ticks.
<p>Some hints:
<ul>
<li>Look at some of the other programs in <tt>user/</tt> to see
how you can obtain the command-line arguments passed to a program. If the user
forgets to pass an argument, sleep should print an error message.
<li>The command-line argument is passed as a string; you can convert it to an
integer using <tt>atoi</tt> (see user/ulib.c).
<li>Use the system call <tt>sleep</tt> (see user/usys.S and kernel/sysproc.c).
<li>Make sure <tt>main</tt> calls <tt>exit()</tt> in order to exit
your program.
<li>Add the program to <tt>UPROGS</tt> in Makefile and compile
user programs by typing <tt>make fs.img</tt>.
</ul>
<p>Run the program from the xv6 shell:
<pre>
$ make qemu
...
init: starting sh
$ sleep 10
(waits for a little while)
$
</pre>
<p>Optional: write an uptime program that prints the uptime in terms
of ticks using the <tt>uptime</tt> system call.
<h2>pingpong</h2>
<p> Write a program that uses UNIX system calls to ``ping-pong'' a
byte between two processes over a pair of pipes, one for each
direction. The parent sends by writing a byte to <tt>fd[1]</tt> and
the child receives it by reading from <tt>fd[0]</tt>. After
receiving a byte from parent, the child responds with its own byte
by writing to <tt>fd[1]</tt>, which the parent then reads.
<p>Some hints:
<ul>
<li>Use <tt>pipe</tt> to create a pipe.
<li>Use <tt>fork</tt> to create a child.
<li>Use <tt>read</tt> to read from the pipe, and <tt>write</tt> to write to the pipe.
</ul>
<h2>primes</h2>
<p>Write a concurrent version of prime sieve using pipes. This idea
is due to Doug McIlroy, inventor of Unix pipes. The picture
halfway down <a href="http://swtch.com/~rsc/thread/">the page</a>
and the text surrounding it explain how to do it.
<p>Your goal is to use <tt>pipe</tt> and <tt>fork</tt> to set up
the pipeline. The first process feeds the numbers 2 through 35
into the pipeline. For each prime number, you will arrange to
create one process that reads from its left neighbor over a pipe
and writes to its right neighbor over another pipe. Since xv6 has
limited number of file descriptors and processes, the first
process can stop at 35.
<p>Some hints:
<ul>
<li>Be careful to close file descriptors that a process doesn't
need, because otherwise your program will run xv6 out of resources
before the first process reaches 35.
<li>Once the first process reach 35, you should arrange that the
pipeline terminates cleanly (Hint: read will return an end-of-file
when the write-side of the pipe is closed).
</ul>
<h2>find</h2>
<p>Write a simple version of the UNIX find program: find all the files
in a directory tree whose name matches a string. For example if the
file system contains a file <tt>a/b</tt>, then running find as
follows should produce:
<pre>
$ find . b
./a/b
$
</pre>
<p>Some hints:
<ul>
<li>Look at user/ls.c to see how to read directories.
<li>Use recursion to run find in sub-directories.
<li>Don't recurse into "." and "..".
</ul>
<p>Optional: support regular expressions in name matching. Grep has some
primitive support for regular expressions.
<h2>xargs</h2>
<p>Write a simple version of the UNIX xargs program: read lines from
standard in and run a command for each line, supplying the line as
arguments to the command. The following example illustrates xarg's
behavior:
<pre>
$ xargs echo bye
hello too
bye hello too
<ctrl-d>
$
</pre>
Note that the command here is "echo bye" and the additional
arguments are "hello too", making the command "echo bye hello too",
which outputs "bye hello too".
<p>xargs and find combine well:
<pre>
find . b | xargs grep hello
</pre>
will run "grep hello" on each file named b in the directories below ".".
<p>Some hints:
<ul>
<li>Use <tt>fork</tt> and <tt>exec</tt> system call to invoke the
command on each line of input. Use <tt>wait</tt> in the parent
to wait for the child to complete running the command.
<li>Read from stdin a character at the time until the newline
character ('\n').
<li>kernel/param.h declares MAXARG, which may be useful if you need
to declare an argv.
</ul>
<h2>Optional: modify the shell</h2>
There are endless ways in which the shell could be extended. Here are
some suggestions:
<ul>
<li>Modify the shell to support wait.
<li>Modify the shell to support lists of commands, separated by ";"
<li>Modify the shell to support sub-shells by implementing "(" and ")"
<li>Modify the shell to allow users to edit the command line
</ul>
</body>
</html>
|