Project 5

Operating System Exercises

by Tim Kindberg. Revised October 2000

 

What is required

Your task is to measure the performance of local and remote invocations under Linux or another OS, in order to learn basic experimental techniques and to increase your understanding of and familiarity with invocation characteristics. The experiments involve use of one or more dedicated computers.

For each experiment, describe your methods: the number of repeats of the operation being measured, the number of the experiments, how you selected the final result, and its accuracy.


1 Timing

Do apropos time to obtain a list of all commands, system calls and library calls related to the topic of time. Take a while to browse documentation on some of these, using the man command. In particular...

Do man 2 gettimeofday. The system call gives time measured in seconds and microseconds. We shall assume that the microseconds figure are precise rather than accurate (we leave you to discover whether this is true of Linux). For the purposes of these exercises, we shall take the resolution of the system clock to be 100Hz.
 

2 Measuring System Call Time

Suppose you are timing a repeatable operation, such as the getpid system call, which you have reason to believe is of smaller or equal duration to the resolution of the system clock.

2.1 How can you nonetheless use gettimeofday to measure the time taken for one operation in milliseconds, to an accuracy of 2 decimal places? Quantify your answer!

Measure the time taken to perform a getpid system call.

2.2 What is the error due to execution of the control loop in your experiment? Explain how you determined this.

2.3 What accounts for variations in the figures you obtain for getpid?

2.4 How did you obtain a single timing figure from the varied measurements?
 

3 Measuring Process Creation Time

There is only one way to create a new process under UNIX: the fork system call. Do man fork to find out about it. Do man exit to find out how a process can terminate itself. Do man wait to find out how a parent can await its children's termination.

Write a program that uses fork to create as many processes as the system will allow and then awaits their termination. Make sure that each new process immediately calls exit. Run your program a suitable number of times and record the values obtained.

3.1 How many processes were created in each experiment?

3.2 How long, in milliseconds, does each fork + wait take? Careful: design a different experiment to the one you just produced. What is the accuracy of your answer?

Do man exec, to find out how to run a specified program in the calling process. You will find that various forms of exec exist. A simple form is execv, which requires a full path name for the program binary, and an argv pointer. Note that argv cannot be NULL (0).

Create a very simple program that immediately exits. Call the resultant binary fred, say.

Produce a modified version of the fork+wait program, so that each child process execs fred. Attach this program. Run this program several times and record the values obtained.

3.3 How do you account for the time difference between this case and the last experiment?

3.4 You probably found that the first measurement in the exec experiment was larger than subsequent ones. Why should that be?
 

4 Exploring the Address Space

Do man size to find out about this command. Use size with appropriate options on a binary fred to find out as much as you can about the layout of code and data for fred.

Find out the size of a page ? the smallest unit of mapped memory.

Input the following simple program, compile it, use size on the binary and convert size’s output to hexadecimal representation.

static char *aStaticString = "fred";
static int aStaticDataItem;
static int anInitialisedStaticDataItem = 0x666;

main(int argc, char *argv[])
{
    unsigned char *cp;
    unsigned char aCharacter;

    cp = &aCharacter;
    printf("address of character on stack = 0x%x\n", cp);
    printf("address of initialised string = 0x%x\n", aStaticString );
    printf("address of uninitialised int = 0x%x\n", &aStaticDataItem);
    printf("address of initialised int = 0x%x\n", &anInitialisedStaticDataItem);
    printf("address of procedure main = 0x%x\n", main);
}

4.1 Apart from the address of aCharacter, account for the address values printed by this program, relating them to the output of size.
 
4.2 Discuss whether you have sufficient evidence to determine the top of the stack’s address range.

Add the following code to the program above.

for(cp = &aCharacter; ; cp -= PAGESIZE) /* you need to #define PAGESIZE */
{
    aCharacter = *cp;
    printf("made it to 0x%x!\n", cp);
}

4.3 You should find that this program gives a ‘segmentation violation’ after a certain number of loop iterations. What has happened? Investigate whether the same effect occurs using a recursive procedure call to extend the stack, and account for what you find.

4.4 Write a program to walk the whole address space of a process, from address 0. The program should report address ranges in which write and/or read accesses to memory are possible. Attach a listing, and briefly explain here what you found by running the program. Explain your findings.

Hints: An attempt to access an address outside the address space causes a segmentation violation exception (SIGSEGV: do man signal). Alternatively, you may utilise system calls to check your addresses. Your program may take a long time; consider how crude you can make the search.
 

5 In-process invocation

5.1 How long does a procedure call with no arguments and no body take?
5.2 How long does a procedure call with 10 arguments and no body take?
 

6 Inter-process, intra-machine invocation

The program cliserv_rpc.c, when given "client" as an argument repeatedly calls sendto with a dummy data argument of size datasize (given as another program argument), followed by recvfrom with a 4-byte data argument. When given "server" as an argument it repeatedly calls recvfrom with a data argument of size datasize (given as the second program argument), then sends a reply using sendto with a 4-byte data argument. Note that this server does not perform any useful processing upon the arriving ‘requests’. Nor does it do a lookup to identify a (dummy) procedure or ‘method’ to call. Use some sensible port number for the server, unique on the machine.

Run a client and server together at the same machine, and test that they work together.

6.1 How long does a single request-reply interaction take when datasize is a) 4 bytes?  b) 1024 bytes?  Explain the accuracy of your answers, and how you arrived at them from the measurements.

6.2 How do you think the elapsed time for a local request-reply interaction is spent? Give a qualitative answer.
 

7 Inter-machine invocation

Run the server program and the client program between two different, dedicated machines.

7.1 How long does a single request-reply interaction take when datasize is a) 4 bytes? b) 1024 bytes? State the accuracy of your answers, and how you arrived at them from the measurements.

7.2 sendto uses UDP. This places an 8-byte header onto an IP packet, which in turn uses a 20-byte header. An Ethernet packet carries 18 bytes of its own header and checksum. The minimum Ethernet packet size is 64 bytes. Ethernet bandwidth is 10 or 100 Mbits/sec (find out about your Ethernet).

What is the total hardware transmission time (time "on the wire") for the 2 packets in a single remote request-reply interaction involving a) 4 bytes b) 1024 bytes?

How is the rest of the elapsed time for a remote request-reply interaction spent? Give a qualitative description, in terms of differences to your answer to 6.2.
 

8 Inter-machine asynchronous invocation

Adapt the previous program to produce the following. As a client, the program repeatedly calls sendto ? but not recvfrom. As a server, it repeatedly calls recvfrom ? and does not reply. Make the server report if any messages are lost.

Run a client and server at separate, dedicated machines. NB You may find that sendto fails for lack of buffer space after a certain number of sends. Try to accommodate this by reducing the number of attempted sends each time.

8.1 Do messages get lost? If so, how? How did you detect losses?

8.2 How long does each client request take if datasize is a) 4 bytes? b) 1024 bytes?
 

9 TCP and Connected Sockets

Adapt the program cliserv_rpc.c so that the client and server use a single pair of connected sockets for request-reply communication. NB It is a good idea to use the call setsockopt before calling bind on the socket you will use to accept a connection. This avoids a problem of your program not working because the bound address otherwise stays "in use" for a considerable time after the program has terminated:

     int option = 1;
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
 

9.1 How long does a single request-reply interaction take when datasize is a) 4 bytes? b) 1024 bytes?

9.2 How do you think the elapsed time for a remote request-reply interaction is spent? Give your answer in terms of how it differs from your answer to the corresponding part of 7.2, and in particular refer to the fact that TCP is a reliable protocol.
 

10 TCP for Request/reply Interactions?

10.1 If the server of question 9 takes a long time to reply, then does TCP send additional "are you still there?" or acknowledgement messages? Describe an experiment performed to check whether/how the TCP protocol actions vary with the amount of processing performed by the server. Hint: Use netstat -s to count the number of packets involved in request-reply exchanges. Comment on your results. Attach your program and any related information.

10.2 If you were designing an RPC/RMI system under Unix, would you use connected or unconnected sockets? Why? What are the merits of the other choice?