CS 3733 Operating Systems, Fall 2005 Assignment 3


Due Thursday, October 27, 2005

Assignment 3 has 6 parts.

This assignment is based on the client sever model using named pipes. You will run a single server that will take requests from multiple clients. Requests will always come in on a fixed named pipe. Responses will be issued on a pipe whose name is passed as part of the request. The client will keep track of performance statistics.

Each time you start a new part of this assignment, create a new directory. When you are finished, you should have an assign3 directory with subdirectories part0, part1, part2, part3, part4, and part5. When creating one of these directories, start by copying into it the contents of the previous directory. Each time you write a main program, use lint and make sure you understand all of the warning messages. Save the lint output. Read this information about running lint. Review the comments from Assignment 1 and make sure you do not make the same types of errors.

Obtaining help
You are to do this assignment on your own, but you may ask me for help at any time. The earlier you start the assignment, the more help will be available. You may obtain help from me before or after class, by coming to my office, or by email.

Read through the entire assignment and look at the cover sheet before writing any code. You will be graded not only on the correctness of your programs, but their clarity and the methods you used for testing.

Motivation

The client server model is used for many types of communication. One example is the web server/web browser. The web server waits for requests to come in from a browser. Each request is processed and the result is sent back to the browser. One important issue is performance. In a later assignment, you will examine the performance of a server that communicates with clients over a network. In this assignment you will do something similar, but communication will be between processes on the same machine. Communication will be done using named pipes (FIFOs). A single request server will process all requests. A client will send multiple requests to the request server and receive the responses.

Web servers must be robust. They typically are started with the booting of the OS and stay up until the machine is brought down (or crashes). Web servers should not terminate just because a request from a browser is invalid. A web server with a memory leak will eventually use up all of the memory resources of the server machine and will not be able to perform its desired function. It is critical that web servers to not have any memory leaks. The servers that you write should be robust.

In Part 0, you will make sure your functions in serialize.c from assignment 1 are working. We will use the parse_for_int_token function in a trivial way in this assignment. You will be able to do most of the assignment even if this is not working, but you will need this routine in a later assignment. In Part 1 you will write the basic server and client programs. In Parts 2, 3 and 4 you will make your programs more sophisticated. In Part 5 you will do some additional testing.


Part 0:
If you haven't done so already, make sure your serialize and deserialize functions from assignment 1 are working correctly. Put the prototype for parse_for_int_token in serialize.h and make sure it has external linkage. These should be in the file serialize.c.

Part 1:
Obtain copies of Programs 6.7 and 6.8 from USP. These programs are called pipeserver and pipeclient. Also get a copy of restart.c and restart.h from USP, Chapter 6. Make sure you can compile and run these programs. Copy pipeserver.c into pserver.c and copy pipeclient.c into pclient.c. Modify the makefile to compile and lint these programs. Do not modify either restart.c or restart.h.

Modify pserver.c so that instead of calling copyfile it reads lines from requestfd in an infinite loop. Use the readline function from restart.c. After reading a line pserver calls processline which takes a single string parameter. It then goes back and reads the next line. For now, just write a simple processline that outputs the line to standard output. Test this with your pclient program.

The readline function is given a buffer of a given size and returns an error if it is sent a line that is too long. You can assume that valid lines will be shorter than 1024 bytes. Your pserver needs to correctly process valid lines and not crash when it gets an invalid one. Have pserver write an error message to standard error when it gets an invalid line. Do not call processline or terminate the loop when an error occurs. pserver should only exit if it cannot initially open the pipe whose name it gets from the command line, or it receives a signal (e.g. from CTRL-C).

Part 2:
Modify pclient so that it takes two command line arguments, the name of a request pipe (as before) and the name of a response pipe. The program will create the response pipe if necessary, open it for reading and writing, and send the name of the response pipe as a single line (ending with a newline but no string terminator) to the request pipe. It will then read a line from the response pipe, output the line to standard output, and terminate. You may assume that a valid response line contains at most 1024 bytes. Your program should handle errors gracefully and should not crash or experience a buffer overflow under any circumstances.

Modify the pserver program so that it works with pclient. The parameter to processline will now be the name of an existing FIFO. Have it open this pipe for writing and send a single line (terminated by a newline, but no string terminator) to this pipe. It then closes the pipe. processline will keep track of the number of times it has been called and the number of times it was unsuccessful. The line it sends will be a short message of the form:
Server: xxx has processed: yyy lines with this many errors: zzz
where xxx is the process ID of the server, yyy is the total number of times processline has been called, and zzz is the number of times it was unsuccessful. It is unsuccessful if it cannot open the pipe or an error occurs when it writes to or closes the pipe.

When this is working, have pclient use parse_for_int_token to determine the three integer values in the message it receives and output them appropriately on separate lines. (Here, appropriately means that each line also contains a description of what the number represents.) If an error occurs, an appropriate error message is output instead. Send normal output to standard output and error messages to standard error.

If you cannot get your parse_for_int_token to work, just output the line you have read unchanged.

Part 3:
Modify pserver so that processline does a calculation before sending back its result. Have it generate a fixed number of random numbers using drand48. See the man page for details. Pass the number of random numbers to generate as an additional command line parameter of pserver. Use a value such that it takes about 500 ms. to generate the random numbers on the machine you are using. It is up to you to figure out how to do this. One possibility is to write a test program that will generate n random numbers and find the value of n that will cause the program to take about 5 seconds. Keep in mind that you might have to use a different value of n when you run on a different machine or there is a different load on the system.

Read pages 301 - 307 of USP about times and timers. Have your client program use a method similar to Program 9.1 to determine the number of microseconds needed between the request to the server and the response. The first call to gettimeofday should be right before the write to the request pipe and the second call should be right after the readline from the reply pipe. No other I/O should be done between these calls. Print the number of microseconds on a descriptive line. Now modify pclient so that it takes an additional command line argument, the number of requests to make. Put the write and readline in a loop, making this number of requests. Call gettimeofday only twice, once before the loop starts and once after it to determine the number of microseconds it takes to make all of the requests. Do not do any other I/O in the loop. After the second call to gettimeofday output the number of microseconds and information from the last response from the server. Do not output the information from the intermediate responses, only from the last response after the timing has been done.

Perform the following experiment. Open 3 windows. Run the server in one window. Choose your parameters so that a request is handled in about 500 milliseconds. In a second window run the client with 10 requests. Record the results. In a third window, set up to run the client again with 10 requests. Start the clients in windows 2 and 3 at almost the same time. Make sure the two clients are using the same request pipe but different reply pipes. Record the result, including what machine you ran on.

Part 4: (revised) Copy pserver.c into pserverp.c and make perversp.c into a parallel server. To do this, after a request comes in, fork a child to call processline. The child calls processline and exits. The parent forks the child and performs the next iteration of the loop. Do this in such a way that the parent still keeps track of the total number of times that processline has been called (by all of the processes). This should be equal to the number of forks. Think about the meaning of the count of the number of errors reported by your version of processline. You do not have to modify the program so that it correctly keeps track of the total number of errors.

Part 5 Do the experiment from Part 3 with the server from Part 4. Run two clients at the same time using different reply pipes. Run this on the same machine you used for the Part 3 tests. If necessary, rerun the Part 3 tests so they are on the same machine as the tests for this part. Do you get better performance with the parallel server? Why or why not? Under what circumstances will the parallel server perform significantly better than the serial server?


Handing in your program:
Use this cover sheet. Consecutively number all of the other pages you turn in. Each page that contains your work should have a number on it. Make sure you have answered the questions from the cover sheet. Turn this in at the beginning of lecture on the due date.