OPERATING SYSTEMS – ASSIGNMENT 1 XV6, PROCESSES, SYSTEM CALLS, IPC

Introduction

Throughout this course we will be using a simple, like teaching called xv6: http://pdos.csail.mit.edu/6.828/2010/xv6-book/index.html

The xv6 OS is simple enough to cover and understand within a few weeks yet it still contains 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 a lot of useful information and getting started tips there: http://pdos.csail.mit.edu/6.828/2010/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 exploring xv6 and extend it to support various scheduling policies.

Task 0: Running xv6

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

 Open a , and traverse to a in your computer where you want to store the   sources for the OS course. For example, in : ~/os142  ~/os142  Execute the following command (in a single line): svn checkout http://bgu-os-142-xv6.googlecode.com/svn/trunk assignment1

This will create a new folder called assignment1 will contain all project files.   Build xv6 by calling: make  Run xv6 on top of QEMU by calling: make clean qemu Warm up (“Hello XV6”)

This part of the assignment is aimed getting you started. It includes two extensions to the xv6 kernel. The main purpose of this assignment is to get familiar with xv6 kernel in general and with its processes/system calls implementation in particular.

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 quite limited. In addition, in most modern Unix based operating systems the implementation of these extensions may be slightly different (implementation in user-space instead of kernel-space), but for the sake of simplicity we will implement them in kernel-space.

Extension 1: support the PATH

When a single program is executed by the shell it performs two system calls: (in order to create another to run the program) and (in order to change the new created process' image). Upon invoking the exec , the kernel seeks the appropriate binary file in the current and executes it. If the desired file does not exist in the working directory, an error is returned to the shell who then prints an error message. For example:

: Command not found.

A ‘PATH’ is an environment variable which specifies the list of directories where commonly used reside. If, upon typing a command, the required file is not found in the current working directory, the kernel attempts to execute the file from one of the directories specified by the PATH. An error is returned only if the required file was not found in the working directory or in any of the directories listed in the PATH.

Your first task is to support a PATH environment variable. We will maintain our PATH as part of the kernel. You can assume that the length of value will not exceed MAX_PATH_LENGTH = 256. You must set the initial value of PATH variable to contain the root directory.

1.1 Change exec behavior so that in case a required is not found in the current working directory, the kernel will attempt to execute the file from one of the directories specified by the PATH. An error will be returned only in case the required executable is not found in any of these directories.

1.2 Change fork implementation so it will be familiar with PATH environment variable (i.e., when the is created, it will the value of PATH variable from its parent). 1.3 Implement a system call: int set_path(char* path) This system call sets the given path value to the environment variable path. The system call returns 0 upon success and -1 upon failure.

Tip: A good place to store the path variable might be proc struct (can be found at proc.h). Tip: You can find current exec implementation at sysfile. and exec.c Tip: Adding a system call requires some delicate work and proper registration. Be sure to add changes to syscall.c, syscall.h, usys.S and sysproc.c

1.4 Implement a system call: int get_path(char* path) This system call returns the current value of the environment variable path (actually it must save the current value of the path environment variable at the address pointed by a single argument). The system call returns 0 upon success and -1 upon failure.

1.5 Extend current implementation shell with a new command set, that takes as a single argument a list of directories separated by ';' representing paths. This command should update the value of environment variable path (by using the set_path system call).

Examples: set /; The Path variable will include the root directory

set /;/foo/;/foo/bar/; The Path variable will include the root directory, the folder named foo, and the folder named bar (which is located in the folder foo).

1.6 Write a simple program (path) that will the current value of the environment variable path.

Extension 2: support right, left, up and down arrows (←, →, ↑, ↓)

The ability to edit the inputs typed is a basic service provided by most shells. By pressing the right or left arrows (‘←’, ‘→’), the user can between the typed chars and decide where to delete a char or add a new char (insert mode). By pressing the up or down arrows ('↑', '↓') the user can browse the inputs history for older or newer inputs respectively.

In this task you will emulate these behaviors. You will need to support the movement of the cursor right and left, by pressing the ‘←’ and ‘→’ keys respectively, so that the line typed in the shell will change where the cursor currently is. In addition, we will maintain our command line history in the kernel. You should support a history of up to MAX_HISTORY_LENGTH = 20 records. Each record has maximum length of INPUT_BUF (see console.c).

When the number of records exceeds MAX_HISTORY_LENGTH, you should reuse entries in a FIFO manner. There are no limitations of how to deal with edge cases (what happens when a user presses (up / down) than 20 times) but you (obviously) have to make sure the kernel does not crash.

Note: In most Unix based systems these behaviors are implemented at the shell level. In order to keep things simple we are implementing it at the kernel level. Tip: a good place to start working on this task is trap.c and console.c

Dining philosophers problem

At this problem N silent philosophers sit at a round table with bowls of spaghetti. Forks are placed between each pair of adjacent philosophers. Each philosopher must alternately think and eat. However, a philosopher can only eat spaghetti when he has both left and right forks. Each fork can be held by only one philosopher and so a philosopher can use the fork only if it's not being used by another philosopher. After he finishes eating, he needs to put down both forks so they become available to others. A philosopher can grab the fork on his right or the one on his left as they become available, but can't start eating before getting both of them.

Eating is not limited by the amount of spaghetti left: assume an infinite supply. The problem is how to design a discipline of behavior such that each philosopher won't starve; i.e., can forever continue to alternate between eating and thinking assuming that any philosopher cannot know when others may want to eat or think.

One of possible solutions assumes existence of an arbitrator (e.g., a waiter) that can permit/forbid philosopher to pick up forks. In order to pick up the forks, a philosopher must ask permission of the waiter. The waiter gives permission to only one philosopher at a until he has picked up both his forks. Putting down a fork is always allowed. This approach can result in reduced parallelism: if a philosopher is eating and one of his neighbors is requesting the forks, all other philosophers must wait until this request has been fulfilled even if forks for them are still available.

Write two user space programs:

 waiter.c – this program will imitate the behavior of an arbitrator. First it will receive as an argument (from the shell) the amount of the philosophers in the problem. Then it will create two pipes: one for writing permission to a philosopher to take forks and another to receive message from philosopher that finished eating. Next it will create the appropriate amount of processes (i.e., each process represents a single philosopher). For each created process it will redirect the standard input/output to pipes created before, and finally will execute the code of the philosopher (exec philosopher). The behavior of the waiter after creation of the philosophers will be as follows: first it will write a message to pipe that he permits to take forks, afterwards it will send next permission only after receiving a message from a philosopher that he has finished eating. For each action the waiter will print a message:

o Allowing taking the forks.

o Philosopher is finished eating.

The waiter must terminate only when all philosophers are terminated.

 philosopher.c – will alternate between eating and thinking. The philosopher will start from waiting for a message from the waiter. When he will receive the permission, he will start eating ( for one second) and afterwards it will send a message back to the waiter that he has finished eating. For every action (i.e., eat/think) the philosopher will print following message: philosopher eating/thinking.

Tip: you can think of your own termination condition for the philosopher, but every philosopher must change its state at least 6 times. Submission Guidelines

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 comments – 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 must be added explicitly with the ‘svn add’ command:

> svn add

In case you need to revert to a previous version:

> svn revert

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 grades will only apply your latest patch, 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 the graders will examine your code on lab computers only! We advise you to test your code on lab computers prior to submission, and in addition after submission to download your assignment, apply the patch, compile it, and make sure everything runs and works. 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: 1. Quota – as you may 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 is not backed up – remember to back up your work as often as possible. 2. 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 in later stages. Although we do not limit you, 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 *.c

Debugging

You can try to 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. Due to the large number of students taking this course we will only be able to support technical problems that occur on lab computers.

Having said that, students who wish 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 . 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

2. Install Linux and QEMU on your own PC. 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’ personal computers.

Enjoy !!!