CS 3733 Operating Systems, Fall 2004 Assignment 4


Due Thursday, November 11


In the previous assignment you used children of the main process to do concurrent executions in a make utility. In this assignment we will do the same thing but the children will have a different program as their parent.

In the first part of the assignment you will write an independent program called pipecompiler that will take the name of two pipes as its command line arguments, read lines from one pipe, execute the corresponding command, and send a response back on the second pipe.

In the second part of the assignment you will modify your mymake program from Assignment 3 so that it sends all requests for execution to a single copy of pipecompiler which we assume is already running.

In the third part of the assignment you will use multiple copies of pipecompiler, spreading the load as evenly as possible. Note that this does not gain much in efficiency since all of the pipecompiler programs are running on the same machine. In the next (and last) assignment, you will replace the pipes with network communication and the compilers will be running on different machines, possibly allowing you to do the compilation faster.

In the last part of the assignment you will add signal processing.

Each of the parts of this assignment should be done in a separate directory.
Read the entire assignment before writing any code.


Part 0
If you have not already done so, get assignment 3 to work correctly.


Part 1
Get a copy of pipeserver.c from Chapter 6 of USP and rename it pipecompiler.c. Modify pipecompiler so that it takes two fifo names as command line parameters, requestpipe and replypipe. Each will be created if necessary and opened for both reading and writing. Instead of calling copyfile, it starts an infinite loop. In the loop it reads a line from the requestpipe and executes the line as you did in execute_line from assignment 3. It then calls wait_for_completion and sends a single character to the replypipe, a '0' (ASCII code 48) for success or a '1' (ASCII code 49) for failure. Note that wait_for_completion returns 0 or 1 (ASCII codes 0 and 1) while pipecompiler uses printing characters. (This makes testing simpler.) Change the usage message in pipecompiler. Remove any print messages from wait_for_completion. Note that once pipecompiler enters the loop, it never exits, even if an error occurs.

Test this by using cat in one window to send lines to the requestpipe and cat in another window to read from the replypipe.


Part 2
Modify your mymake from assignment 3 so that instead of calling execute_line it uses two named pipes, a requestpipe and a replypipe. Pass the names of these pipes on the command line. Your program will now have three command line parameters, a makefile name, the replypipe name, and the requestpipe name in this order.

Open the requestpipe for writing and the replypipe for reading. Pass the requestpipe file descriptor to a new function: execute_line_fd to send the line to the pipe. The function execute_line_fd will have two parameters, the line to send (a string) and the the file descriptor. The line should contain the newline. Do not send the string terminator to the pipe.

Write a new function: wait_for_completion_fd which takes 2 parameters, a file descriptor and count. It reads count bytes from the file descriptor and returns 0 if all were successful and 1 otherwise. You will be calling this function with the replypipe file descriptor as one of its parameters. You will need to keep track of the number of executions that are pending so that when a blank line is read, you can pass the appropriate count to wait_for_completion_fd. Your mymake program should terminate with an appropriate error message if wait_for_completion_fd returns failure.

Put all of the new functions you write in your makeutil.c file.


Part 3
In Part 2, all lines were sent to the same requestpipe. Now we will use an arbitrary number of pipes passed as the third, fourth, etc. command line arguments. Start by opening all of the pipes and saving the file descriptors of the requestpipes in an array. There will still be only one replypipe. As executions are required, send each one to a different pipe until all pipes are used. Then start again with the first requestpipe. Do this until a blank line comes in. Keep track of the total number of executions pending. When a blank line comes in, call wait_for_completion_fd with the appropriate count. After this, the next request will go to the first requestpipe.


Part 4
Add a signal handler for SIGUSR1. Start the program by displaying the process ID and setting up the signal handler. The signal handler will display the number of pending executions on each requestpipe. For each pipe, the name of the pipe and the count of pending executions will be displayed. For this assignment you are allowed to use fprintf in the signal handler. Put the handler in the mymake.c file.

Note: if you cannot get Part 3 to work, you can do Part 4 based on Part 2. There will only be one pipe displayed along with the number of pending executions.


Questions:

  1. Suppose there are 2 requestpipes and 3 lines to execute before a blank line and you are running on a machine with 4 CPUs. What is the maximum number of simultaneous executions that can occur? You are being asked to determine whether the 3 executions will be done in parallel with the above implementation. If not, how could you solve this problem (without increasing the number of pipes)?
  2. Look at the comment labeled Important below. Under what circumstances will the reply pipe not be empty when your mymake program terminates?


Testing your program.
You are responsible for creating tests cases for testing your program. After you have done this, copy your mymake and pipecompiler executables into a clean directory and untar this file in that directory. If you have completed Part 3 successfully, open three windows in this directory. In two the the windows execute:
./pipecompiler request1 reply and
./pipecompiler request2 reply.
In the third window, execute each of the following. In each case save the output from all three windows.
./mymake mymakefile1 reply request1 request2
./mymake mymakefile2 reply request1 request2
./mymake mymakefile3 reply request1 request2
./mymake mymakefile4 reply request1 request2

If you have Part 4 also working, execute
./mymake mymakefile5 reply request1 request2
You must make sure there are no additional copies of mymake running on your machine, or the test programs will not be able to send signals to your program. Make sure your program is receiving the signals sent by this final run. If you cannot figure out what is wrong, see me.

If you have Part 2 working, but not part 3, run the first four tests using 2 windows, one running the first pipecompiler and the other running the four tests above, omitting the last command line parameter.

Important:Before running each test, make sure that the reply pipe is empty. You can do this by using ls -l to see the amount of data in the pipe. If it is not empty, execute
cat reply
and then use CTRL-C to terminate the cat.


Handing in your assignment
Use this cover sheet. Consecutively number all of the other pages you turn in.

Be sure to clearly label the output of each of the windows for the tests that you turn in.

If a part of this assignment is not completely working, you must describe in detail exactly how far you got in trying to get it to work, what you tested, and what results you got from these tests. This is necessary if you want to receive partial credit for the work you did.