Due by 11:59pm, Tuesday, January 30.
This assignment has four goals:
/procfile system; 2) building a shell. Everything you do in this warm-up assignment is at the user-level (outside of the OS kernel).
The OS is a program that uses various data structures. Like all programs in
execution, you can determine the performance and other behavior of the OS by
inspecting its state - the values stored in its data structures. In this
part of the assignment, we study some aspects of the organization and behavior
of a Linux system by observing values of kernel data structures exposed through
/proc virtual file system.
/proc virtual file system:
Linux uses the
/proc file system to collect information from
kernel data structures. The
/proc implementation provided
with Linux can read many different kernel data structures. If you
/proc on a Linux machine, you will see a
number of files and directories at that location. Files in this directory
subtree each corresponds to some kernel data structure. The subdirectories
with numeric names contain virtual files with information about the process
whose process ID is the same as the directory name.
/proc can be read like ordinary ASCII files. You can
open each file and read it using library routines such as
proc (5) manual page explains
the virtual files and their content available through the
Requirements in detail:
In this part, you are asked to write a program to report the behavior of the Linux kernel. Your program should run in two different versions. The default version should print the following values on
proc_parse, running it without any parameter should print out information required for the first version. Running it with two parameters "
proc_parse <read_rate> <printout_rate>" should print out information required for the second version.
read_raterepresents the time interval between two consecutive reads on the
printout_rateindicates the time interval over which the average values should be calculated. Both
printout_rateare in seconds. For instance,
proc_parse 2 60should read kernel data structures once every two seconds. It should then print out averaged kernel statistics once a minute (average of 30 samples). The second version of your program doesn't need to terminate.
The OS command interpreter is the program that people interact with in order to launch and control programs. On UNIX systems, the command interpreter is often called shell: a user-level program that gives people a command-line interface to launching, suspending, and killing other programs.
bash, ... are all
examples of UNIX shells. You use a shell like this every time you log
into a Linux machine at a URCS computer lab and bring up a terminal.
It might be useful to look at the manual pages of these shells, for
example, type "
The most rudimentary shell is structured as the following loop:
fork()system call to spawn a new child process;
exec()system call (or one of its variants) to launch the specified program;
wait()system call (or one of its variants) to wait for the child to terminate;
Although most of commands people type on the shell prompt are the names
of other UNIX programs (such as
shells also recognize some special commands (called internal commands)
which are not program names. For example, the
command terminates the shell, and the
cd command changes
the current working directory. Shells directly make system calls to
execute these commands, instead of forking a child process to handle
Requirements in detail:
Your job is to implement a very primitive shell that knows how to launch new programs in the foreground and the background. It should also recognize a few internal commands. More specifically, it should support the following features.
exitshould use the
exit()system call to terminate the shell.
chdir()system call to change to a new directory.
To allow users to pass arguments you need to parse the input line into
words separated by whitespace (spaces and '\t' tab characters). You
might try to use
strtok_r() for parsing (check the manual
strtok_r() and Google it for examples of using it).
In case you wonder,
strtok_r() is a user-level utility, not a
system call. This means this function is fulfilled without the help of the
operating system kernel. To make the parsing easy for you, you can
assume the '&' token (when used) is separated from the last argument with
one or more spaces or '\t' tab characters.
The shell runs programs using two core system calls:
execvp(). Read the manual pages to see how to use them.
fork() creates an exact copy of the currently
running process, and is used by the shell to spawn a new process. The
execvp() call is used to overload the currently running
program with a new program, which is how the shell turns a forked process
into the program it wants to run. In addition, the shell must wait until
the previously started program to complete unless the user runs them at
the background (with &). This is done with the
one of its variants (such as
waitpid()). All these system
calls can fail due to unforeseen reasons (see their manual pages for
details). You should check their return status and report errors if they
You can get a working shell from here (executable on Linux x86 platforms) and play with it. I wrote it in a few hours and I haven't tested it extensively. Let me know if you find any problems with it. Note that this is not intended to be a complete solution. It does not support some features that you are asked to implement.
No input the user gives should cause the shell to exit (except when the
Ctrl+D). This means your
shell should handle errors gracefully, no matter where they occur. Even
if an error occurs in the middle of a long pipeline (if you choose to do
the CSC456 part described below), it should be reported accurately and
your shell should recover gracefully. In addition (if you choose to do
the CSC456 part described below), your shell should not
generate leaking open file descriptors. Hint: you can monitor
the current open file descriptors of the shell process through the
/proc file system.
In this additional part, your shell needs to support pipes. Pipes allow the
stdouts of a list of programs to
be concatenated in a chain. More specifically, the first program's
stdout is directed to the
stdin of the second
program; the second program's
stdout is directed to the
stdin of the third program; and so on so forth. Multiple
piped programs in a command line is separated with the token "|'. A
command line therefore looks like the following form:
<program1> <arglist1> | <program2> <arglist2> | ... | <programN> <arglistN> [&]
Try an example like this: pick a text file with more than 10 lines (assumed it is called
textfile) and then do
cat textfile | gzip -c | gunzip -c | tail -n 10
in a regular shell or in the working shell we provide. Pause a bit to think what it really does. Note that multiple processes need to be launched for piped commands and all of them should be waited in a foreground execution.
A note on the programming language:
C/C++ is the only choice for this assignment and all later programming assignments. We are not alone on this. Most of the operating system kernels (Linux and other UNIX variants) themselves are written in C; the remaining parts are written in assembly languages. Higher-level languages (Java, Perl, ...) are less desirable because C is more efficient and it allows more flexible control on system resources.
You are asked to electronically turn in your source files and a makefile. After compiling, we should see two executables (one for Part I and one for Part II). Attach a README file describing the names of the two executables, special compiling instructions, or anything else special you want to let us know. The README file should be in plain text format. Instructions for electronic turn-ins can be found on the class Web page.
Late turn-in policy:
Late turn-ins will be accepted for up to three days, with 10% penalty for each late day. No turn-ins more than three-day late will be accepted.