This repo contains my version of the final project for the Lab module of the Operating Systems and Lab course at UniPi, Academic Year 2020-2021. The project is released under a MIT License.
It implements a simple file-storage server that receives files and concurrent requests from several clients. The server process stores the files in memory (RAM) and acts as a file cache: whenever the reserved space is full files are ejected and sent back to clients. The client-server communication is based on an API library that implements the protocol for several operations, such as opening/reading/writing files and many more.
The main source files are in the src/
subdirectory; include files are in includes/
. The obj/
and libs/
subdirectory are used for object files and dynamic libraries generated at compile time. The bin/
subdirectory will contain the executables after a successfull compilation.
config/
contains a single file, config/config.txt
which specifies the main parameters for the server process.
logs/
is the default directory in which log files written by the server app are stored. They can be parsed and analysed through the scripts/statistiche.sh
script (which will be described later).
report/
contains a report on the main implementation details of this project (written in italian).
scripts/
contains some Bash scripts used to implement tests.
tests/
contains random files to test some aspect of the project (mainly the client options and the replacement algorithm).
Some directories contain a .keep
file: this file is used only to ensure that Git loads the directory.
A Makefile
was created to ease the compilation procedure. It supports several options:
make
compiles the whole project, creating the directories for object files, binaries and libraries if they don't existmake clean
cleans all auxiliary files and executablesmake test1
andmake test2
launch respectively the first and the second test. In particular the second test provides two more options:make test2FIFO
to test the FIFO replacement algorithmmake test2LRU
to test the Least Recently Used replacement algorithm In the current version,make test2
is equivalent tomake test2LRU
.
make cleanTest1
,make cleanTest2
andmake cleanTests
to clean files generated by the various testsmake stats
tries to analyse the last log file present inlogs/
using thescripts/statistiche.sh
script.
The server config file is (by default) located in config/config.txt
but can optionally be specified by passing the config path as an additional argument to the server executable. The config.txt
file has the following format:
no_worker = <number of worker threads>
max_space = <max space in MBytes>
max_files = <max number of files>
cache_pol = <cache replacement policy, may be FIFO or LRU>
sock_path = <path to socket file>
path_dlog = <path to the directory containing log files>
There might be any number of spaces before or after the equal sign, or any number of empty lines between two different key=value
lines, but all keywords must be present once and only once.
A possible client is implemented and (after a successfull compilation) may be executed by giving the command ./bin/client
. All options and requests are given to the client through the command line. The possible optional argument can be seen with the option -h
.
The communication client-server is implemented through a dynamically linked library libapi.so
. The API library may be freely used with a different client, but since it depends on a utility library libutil.so
the two must be linked to the client object files.
The scripts
directory contains some useful scripts:
scripts/create_dirs.sh
creates several subdirectories (such asobj/
,bin/
,libs/
,logs/
and others) if they don't already exist (it is automatically called bymake
);scripts/test1.sh
andscripts/test2.sh
are two tests (they may also be called through the Makefile, as described before). In particular the latter might be called with an optional argument representing the chosen replacement algorithm, which must be eitherFIFO
orLRU
. If no optional argument is given,LRU
is assumed;scripts/statistiche.sh
computes some statistics on a log file generated by the server: if an optional argument is given it's interpreted as the path to a log file, otherwise the scripts takes the last log file present in thelogs/
directory.
The following table describes what optional features (optional for the summer exam term) were implemented.
Feature | Implemented? |
---|---|
LRU alg. | ✔ |
LFU alg. | ✗ |
Log files | ✔ |
File stats.sh | ✔ |
Compression | ✗ |
test3 | ✗ |
Send files to client (option -D) | ✔ |
Lock (-l) | ✗ |
Unlock (-u) | ✗ |
- Source files don't have to be so split up. It isn't an error and I did it for my own sanity, but it isn't necessary.
- To improve the logging performance, the logger function might maintain an internal buffer - that is, a list of things to print - and print the whole buffer once it reaches a certain capacity.
- The logger should print something in case of fatal errors - for example a failed
safe_malloc
, that automatically aborts the process with anexit(EXIT_FAILURE)
. - Utility functions (such as those in the
libutil.so
library, likesafe_malloc
orsafe_pthread_*
) mustn't abort the process without giving any choice to the programmer, even if the error is always fatal.- One way to solve this problem is to add an additional
bool
parameter to those functions: if the parameter istrue
on error the process is aborted, otherwise an error code is returned. This way the programmer may use the function safely and with full control over the function's behaviour.
- One way to solve this problem is to add an additional