You are on page 1of 7

OPERATING SYSTEMS, A SSIGNMENT 1

XV6 INTRODUCTION, SHELL, SYSTEM CALLS AND SIGNALS

Introduction

Throughout this course we will be using a simple, UNIX like, teaching operating system
called xv6: http://pdos.csail.mit.edu/6.828/2011/xv6.html

The xv6 OS is simple enough to cover and understand within a few weeks yet it still
implements the important concepts and organizational structure of UNIX. To run it, you
will have to compile the source files and use the QEMU processor emulator (installed on
all CS lab computers).

Tip: xv6 was  (and  still  is)  developed  as  part  of  MIT’s  6.828  Operating  Systems  
Engineering course. You can find a lot of useful information and getting started
tips there: http://pdos.csail.mit.edu/6.828/2011/overview.html
Tip: xv6 has a very useful guide. It will greatly assist you throughout the course
assignments: http://pdos.csail.mit.edu/6.828/2011/xv6/book-rev6.pdf
Tip: you may also find the following useful:
http://pdos.csail.mit.edu/6.828/2011/xv6/xv6-rev6.pdf

In this assignment, we will start exploring xv6 and extend it to support signals. The last
part will be devoted to writing a user program, which will test the OS modifications.

Task 0: running xv6

Begin by downloading our revision of xv6, from the os122 svn repository:

 Open a shell, and traverse to the desired working directory.


 Execute the following command (in a single line):

svn checkout http://bgu-os-122-xv6-rev6-1.googlecode.com/svn/trunk


assignment1

This will create a new folder called assignment1 which will contain all project
files.
 Build xv6 by calling: make
 Run xv6 on top of QEMU by calling: make qemu
Task  1:  warm  up  (“HelloXV6”)

This part of the assignment is aimed at getting you started. It includes an extension to
the xv6 shell – a simplistic implementation of the pwd command

Note that in terms of writing code, the current xv6 implementation is limited: it
does not support system calls you may use when writing on Linux and its
standard library is very thin.
Throughout all assignments, we will try to refrain from directing you to specific
parts of xv6. Although this may be a challenge by itself, we believe that in time it
will help you understand this OS better.

Extending xv6 – current working directory in shell (pwd)

In this task you will replace the xv6 shell's current prompt (the  ‘$’  symbol) with the full
path to the current working directory. This improved prompt is a simple form of a
productivity tool.

One can think of several different ways to implement this feature. The solution you will
follow in this task is a naïve one and will not involve important file system concepts
discussed at later parts of the course. It relies on the maintenance of a new variable
holding the string representation of the current working directory.

Start by finding the shell's source file. Look for the function which is in charge of
handling the change directory command (i.e., “cd”). Add a variable that will be used by
the shell to print the current working directory. This  variable’s  value  should  be  revised  
after  each  call  to  “cd  <val>”  and  reflect  the  new  working  directory.  

Assume that the maximal size of the prompt message does not exceed 256 characters.

Remember that the command may fail and illegal arguments or a single dot
(“cd  .”)  may  be  entered.

Next, locate the code lines that are in charge of printing the prompt to the user. Change
these lines so that the value of your new variable is printed instead.

For example, after  initialization  the  shell’s  prompt should be:


/>

After  invoking  “cd  temp” (and only if the temp folder exists) the prompt would change
to:
/temp/>

Tip: your solution should only affect the shell's source file.
Tip: don’t  forget  about relative changes to the working directory, such as “cd ..”.
Task 2: Signals framework

As seen in class, signals are a simple inter process form of communication currently not
implemented in xv6. In this part of the assignment, you will add the framework that will
enable the passing of signals from one process to the other. This implementation will
cover the basic features needed for a signals framework, and despite its resemblance to
the Linux framework it is far from being complete.

2.1 – updating the process data structure:

The first step towards meeting this goal is to  extend  the  ‘proc’  struct located at proc.h
(line 61). The struct should contain a data word called signal each of whose bits
represents a currently unhandled (pending) signal. For example, when this  word’s  value
is 0x…2  (hex  representation  of  the  binary  word  00…0010)  then  this  process  received  a  
signal whose identifier is 2. For simplicity, you may assume that your implementation
will never have to support more than 32 signals.

Note that this representation follows many modern operating systems where
multiple signals of the same type (having the same identifier) are ignored.

Each signal must also be associated with some action. To support this, add an array of 32
entries where every entry is a pointer to a function (accepting no arguments and
returning no value). By default all signals should be ignored, except for the ones defined
in task 3.

2.2 – registering to alternate signal handlers:

A process wishing to register a custom handler for a specific signal will use the following
system call which you should add to xv6:

int signal(int signum, sighandler_t handler)

This system call will register a new handler (handler) for a given a signal number
(signum). If successful, 0 is returned otherwise a -1 value is returned. The type
sighandler_t should be defined as:

typedef void (*sighandler_t)(void);

Tip: Adding a system call requires some delicate work and proper registration. Be
sure to add changes to syscall.c, syscall.h, usys.S, user.h and sysproc.c

2.3 – sending a signal:

So far, we have allowed a process to prepare itself for an incoming signal. Next we will
add the ability to send a signal to a different process. Add the following system call:

int sigsend(int pid, int signum)

Although  “kill”  is  the  standard  name  for  the  system  call  in  charge  of  sending  a  
signal, it is already used in xv6 for terminating processes.

Given a process id (pid) and a signal number (signum), the sigsend system call will
send process pid the desired signal number. Upon successful completion, 0 will be
returned. A -1 value will be returned in case of a failure.
2.4 – getting the process to handle the signal:

Finally, you are to implement some mechanism which will make sure that a process
receiving a signal actually executes the relevant signal handler.

In your extension of xv6, a signal is handled (if at all) whenever the scheduler returns to
the process. That is, before the scheduler allocates a time slice to a process, it first checks
for sent signals to that process and if needed it updates the instruction pointer (and
stack pointer) by calling register_handler. This way, when the process receives its
time slice, the code for the signal handler is executed.

void
register_handler(sighandler_t sighandler)
{
char* addr = uva2ka(proc->pgdir, (char*)proc->tf->esp);
if ((proc->tf->esp & 0xFFF) == 0)
panic("esp_offset == 0");

/* open a new frame */


*(int*)(addr + ((proc->tf->esp - 4) & 0xFFF))
= proc->tf->eip;
proc->tf->esp -= 4;

/* update eip */
proc->tf->eip = (uint)sighandler;
}

The register_handler function is not native to xv6 and was added by the OS122
team to support the present assignment (you can find it in proc.c). The function locates
the  current  process’  stack  and  opens  a  new  frame.  It  must  also  update  the  old
instruction pointer so that when the new code (sighandler) is completed the process
normally resumes its execution.

The details of this function go beyond the scope of what you have seen so far and
require  better  understanding  of  xv6’s  memory  management.

2.5 – inheriting handlers to a child process:

Modify the fork system call so that a child process will have the same registered signal
handlers as his parent.

Task 3: A few simple signals

Support the following signals: SIGINT (id=0), SIGUSR1 (id=1), SIGUSR2 (id=2) and
SIGCHLD (id=3).

The default behavior for these signals is specified below:

SIGINT – terminates a process

SIGUSR1 – reserved  for  user  defined  handlers,  by  default  prints  “SIGUSR1  <pid>”

SIGUSR2 – reserved  for  user  defined  handlers,  by  default  prints  “SIGUSR2  <pid>”

SIGCHLD – notifies  the  process’  parent  of  any  state  change.  Specifically, whenever a
process ends it should send a SIGCHLD to its parent.
Support the generation of a SIGINT signal to the currently executing process from the
keyboard.  This  signal  will  be  generated  whenever  the  user  presses  Ctrl+C  (‘^C’).

Notice that the signal could be sent when the current process sleeps, or even when the
shell awaits for the user's input. Find a way in which you would properly handle these
situations. procdump() -> console.c -> consoleintr

Tip: start by finding out where xv6 handles other special key combinations (e.g. ^p)

Task 4: Testing

In this section you will add a user application which tests the impact of your new signals
framework.

Creating a sanity test

Similar to several built-in user space programs in xv6 (e.g., ls, grep, echo, etc.), you can
add your own user space programs to xv6.

Add a program called sanity. This program will fork 3 child processes and will then offer
the user the following simple command line interface.

Enter a child id (0 – 2): <input>


Which signal to send: <input>

The program should terminate after all child processes have terminated. Until then
these lines should be printed, allowing the user to test the signals framework.

Each child process will handle signal number 4 by printing “ouch <pid>”. While not
handling a signal each child process should run in an infinite loop, sleeping in each
iteration for half a second.

Tip: to add a user space program, first write its code (e.g., sanity.c). Next update
the Makefile so that the new program is added to the file system image. The
simplest way to achieve this is by modifying the lines right after “UPROGS=\”.
Tip: you can (and should!) read more about the make utility here:
http://www.opussoftware.com/tutorial/TutMakefile.htm
Tip: You have to call the exit system call to terminate a processes' execution.
Tip: The fork system call's return value will come in handy.

Submission guidelines

Assignment due date: 22/4/12, 22:00

Make sure that your Makefile is properly updated and that your code compiles with
no warnings whatsoever. We strongly recommend documenting your code changes with
remarks – these are often handy when discussing your code with the graders.

Due to our constrained resources, assignments are only allowed in pairs. Please note
this important point and try to match up with a partner as soon as possible.

Submissions are only allowed through the submission system. To avoid submitting a
large number of xv6 builds you are required to submit a patch (i.e. a file which patches
the original xv6 and applies all your changes).
You may use the following instructions to guide you through the process:

Back-up your work before proceeding!

Before creating the patch review the change list and make sure it contains all the
changes that you applied and noting more. Modified files are automatically detected by
svn but new files should be added explicitly with  the  ‘svn  add’  command:

>svn add<filename>

In case you need to revert to a previous version:

>svn revert <filename>

At this point you may examine the differences (the patch):

>svn diff

Alternatively, if you have a diff utility such as kompare:

>svn diff | kompare –o -

Once you are ready to create a patch simply make sure the output is redirected to the
patch file:

>svn diff >ID1_ID2.patch

Tip: although graders will only apply your latest patch file, the submission system
supports multiple uploads. Use this feature often and make sure you upload
patches of your current work even  if  you  haven’t  completed  the  assignment.

Finally, you should note that graders are instructed to examine your code on lab
computers only (!) - Test your code on lab computers prior to submission.

Tips and getting started

Take a deep breath. You are about to delve into the code of an operating system that
already contains thousands of code lines. BE PATIENT. This takes time!

Two common pitfalls that you should be aware of:

I. Quota – as you well know, your CS share is limited. Before beginning your work
we recommend cleaning your home folders and running xv6 with no
modifications. If you still encounter problems you can try to work on freespace
(another file server). Note that unlike your home folders, freespace data is not
backed up – remember to back up your work as often as possible.
II. IDE auto-changes – we are aware that many of you prefer to work under
different IDEs. Note that unless properly configured these often insert code lines
or files which may cause problems at a later stages.
Although we do not limit you (in contrast we urge you to find the editor/IDE of
your liking) our advice is to use the powerful vi editor or GNU Emacs. If you
want an X application you can try running vim or gedit.
Another useful tip is to invoke grep often to quickly navigate through the code:

grep –rni <search argument> *.c

Debugging

You can try to debug xv6’s  kernel  with gdb (gdb/ddd is even more convenient). You can
read more about this here: http://zoo.cs.yale.edu/classes/cs422/2011/lec/l2-hw.

Working from home

The CS lab computers should already contain both svn and qemu and if a host you are
working on does not please email os.cs.bgu@gmail.com so that this is fixed ASAP.

Due to the large number of students taking this course we will only be able to support
technical problems that occur on the lab computers.

Having said that students wishing to work on their personal computers may do so in
several ways:

1. Connecting from home to the labs.


a. Install PuTTY (http://www.chiark.greenend.org.uk/~sgtatham/putty/).
b. Connect to the host: lvs.cs.bgu.ac.il, using SSH as the connection
type.
c. Use the ssh command to connect to a computer running Linux (see
http://www.cs.bgu.ac.il/facilities/labs.html to find such a computer).
d. Run QEMU using:
make qemu-nox.

Tip: since xv6 may cause problems when shutting down you may want to consider
using the screen command.
screen make qemu-nox
Tip: use the default key bindings to toggle or even detach from a screen. Read
about them using:
man screen
2. Install Linux and QEMU on your own PC. Microsoft windows users can easily
install a dual boot (Windows-Linux) host with wubi:
http://www.ubuntu.com/desktop/get-ubuntu/windows-installer

Again, we will not support problems occurring on students PCs’.

Good Luck!

You might also like