Due date: Thursday October 14th, by Midnight
Warning! You must have your project in by Midnight on Friday, October 16th at the latest! Projects will not be accepted after this time! We need to get all the projects graded as quickly as possible to evaluate course grades.
There are two parts to this assignment. The first involves modifying your server from your project 3 to use threads. The second part involves some simple performance comparisons between processes and threads. Most of this document describes how to modify your server. The Performance section describes the comparisons you are to do.
You are to make the Electronic Handball Server in Project 3 multi-threaded. The external functionality of the server and the client will remain identical to that of Project 3, but internally the server will have multiple threads of control.
The main server thread will wait on the socket, accepting
connections. Upon receiving a connection (via the
sockaccept()
call), the server will spawn a new thread to
handle the connection. The main thread will then loop back up to
accept more connections. Thus, there will be a new thread created per
connection.
As usual, all system calls, including those involving threads,
should be error checked. In most cases, the server can simply exit if
a system call fails, but this should be done on a per thread
basis, and not a per process basis as before (ie- use
pthread_exit(NULL)
instead of exit()
).
Since the server will no longer need to communicate to other
fork
'ed processes, shared memory will not be needed (or
select()
, if you used the single server process solution
to your project 3). Instead, all the server threads will share data
through global variables. Since multiple threads may be accessing a
global variable, access must be protected. Pthreads provide a simple,
primitive synchronization variable called a mutex. It provides
a single, absolute owner of a section of code (the critical section)
that is bracketed between calls to pthread_mutex_lock()
and pthread_mutex_unlock()
(see Hints below for details). The first thread that
locks the mutex gets ownership, and any subsequent attempts to lock it
will fail, causing the calling thread to go to sleep. When the owner
unlocks, it, one of the sleeping threads will be awakened, made
runnable and given the chance to obtain ownership of the mutex.
You will use the same socket code wrappers for this project as you did for project 3 to allow for client-server communication.
You will use the Pthreads library for your threading.
(Note, gcc
was only recently compiled to support pthread
code. So, you may consider using cc
, the native Compaq
compiler, to build your project.) See the server.c
and
client.c
samples for examples of how to make a basic server and client
threaded.
You will need a #include < pthread.h >
in your
source code and -lpthread
as an option to the linker to
use pthreads. See the below Makefile
below for a sample.
Do a man
on any of the below calls for more
information:
The functions pthread_create()
,
pthread_kill()
and pthread_exit()
can be
used to create and destroy threads.
add1.c
in the Samples section for a specific
example.
The mutex functions pthread_mutex_init()
,
pthread_mutex_lock()
,
pthread_mutex_unlock()
and
pthread_mutex_destroy()
will be used for your mutex
variables.
add2.c
in the Samples section for a specific
example.
You might use pthread_join()
to cause one thread
to wait for another to finish.
Warning! Some library functions are not thread safe because they
use global or static variables, as discussed in
class. strtok()
is one of them. Read the man page to see
if such system calls are thread safe. Often, there is a thread safe
version, such as strtok_r()
.
Since you will be maintaining two source code programs for this project (the client and the threaded server), as well as the socket wrappers, you will want a Makefile. Here is a sample:
# # makefile for threaded client-server (using pthreads) # LIB = -lpthread CC = cc all: server client server: server.c sock.o $(CC) -o server server.c sock.o $(LIB) client: client.c sock.o $(CC) -o client client .c sock.o $(LIB) sock.o: sock.c sock.h $(CC) -c sock.c clean: /bin/rm -f client server core *.o *~
See the man page for make
for additional information
on how to use makefiles.
You are to experimentally measure some performance parameters for
your threaded server versus multiple-process. Specifically, you will
use getrusage()
to measure:
The easiest way of doing this is to have each child process (in the
forked server) print out getrusage()
statistics for
itself. The system call getrusage()
returns some process
statistics that are kept by the operating system. See count.c
for a sample of how to use
getrusage()
and do a man getrusage()
for
more information. For your project, you may print the statistical
information to a file or just capture it from standard output. You
can then analyze the data by adding the values across all
children.
For the thread based server, you do not need to gather data for all the children and then add it. Instead, you just have the main thread gather data about itself.
You should do multiple runs with an increasing number of clients, say at least 1, 2, 3 and 4. Fixing a certain number of clients, you should also varying the length of the client connections, say from 10 seconds up to 2 minutes.
After gathering the data, you must analyze it. Draw at least two graphs:
Each graph should show two lines, the threaded server performance and the forked() server performance. The graph should have a title, and the axes should be clearly labled with the units. You should also compute average values for each of the statistics gathered above. Draw conclusions on what you see in the graphs. In particular, you must explain any results that result in conclusions you draw up given your data.
You might use gnuplot
to generate your graphs.
Gnuplot is an interactive plotting program. Do a man
gnuplot
for more information. Gnuplot has an
online help in it's shell, too. Just type "help".
Be sure to turn in all
code needed to compile your
program, including the socket wrappers, Makefile and client code. Make sure
you have the names and login ids for all group members. In
addition, turn in the code you used to do your performance evaluation
along with your performance writeup. You may turn in your graphs
electronically as long as they are in postscript, excel or some other
format that I can read. Please compress your files before turning
them in.
Send all questions to the TA mailing list.