Lab: Xv6 and Unix utilities

This lab will familiarize you with xv6 and its system calls. You will learn how to create Unix/Linux utility programs using the Unix/Linux API. The Xv6 API is a simplfied Linux API. For example, the Xv6 API has a single exec function call to execute a new program within a process, whereas, Linux has a family of exec calls (ie., execp, execl, execvp, execlp).

You will create several Unix/Linux utility programs. All of the programs have a starting .c program that you must edit. For example, there is a kittycat.c program. You must design the code, edit the program, and test that it satisfies the requirements.

Before you begin lab system calls, read Chapter 1 of the xv6 book.

In this lab, there are several questions for you to answer. Questions are in boxes with a light orange background. Write each question and its answer in your notebook. Take photo(s) of your questions/answers and submit the photo(s) on Canvas. Additionally, in the directory of your xv6-labs, create the file: answers-util.txt. You do not need to put answers in this file, but the grading script checks to see if this file exists. If it exists, I assume you have answered the questions in your notebook.

Boot xv6

You can do these labs on our CPSC server or on your own computer. If you use your own computer, have a look at the lab tools page for setup tips.

1. What does it mean to boot (which is short for bootstrap) a computer?

Fetch the git repository for the xv6 source for the lab:

$ git clone https://github.com/gustycooper/xv6-labs.git
Cloning into 'xv6-labs'...
...
$ cd xv6-labs

The repo is setup so that git checkouts the riscv branch when cloning the repo. You must switch to the util branch to conduct this lab.

$ git status
On branch riscv
Your branch is up to date with 'origin/riscv'.

nothing to commit, working tree clean
$ git checkout util

The xv6-labs repository differs slightly from the book's xv6-riscv; it mostly adds some files. If you are curious look at the git log:

$ git log

The files you will need for this and subsequent lab assignments are distributed using the Git version control system. For each of the labs you will checkout (git checkout util) a version of xv6 tailored for that lab. To learn more about Git, take a look at the Git user's manual, or, you may find this CS-oriented overview of Git useful. Git allows you to keep track of the changes you make to the code. For example, if you are finished with one of the exercises, and want to checkpoint your progress, you can commit your changes by running:

$ git commit -am 'my solution for util lab exercise 1'
Created commit 60d2135: my solution for util lab exercise 1
 1 files changed, 1 insertions(+), 0 deletions(-)
$

You can keep track of your changes by using the git diff command. Running git diff will display the changes to your code since your last commit, and git diff origin/util will display the changes relative to the initial util code. Here, origin/util is the name of the git branch with the initial code you downloaded for the class.

Build and run xv6:

2. The Makefile has many targets. Locate the target qemu for the command make qemu and explain what it does.

$ make qemu
riscv64-unknown-elf-gcc    -c -o kernel/entry.o kernel/entry.S
riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_UTIL -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
...
riscv64-unknown-elf-ld -z max-page-size=4096 -N -e main -Ttext 0 -o user/_zombie user/zombie.o user/ulib.o user/usys.o user/printf.o user/umalloc.o
riscv64-unknown-elf-objdump -S user/_zombie > user/zombie.asm
riscv64-unknown-elf-objdump -t user/_zombie | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > user/zombie.sym
mkfs/mkfs fs.img README  user/xargstest.sh 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/_grind user/_wc user/_zombie
nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 954 total 1000
balloc: first 591 blocks have been allocated
balloc: write bitmap block at sector 45
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0

xv6 kernel is booting

hart 2 starting
hart 1 starting
init: starting sh
$

If you type ls at the prompt, you should see output similar to the following:

$ ls
.              1 1 1024
..             1 1 1024
README         2 2 508
kittycat.txt   2 3 597
xargstest.sh   2 4 93
cat            2 5 32848
echo           2 6 31696
forktest       2 7 15832
grep           2 8 36224
init           2 9 32200
kill           2 10 31664
ln             2 11 31480
ls             2 12 34792
mkdir          2 13 31712
rm             2 14 31704
sh             2 15 54144
stressfs       2 16 32592
usertests      2 17 180488
grind          2 18 47536
wc             2 19 33800
zombie         2 20 31064
kittycat       2 21 32864
sleep          2 22 31208
pingpong       2 23 31224
primes         2 24 31208
find           2 25 31208
xargs          2 26 31208
console        3 27 0
These are the files that mkfs includes in the initial file system; most are programs you can run. You just ran one of them: ls. Several of the files are templates that you must design, edit, and test as part of this lab. Here are a couple more commands for you to enter.
$ echo kittycat
kittycat
$ cat kittycat.txt
This is the text in the file kittycat.

Soft kitty, warm kitty, 
little ball of fur! 
Happy kitty, sleepy kitty, 
purr purr purr!
Xv6 kitty, Xv6 kitty,
grrr, grrr, grrr!

And the cat's in the cradle and the silver spoon
Little boy blue and the man in the moon
"When you coming home, dad?" "I don't know when"
But we'll get together then
You know we'll have a good time then

A long tailed cat sitting by the old rocking chair
Now he don't realize that there's a danger there
But he don't care no rock 'n' roll chair is gonna boogie on his day
'Cause when his tail took a low down, syncopate, yeah

xv6 has no ps command, but, if you type Ctrl-p, the kernel will print information about each process. If you try it now, you'll see two lines: one for init, and one for sh.

To quit qemu type: Ctrl-a x (press Ctrl and a at the same time, followed by x).

Grading and hand-in procedure

You can run make grade to test your solutions with the grading program. I will use the same grading program to assign your lab submission a grade. Separately, we will also have check-off meetings for labs (see Grading policy).

The lab code comes with GNU Make rules to make submission easier. After committing your final changes to the lab, type HERE::make handin to submit your lab. For detailed instructions on how to submit see below.

kittycat

Implement the UNIX program kittycat for xv6; your kittycat mimics the UNIX cat program, but it only concatenates the file kittycat.txt to standard output. Invoking kittycat with an argument results in a error. See shell commands below for sample kittycat sessions. Your solution should be in the file user/kittycat.c.

Some Hints

Run the program from the xv6 shell:

      $ make qemu
      ...
      init: starting sh
      $ kittycat
      This is the text in the file kittycat.

      Soft kitty, warm kitty, 
      little ball of fur! 
      Happy kitty, sleepy kitty, 
      purr purr purr!
      Xv6 kitty, Xv6 kitty,
      grrr, grrr, grrr!

      And the cat's in the cradle and the silver spoon
      Little boy blue and the man in the moon
      "When you coming home, dad?" "I don't know when"
      But we'll get together then
      You know we'll have a good time then

      A long tailed cat sitting by the old rocking chair
      Now he don't realize that there's a danger there
      But he don't care no rock 'n' roll chair is gonna boogie on his day
      'Cause when his tail took a low down, syncopate, yeah

      $ kittycat gusty
      kittycat: No args allowed
    

3. What are three songs and artists in the kittycat printout? For the "Soft kitty" song, instead of an artist, which in which TV show was it used and by which character?

Testing

Your solution is correct if your program generates the output shown above. Run make grade to see if you indeed pass the kittycat tests.

Note that make grade runs all tests, including the ones for the assignments below. If you want to run the grade tests for one assignment, type:

     $ ./grade-lab-util kittycat
   
This will run the grade tests that match "kittycat". Or, you can type:
     $ make GRADEFLAGS=kittycat grade
   
which does the same.

sleep

Implement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the file user/sleep.c.

Some Hints

Testing

Run the program from the xv6 shell:

      $ make qemu
      ...
      init: starting sh
      $ sleep 10
      (nothing happens for a little while)
      $
    

Your solution is correct if your program pauses when run as shown above. Run make grade to see if you indeed pass the sleep tests.

Note that make grade runs all tests, including the ones for the assignments below. If you want to run the grade tests for one assignment, type:

     $ ./grade-lab-util sleep
   
This will run the grade tests that match "sleep". Or, you can type:
     $ make GRADEFLAGS=sleep grade
   
which does the same.

4. Use the process state diagram to explain how you think the OS implements a sleeping process.

pingpong

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 should send a byte to the child; the child should print "<pid>: received ping", where <pid> is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print "<pid>: received pong", and exit. Your solution should be in the file user/pingpong.c.

Some Hints

Testing

Run the program from the xv6 shell and it should produce the following output:

    $ make qemu
    ...
    init: starting sh
    $ pingpong
    4: received ping
    3: received pong
    $
  

Your solution is correct if your program exchanges a byte between two processes and produces output as shown above.

5. Explain in your own words what a Xv6/Linux pipe is - not a pipe used for smoking.

primes

Write a concurrent version of prime sieve using pipes. This idea is due to Doug McIlroy, inventor of Unix pipes. The picture halfway down this page and the surrounding text explain how to do it. Your solution should be in the file user/primes.c.

Your goal is to use pipe and fork 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.

Some Hints

Testing

Your solution is correct if it implements a pipe-based sieve and produces the following output:

    $ make qemu
    ...
    init: starting sh
    $ primes
    prime 2
    prime 3
    prime 5
    prime 7
    prime 11
    prime 13
    prime 17
    prime 19
    prime 23
    prime 29
    prime 31
    $
  

6. Do you think there is a largest prime number such that it is the last prime number? What are the number of digits in the largest prime number discovers so far? When was it discovered?

find

Write a simple version of the UNIX find program: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c. The find program has two arguments - the path in which to begin searching and the filename for which to search. For example,
$ find . b
searches all subdirectories of the current directory for the filenamed b.

Some Hints

Testing

Your solution is correct if produces the following output (when the file system contains the files b, a/b and a/aa/b):

    $ make qemu
    ...
    init: starting sh
    $ echo > b
    $ mkdir a
    $ echo > a/b
    $ mkdir a/aa
    $ echo > a/aa/b
    $ find . b
    ./b
    ./a/b
    ./a/aa/b
    $
  

7. What does the Linux command $ echo > b do?

xargs

Write a simple version of the UNIX xargs program: its arguments describe a command to run, it reads lines from the standard input, and it runs the command for each line, appending the line to the command's arguments. Your solution should be in the file user/xargs.c.

The following example illustrates xarg's behavior:
    $ echo hello too | xargs echo bye
    bye hello too
    $
  
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".

Please note that xargs on UNIX makes an optimization where it will feed more than argument to the command at a time. We don't expect you to make this optimization. To make xargs on UNIX behave the way we want it to for this lab, please run it with the -n option set to 1. For instance

    $ (echo 1 ; echo 2) | xargs -n 1 echo
    1
    2
    $
  

Some Hints

High-level Design

Process args passed to xargs
Make sure it has at least one argument. 
For example, $ xargs wc
Create a char* array (e.g. xargv) for arguments to the exec call
Put the first arg passed to xargs in xargv[0]
The read loop will put values in xargv[1], xargv[2], ...
READLOOP:
Read 1 character from standard input
If the read returns 0, you exit READLOOP
If the character read is \n 
  if (fork() == 0)
    call exec with xargv
  else
    wait
else if the character read is a space
  move to next element of xargv
else
  concatenate character onto string to put into xargv
GOTO READLOOP

Testing

To test your solution for xargs, run the seqence of commands provided in the hints section. The shell script xargstest.sh has been provided. Its contents are the following.

mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello

The program sh is the shell. You can use input redirection to redirect the contents xargstest.sh to the shell as shown next. You output should match this.

  $ make qemu
  ...
  init: starting sh
  $ sh < xargstest.sh
  $ $ $ $ $ $ hello
  hello
  hello
  $ $
  
You may have to go back and fix bugs in your find program. The output has many $ because the xv6 shell doesn't realize it is processing commands from a file instead of from the console, and prints a $ for each command in the file.

8. What I find amazing is that the basic Unix/Linux API has not changed in 60 years. Now that you have written programs with pipes, forks, execs, and I/O redirection, what do you think about the Linux API.

Submit the lab

This completes the lab. Make sure you pass all of the make grade tests. Read Lab Submissions for instructions on how to submit your lab.