Overview:
In this assignment, you will add new system calls to a toy operating system called xv6. You will implement 2 system calls:
fsetoff(int fd, int pos)
: This syscall changes the position indicator (offset) of the file as per the user input.fgetoff(int fd)
: This syscall returns the current value of the position indicator (offset) of the specified file.
Additionally, you will learn (and demonstrate) using gdb debugger with xv6.
Learning objectives:
- Gain experience using more substantial code bases written by others in which you do not need to understand every line
- Familiarize with the xv6 code base in particular
- Learn how to add a system call to xv6
- Add a user-level application that can be used within xv6
- Become familiar with a few of the data structures in xv6 (e.g., process table and file)
- Use the gdb debugger on xv6
Administrivia:
- This project is to be performed alone.
- Due Date: June 17th, at 11:59pm (4 slip days throughout course, use wisely)
- This project is to be done on the lab machinesLinks to an external site., so you can learn more about programming in C on a typical UNIX-based platform (Linux).
Details:
System call:
Here are the details about the systems calls you will be adding:
int fsetoff(int fd, int pos)
- fsetoff() syscall will take 2 arguments: open file descriptor and the position value to which the file offset should be changed.
- If fd and pos are valid, fsetoff will update the offset position parameter of the file corresponding to the input file descriptor.
- fsetoff() will return 0 on success, -1 if the file is not open, -2 if the pos parameter is invalid; position parameter can be invalid if it is not a number or less than 0 or greater than the size of the file.
int fgetoff(int fd)
- fgetoff() syscall will take 1 argument: open file descriptor.
- fgetoff() will return -1 if the file is not open.
- If file is open, fgetoff() will return the current position indicator (offset) value corresponding to the file descriptor.
Debugging with GDB
Your second task is to demonstrate that you can use gdb to debug xv6 code. You should show the integer value that fdalloc returns the first time it is called after a process has been completely initialized.
To do this, you can follow these steps:
In one window, start up qemu-nox-gdb. In another window on the same machine, start up gdb (it will attach to the qemu process) and continue it until xv6 finishes its bootup process and gives you a prompt. Now, interrupt gdb and set a breakpoint in the fdalloc() routine. Continue gdb. Then, run the stressfs user application at the xv6 prompt. Your gdb process should now stop in fdalloc. Now, step (or, probably next) through the C code until gdb reaches the point just before fdalloc() returns and print the value that will be returned (i.e., the value of fd). Now immediately quit gdb and run whoami to display your login name.
To sanity check your results, you should think about the value you expect fdalloc() to return in these circumstances to make sure you are looking at the right information. What is the first fd number returned after stdin, stdout, and stderr have been set up?
If gdb gives you the error message that fd has been optimized out and cannot be displayed, make sure that your Makefile uses the flag “-Og” instead of “-O2”. Debugging is also a lot easier with a single CPU, so if you didn’t do this already: in your Makefile find where the number of CPUS is set and change this to be 1.
Take a screenshot showing your gdb session with the returned value of fd printed and your login name displayed. Submit this screenshot to Canvas.
Hints
This project is very similar to a prior OSTEP projectLinks to an external site.. You can refer to some of the hints and documentation included there.
Getting started with xv6
The source code for xv6 (and associated README) can be found in ~cs537-1/public/xv6.tgz . Everything you need to build and run and even debug the kernel is in there. The way to make your own version is to do the following:
prompt> cd $PROJ prompt> cp ~cs537-1/public/xv6.tgz . prompt> tar xvzf ~cs537-1/public/xv6.tgz
This will create an xv6-public directory in the $PROJ directory. You can then cd into that and begin working.
If, for development and testing, you would like to run xv6 in an environment other than the CSL instructional Linux cluster, you may need to set up additional software. You can read these instructions for the MacOS build environment.Links to an external site. Note that we will run all of our tests and do our grading on the instructional Linux cluster so you should always ensure that the final code you handin works on those machines.
After you have obtained the source files, you can run make qemu-nox
to compile all the code and run it using the QEMU emulator. Test out the unmodified code by running a few of the existing user-level applications, like ls
and forktest
. With ls
inside the emulator, you’ll be able to see a few other applications that are available (as well as files that have been created within the xv6 environment).
To quit the emulator, type Ctl-a x
.
You will want to be familiar with the Makefile and comfortable modifying it. In particular, see the list of existing UPROGS
. See the different ways of running the environment through make (e.g., qemu-nox
or qemu-nox-gdb
).
Find where the number of CPUS
is set and change this to be 1.
For additional information about xv6, we strongly encourage you to look through the code while reading this book by the xv6 authors. Or, if you prefer to watch videos, this great tutorialLinks to an external site. on xv6 by Prof. Remzi describe some of the relevant files. Note that the code and project in the videos do not exactly match what you are doing. We always recommend that you look at the actual code yourself while either reading or watching (perhaps pausing the video as needed).
Implementing the syscalls
The primary files you will want to examine in detail include syscall.c
, sysproc.c
, sysfile.c
, and file.c
. You might also want to take a look at usys.S
, which (unfortunately) is written in assembly.
To add a system call, find some other very simple system call that also set an value to a pointer-based parameter, like sys_fstat
, copy it in all the ways you think are needed, and modify it so it doesn’t do anything and has the new name. Compile the code to see if you found everything you need to copy and change. You probably won’t find everything the first time you try.
A file descriptor is a simple number that a computer’s operating system uses to keep track of open files. When a program opens a file, the operating system assigns it a unique file descriptor. This number is then used by the program to read from, write to, or manipulate the file in other ways. Think of it like a ticket you get at a coat check – it doesn’t contain all the information about your coat, but as long as you have that ticket, you can use it to retrieve or modify your coat. Similarly, programs use file descriptors as references to perform operations on the actual files.
Operating systems maintain a position offset variable per open file to keep track of the current position within the file for read and write operations. This position offset is initialized to 0 when a file is opened. When user processes issue read or write system calls, the operation commences at the file offset, and the file offset is incremented by the number of bytes read/written. In this project, you will be implementing system calls to update and read this file offset.
Consider the modifications needed to make your system calls behave as expected:
- Take a look at sys_read() to understand how to access file structure using the input file descriptor. You will have to implement similar code to obtain file struct from input fd.
- Notice how fileread() uses locks (we will learn more about this later in this course) to ensure mutual exclusion while updating the offset. Consider using locks while reading or writing to the position offset.
- Compare the input pos value to the file size (see filestat() to learn how to obtain file size).
Testing your syscalls
We suggest you write an example user application (submission not required) that tests your system call. Again, we suggest copying one of the straight-forward utilities that already exist, such as cat.c. Modify the application to open a file and invoke your newly implemented syscalls. To compile the user application, you will have to make changes to the Makefile. Look for UPROGS
to add your test user application.
Similar to your first project, some tests will be provided at ~cs537-1/tests/p2. Details will be provided once the tests are released.
Handing It In
Remember there are two handin steps.
For your xv6 code, your handin directory is ~cs537-1/handin/LOGIN/p2
where LOGIN
is your CS login. Please create a subdirectory called ‘src’: ~cs537-1/handin/LOGIN/p2/src.
Copy all of your source files (but not .o files, please, or binaries!) into this directory. A simple way to do this is to copy everything into the destination directory, then type make
to make sure it builds, and then type make clean
to remove unneeded files.
shell% mkdir ~cs537-1/handin/LOGIN/p2/src shell% cd ~cs537-1/handin/LOGIN/p2/src shell% make shell% make clean
Second, for your screenshot of your debugging session, upload your image here in Canvas.
Reviews
There are no reviews yet.