Use the SUN Cereal machines in Hill 248 to run all the programs.
(A) User level threads
(1)
Implement a user-level non-preemptive threads library. Along with your
code, you should hand in a makefile that creates a threads library (called libuthreads-np.a)
that anyone wishing to use your threads package (in particular, Suresh :) can
simply link to.
The API exported by your thread library should have the following primitives:
uthr_create(void
*(*start_func)(void *), void *arg, int flag, int
*thread_id)
Functionality : Create a new thread
start_func, arg : The newly created thread starts executing
start_func, with argument arg.
flags : It's enough to support just the THR_DETACHED flag.
thread_id : Unique identifier for the newly created thread
uthr_exit(void
*status)
Functionality : Exit/Terminate the current thread
status : Exit status of the thread which terminates
uthr_yield()
Functionality : Yield the processor voluntarily
uthr_join(int
target_thread_id, void **status)
Functionality : Wait for the target thread to exit.
target_thread_id : thread_id of an undetached thread for which
the current one waits.
status : Exit value of the target thread
For
the scheduler, use a simple round robin scheme. Measure the
latencies of thread creation and switching threads using your thread
library, and compare it with the (kernel) thread library available on
Solaris.
Hint
You may want to check man pages for getcontext, makecontext,
swapcontext, setcontext etc.
(2)
Extend the thread package from part (1) to handle pre-emptive scheduling.
A thread can be pre-empted if it is executing in the application code. For
pre-emption, set a high-resolution timer, and when the timer expires, do the
scheduling. A thread cannot be pre-empted when it is executing the scheduler
code or library functions (since they are not guaranteed to be re-entrant.)
To find out whether the thread is executing in the application code, one
solution is to to use two dummy functions (DUMMY_BEGIN() and DUMMY_END()) to
mark the beginning and end of the user code. Now, scheduling can be done only
if, at the time of the timer expiration, the return address (program counter)
lies between your begin and end markers.
Hint
The code fragment given above for question C can be used to find the program counter here also.
(The makefile should create a library called libuthreads-p.a.)
(3)
Augment your thread library from part (2) above to provide support for mutexes
and condition variables. You need to define two types, mutex_type, and
condvar_type.
You need to support the following function calls in your thread library.
mutex_init(mutex_type *mutex) - Initialize mutex
mutex_lock(mutex_type *mutex) - Lock mutex
mutex_unlock(mutex_type *mutex) - Unlock mutex
cond_init(condvar_type *cond) - Initialize cond
cond_wait(condvar_type *cond, mutex_type *mutex) - Wait on cond
cond_signal(condvar_type *cond) - Signal and release
1 thread if any are waiting on cond
cond_broadcast(condvar_type *cond) - Release all threads
waiting on cond
Hint
Check the man pages for the above calls to understand their semantics correctly.
(The makefile should create a library called libuthreads-pmc.a.)
(B) Remote File Server (Client/Server programming using sockets)
1) Write C programs to measure and compare the file I/O bandwidth and latency for read and write operations (for various block sizes) using the following 2 methods:
read/write/
mmap
Hints
To measure the bandwidth, use a range of large block sizes and repeat the operations over a period of time and find the average bytes/sec throughput the operation can achieve.
To measure latency, repeat the operations with very small block sizes and measure the average time taken for a single operation.
Try to filter out the effects of the kernel buffer cache - by accessing different files over different days.
2) Write a remote file server program that supports the following file operations: creat, open, seek, read, write, close and unlink.
For the client side, you should provide a library that an application can link to access the files at the server. The library should implement the following interfaces:
int u_init()
int u_creat(char * path, int mode)
int u_open(char *path, int flag, int mode)
int u_lseek(int fd, int offset, int whence)
int u_read(int fd, char * buf, int count)
int u_write(int fd, char *buf, int count)
int u_close(int fd)
int u_unlink(char *path)
u_init() should do the connection setup and any initialization at the client side. Rest of the functions have the same semantics as normal file system operations.
The server should be implemented as a multi-threaded daemon process. One of the threads will be just waiting in a loop for client requests to arrive at a UDP port. Once the requests arrive, it will queue the request in a task queue and return to listening. One or more 'worker' threads will be waiting for tasks to appear in the queue. When tasks arrive, they will pick them up one by one and execute them, and return the result to the client.
(Why is multi-threading required? If you implement this in stages, implement a single-threaded sequential server first, which gets a request, processes the request, returns the results and then goes back to listening for requests. Compare the performance of this with the multithreaded one.)
Measure the performance of the remote file operations and compare them with the results from part (1) and justify the numbers (in terms of the additional protocol and communication costs etc.) Analyse the effect of the UDP packet size (block size of the communication), if any, on the performance. Measure the scalability performance of the server by testing with multiple clients (1, 2 and 4 clients).
So for this part, the performance measurements will have a few orthogonal parameters - the operation's block size, communication block size and varying number of clients.
Extra Credit: Implement some kind of caching scheme and pre-fetching at the server that will improve the performance of the server.
Hint
For UDP/socket programming, refer to "Unix Network Programming" by Richard Stevens or other similar books on Advanced Unix/network programming.
(You will be provided a sample benchmark program soon.)