CSS 430
Program 1: System Calls and Shell
Duedate: See the syllabus
Please note that you must independently work on all programming assignments except the final project. The instructor keeps the code of all the former students in his database, compares your code with the database contents, and will never tolerate any academic dishonesty. In fact, some students have been sent to the vice chancellor's office when their plagiarism were found.
uw1-320-00%like a host name followed by a % symbol, or simply
$How does the shell execute a user command? The mechanism follows the steps given below:
uw1-320-00% a.out a b cThis means that the shell duplicates itself and has this duplicated process execute a.out that receives a, b, and c as its arguments.
The shell has some built-in commands that rather changes its current status than executes a user program. (Note that user programs are distinguished as external commands from the shell built-in commands.) For instance,
uw1-320-00% cd public_htmlchanges the shell's current working directory to public_html. Thus, cd is one of the shell built-in command.
The shell can receive two or more commands at a time from a keyboard input. The symbols ';' and '&' are used as a delimiter specifying the end of each single command. If a command is delimited by ';', the shell spawns a new process to execute this command, waits for the process to be terminated, and thereafter continues to interpret the next command. If a command is delimited by '&', the shell goes forward and interprets the next command without waiting for the completion of the current command.
uw1-320-00% who & ls & dateexecutes who, ls, and date concurrently. Note that, if the last command does not have an delimiter, the shell assumes that it is implicitly delimited by ';'. In the above example, the shell waits for the completion of date. Taking everything in consideration, the more specific behavior of the shell is therefore:
uw1-320-00% a.out < file1 > file2redirects a.out's standard input and output to file1 and file2 respectively, so that a.out reads from file1 and writes to file2 as if it were reading from a keyboard and printing out to a monitor. Another convenience feature is pipeline.
uw1-320-00% command1 | command2 | command3connects command1's standard output to command2's standard input, and also connects command2's standard output to command3's standard input. For instance,
uw1-320-00% who | wc -lfirst executes the who command that prints out a list of current users. This output is not displayed but is rather passed to wc's standard input. Thereafter, the wc is executed with its -l option. It reads the list of current users and prints out #lines of this list to the standard output. As a result, you will get the number of the current users.
Through this series of assignments, you are to implement and/or to enhance some portions of ThreadOS. ThreadOS loads Java programs that have been derived from the Thread class, manages them as user processes, and provides them with some basic operating system services. Those services include thread spawn, thread termination, disk operations, and even standard input/output. ThreadOS receives all service requests as a form of interrupt from each user thread, handles them, and returns a status value to the interrupting thread.
Component | Java/Class | Description |
Boot | Boot.java | invokes a BOOT system call to have Kernel initialize its internal data, power on Disk, start the Scheduler thread, and finally spawn the Loader thread. |
Kernel | Kernel.java | receives an interrupt, services it if possible, otherwise forwards its request to Scheduler or Disk, and returns a completion status. |
Disk | Disk.java | simulates a slow disk device composed of 1000 blocks, each containing 512 bytes. Those blocks are divided into 10 tracks, each of which thus includes 100 blocks. The disk has three commands: read, write, and sync detailed in the assignment 3, 4, and 5. |
Scheduler | Scheduler.java, TCB.java | receives a Thread object that Kernel instantiated upon receiving an EXEC system call, allocates a new TCB(Thread Control Block) to this thread, enqueues the TCB into its ready queue, and schedules its execution in a round robin fashion. |
SysLib | SysLib.java | is a utility that provides user threads with a convenient style of system calls and converts them into corresponding interrupts passed to Kernel. |
To start ThreadOS, simply type:
uw1-320-00% java Boot ThreadOS ver 1.0: Type ? for help threadOS: a new thread (thread=Thread[Thread-3,2,main] tid=0 pid=-1) -->Boot initializes Kernel data, powers on Disk and starts Scheduler. It finally launches Loader that then carries out one of the following commands:
? | prints out its usage. |
l user_program | starts user_program as an independent user thread and waits for its termination. |
q | synchronizes disk data and terminates ThreadOS |
Note that Loader is not a shell. It simply launches and waits for the completion of a user program (which may behave as a shell). From ThreadOS' point of view, there is no distinction between Loader and the other user programs.
public class PingPong extends Thread { private String word; private int loop; public PingPong( String[] args ) { word = args[0]; loop = Integer.parseInt( args[1] ); } public void run( ) { while ( true ) { System.out.print( word + " " ); for (int i = 0; i < loop; i++ ); } } }If you write the following main function:
public class ThreadDriver { public static void main( String[] args ) { String args[2]; args[0] = "ping"; args[1] = "10000"; new PingPong( args ).start( ); args[0] = "PING"; args[1] = "90000"; new PingPong( args ).start( ); } }it will instantiate two PingPong threads, one printing out "ping" every 10000 dummy iterations and the other printing out "PING" every 90000 dummy iterations.
ThreadOS Loader actually takes care of this thread-instantiating part of main function. Once you invoke ThreadOS, Loader waits for a l command, say "l PingPong ping 10000". Then, it will load your PingPong class into the memory, instantiate its object, pass a String array including ping and 10000 as arguments to this thread, and wait for its termination. Note that general Java threads can receive any type of and any number of arguments, however ThreadOS restricts its user programs to receive only a String array as their argument.
Java itself provides various classes and methods that invoke real OS
system calls such as System.out.println and sleep. Since
ThreadOS is an operating systems simulator, user programs
running on ThreadOS are prohibited to use such real OS system
calls. Prohibited classes include but are not limited to:
Instead, user programs are provided with ThreadOS-unique system calls including standard I/O, disk access, and thread control. Therefore, System.out.print( word + " " ); should be replaced with one of ThreadOS-unique systems calls:
SysLib.cout( word + " " );
While Java threads can be terminated upon a simple return from their run method, ThreadOS needs an explicit system call to terminate the current user thread.
SysLib.exit( );This is because thread termination is a part of thread control, thus one of ThreadOS services. Since the above example of user thread falls into an infinitive loop, we need to revise it so that this example code safely terminates the invoked thread and resumes Loader.
public class PingPong extends Thread { private String word; private int loop; public PingPong( String[] args ) { word = args[0]; loop = Integer.parseInt( args[1] ); } public void run( ) { for ( int j = 0; j < 100; j++ ) SysLib.cout( word + " " ); for (int i = 0; i < loop; i++ ); } SysLib.cout( "\n" ); SysLib.exit( ); } }
Kernel.interrupt( int interruptRequestVector, int trapNumber, int parameter, Object args );where interruptRequestVector may be 1: INTERRUPT_SOFTWARE, 2: INTERRUPT_DISK, and 3: INTERRUPT_IO; trapNumber specifies a request type of software interrupt such as 0: BOOT, 1: EXEC, 2: WAIT, 3: KILL, etc.; parameter is a device-specific value to control each device; and args are arguments of each interrupt request.
Since this interrupt method is not an elegant form to a user program, ThreadOS provides a user program with its system library, called SysLib that includes several important system-call functions as shown below. (Unless otherwise mentioned, each of these functions returns 0 on success or -1 on error.)
ps -A | grep argv[1] | wc -lImplement processes using the following system calls:
Process | Command | Stdin | Stdout |
Parent | wait for a child | no change | no change |
Child | wc -l | redirected from a grand-child's stdout | no change |
Grand-child | grep argv[1] | redirected from a great-grand-child's stdout | redirected to a child's stdin |
Great-grand-child | ps -A | no change | redirected to a grand-child's stdin |
uw1-320-00% java Boot ThreadOS ver 1.0: Type ? for help -->l Shell l Shell threadOS: a new thread (thread=Thread[Thread-6,2,main] tid=1 pid=0) shell[1]% TestProg1 & TestProg2 & ... A concurrent execution of TestProg1 and TestProg2 ... shell[2]% TestProg1 ; TestProg2 ... A sequential execution of TestProg1 and TestProg2 ... shell[3]% exit -->q uw1-320-00%Once your Shell.java is invoked, it should print out a command prompt:
shell[1]%When a user types multiple commands, each delimited by '&' or ';', your Shell.java executes each of them as an independent child thread with a SysLib.exec( ) system call. Note that the symbol '&' means a concurrent execution, while the symbol ';' means a sequential execution. Thus, when encountering a delimiter ';', your Shell.java needs to call SysLib.join( ) system call(s) in order to wait for this child thread to be terminated. Since SysLib.join( ) may return the ID of any child thread that has been previously terminated, you have to repeat calling SysLib.join( ) until it returns the exact ID of the child thread that you wants to wait for.
You do not need to implement standard I/O redirection or pipes. You do not need to provide shell variables and programming constructs, either. Only the required functionality of your Shell.java is handling an arbitrary number of commands in a line. You may assume that commands, arguments, and even delimiters are separated by arbitrary amounts of spaces or tabs.
To test your Shell.java, use PingPong.java that is found in the same directory as ThreadOS. Your test should be
Shell[2] PingPong abc 10000000 & PingPong xyz 10000000 & PingPong 123 10000000 & Shell[1] PingPong abc 10000000 ; PingPong xyz 10000000 ; PingPong 123 10000000 ;
ThreadOS can be found in
Copy all compiled class files into your directory and thereafter
compile your shell.java. Do not try to compile the ThreadOS source
code, some portion of which cannot be accessed. (Those are your future
assignments.)
Hints: In order to read a command line, you should use SysLib.cin( StringBuffer s ) that returns a line of keyboard input to the StringBuffer s. Parsing and splitting the line into words can be performed with the StringTokenizer class found in java.util. For the same purpose, you can also use the SysLib.stringToArgs( String s ) utility function which is much easier to use.
Your processes.cpp source code An output when running: processes mingetty ps -A | grep mingetty | wc -l processes ksand ps -A | grep kscand | wc -l processes sendmail ps -A | grep sendmail | wc -l
Shell.java An output when testing your Shell.java with PingPong.java