178 lines
9.1 KiB
Markdown
178 lines
9.1 KiB
Markdown
# Introduzione
|
|
|
|
Il progetto realizzato consiste in un file storage gestito da un server
|
|
multithreaded e un client che possono comunicarsi attraverso le funzioni
|
|
implementate dall'API. Il codice è presente sulla repository privata al
|
|
link: [tautocrono.it](http://tautocrono.it).\
|
|
Si può accedere usando le credenziali:
|
|
|
|
::: center
|
|
Username Password
|
|
---------- -----------------
|
|
Guest
|
|
:::
|
|
|
|
Il progetto è compilato in modo automatico tramite makefile con i
|
|
target: all, test1, test2, test3. Il target all fa partire un'altro
|
|
sottoprocesso make che eseguirà il target multi con degli argomenti
|
|
aggiuntivi, tipo `-j` in modo da rendere la compilazione multiprocesso.
|
|
Ci sono anche i target clean e cleanall che rimuovono gli eseguibili
|
|
creati con i precedenti target.
|
|
|
|
Sono inclusi anche dei file per testare le funzionalità del client e del
|
|
server nella cartella *testFiles*.
|
|
|
|
Gli oggetti vengono creati nella directory *obj/*, mentre gli eseguibili
|
|
vengono creati nella directory *build/*. La directory *src/* contiene il
|
|
codice che fa parte del main del server e del client; il resto del
|
|
codice è contenuto nella directory *lib/*.
|
|
|
|
È stata implementata sia la politica di rimpiazzamento FIFO che la
|
|
politica LRU.
|
|
|
|
# Server
|
|
|
|
Il server prende come unico argomento il path al file di configurazione.
|
|
Un esempio di file di configurazione è `config.ini` presente nella root
|
|
del progetto. La struttura del file è analoga a quella di un file
|
|
[INI](https://en.wikipedia.org/wiki/INI_file), quindi l'ordine degli
|
|
argomenti dentro a sezione è irrilevante. Il file di configurazione
|
|
viene letto da una libreria di \"*terza parte*\" ottenuta da
|
|
[rxi/ini](https://github.com/rxi/ini). Si sarebbe potuto usare la
|
|
libreria [Glib](https://gitlab.gnome.org/GNOME/glib) tuttavia si è
|
|
riscontrata una memory leak con valgrind.
|
|
|
|
La libreria threadpool e la libreria conn sono state tratte dalla
|
|
soluzione dell'esercitazione 11 pubblicata sulla pagina del corso. Si è
|
|
preferito l'utilizzo di `strsep` invece di `strtok` o `strtok_r` sia nel
|
|
programma server che client. Il codice è stato adattato leggermente da
|
|
quello presente nella libreria C GNU
|
|
([strsep](https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strsep.c;h=b534a1ec17fd2e91087af04abe1c3f1ac3e74ce0;hb=HEAD))
|
|
in modo da utilizzare la funzione omonima su piattaforme BSD e MacOS.
|
|
|
|
## Main
|
|
|
|
Il thread principale accetta le connessioni in arrivo da i client su un
|
|
socket di tipo `AF_UNIX` con nome di default *socket*. Viene interrotto
|
|
dal thread `sighandler_thread` se ricevuto un segnale fra: `SIGHUP`,
|
|
`SIGINT` o `SIGQUIT`.
|
|
|
|
## serverWorker
|
|
|
|
Il thread main dopo aver accettato la connessione di un client crea un
|
|
thread \"*worker*\" che aggiunge alla threadpool (definita nei file
|
|
omonimi). Ogni worker serve una singola richiesta da parte di un
|
|
qualsiasi client connesso. Se le richieste contemporanee dei client
|
|
fossero maggiori della dimensione della threadpool, le richieste vengono
|
|
accodate in una *pending queue* di dimensione stabilita nel file di
|
|
configurazione del server. La singola richiesta viene letta dal worker
|
|
thread e viene poi eseguita la funzione associata alla richiesta
|
|
presente nella libreria *apiFile*. Alcune di queste operazioni possono
|
|
richiedere la presa di una lock associata a un file, quindi per gestire
|
|
queste richieste si è fatto uso di una struttura di dati interna
|
|
`waiting_t`. La richiesta accodata non è quindi gestita dal thread
|
|
corrente, ma dal thread che rilascerà la lock al file associato. Si
|
|
evitano quindi *polling* o l'utilizzo di *condition*.
|
|
|
|
## sighandler_thread
|
|
|
|
Il thread gestisce i segnali in arrivo al server. Se il segnale ricevuto
|
|
è di tipo `SIGINT` o `SIGQUIT`, il server termina prima possibile. Se il
|
|
segnale ricevuto è del tipo `SIGHUP` invece si attende che ogni worker
|
|
abbiano servito le richieste dei client precedentemente connessi.
|
|
|
|
# Storage
|
|
|
|
Il server gestisce la memorizzazione dei file tramite la libreria
|
|
*fileQueue*. Nel header file *fileQueue.h* la costante `ALGORITHM`
|
|
imposta la politica di rimpiazzamento. Le funzioni che differiscono in
|
|
comportamento sono: `dequeueN`, `lockFileInQueue`, `unlockFileInQueue`,
|
|
`openFileInQueue`, `find`, `request`, `searchFile`. Infatti l'elemento
|
|
che richiedono come input viene accodato alla fine della struttura nella
|
|
politica LRU. Le altre funzioni invece non modificano l'ordine degli
|
|
elementi.
|
|
|
|
La struttura principale è una linked list chiamata `nodeT` che contiene
|
|
i dati del file associato e un puntatore al prossimo elemento. Dato che
|
|
l'operazione di scrittura di un file in memoria non è atomica, si è
|
|
deciso di usare due misure dell'occupazione della memoria di un file:
|
|
`size` e `valid`. L'unico momento in cui potrebbero essere differenti è
|
|
quando il client ha richiesto la scrittura di dati, comunicando la
|
|
dimensione, quindi il server \"alloca\" tale dimensione come `size`,
|
|
lasciando invariato la dimensione `valid`. Dopo che il client ha inviato
|
|
i dati da inserire nel file, le dimensioni dovrebbero di nuovo
|
|
combaciare. Una singola operazione di scrittura può richiedere più di un
|
|
file rimosso per aver abbastanza memoria libera; questa operazione viene
|
|
attuata atomicamente e viene considerata come un'unica operazione di
|
|
rimpiazzamento.
|
|
|
|
# Logging
|
|
|
|
Il server usa la libreria \"`taglialegna`\" per scrivere i messaggi di
|
|
log su un file specificato nel file di configurazione. Le scritture
|
|
vengono sincronizzate tramite lock. Sono state scritte due funzioni che
|
|
permettono la scrittura: `taglia_write`, `taglia_log`. `taglia_log`
|
|
scrive anche un timestamp prima del messaggio di log. All'inizio
|
|
dell'esecuzione del server vengono stampate alcune informazioni lette
|
|
dal file di configurazione, come ad esempio la dimensione del threadpool
|
|
o la dimensione massima della struttura di cache del server. Al termine
|
|
vengono invece scritti alcuni dati di sunto delle operazioni, ad esempio
|
|
il numero massimo di file memorizzati o la dimensione massima raggiunta.
|
|
|
|
# Client
|
|
|
|
Oltre alle funzioni di API richieste dalla specifica è stata
|
|
implementata la funzione `setDirectory`, perchè si è interpretata come
|
|
se le opzioni `-d` e `-D` dovessero poter essere associate a più di un
|
|
comando. Tuttavia questa funzionalità non è presente nel client in modo
|
|
diretto dato che vengono inserite `-d`/`-D /dev/null` fra due opzioni
|
|
`-w`/`-W` o `-r`/`-R` successive. L'opzione `-d /dev/null` o
|
|
`-D /dev/null` è stata ottimizzata leggermente in modo da non fare
|
|
chiamate di sistema.\
|
|
Il client esegue prima l'opzione `-f`, in seguito l'opzione `-t`, poi in
|
|
ordine tutti gli altri argomenti. In caso di errore durante la scrittura
|
|
su un file dopo la creazione, il client richiede la rimozione del file.\
|
|
Per la comunicazione con il server si è optato per un protocollo
|
|
ispirato a quello html o ftp, costituito da due cifre, la prima denota
|
|
il risultato (positive completion/transient negative
|
|
completion/permanent negative completion), la seconda invece è una
|
|
specifica della prima. Non tutte le combinazioni sono state usate.
|
|
|
|
::: center
|
|
Number Meaning
|
|
---------------------------- ------------------------------------------------------------
|
|
2[\*]{style="color: gray"} positive completion reply
|
|
4[\*]{style="color: gray"} transient negative completion reply (command not executed)
|
|
5[\*]{style="color: gray"} permanent negative completion reply (errors)
|
|
[\*]{style="color: gray"}0 syntax
|
|
[\*]{style="color: gray"}1 information
|
|
[\*]{style="color: gray"}2 connection
|
|
[\*]{style="color: gray"}5 file system
|
|
:::
|
|
|
|
Di seguito una tabella in cui si associa ogni numero al codice usato:
|
|
|
|
::: center
|
|
Number Meaning Code
|
|
--------------------------- --------------------------------- ----------------------------
|
|
20 OK `MEOK`
|
|
[21]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
[22]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
25 file purged `MEFP`
|
|
[40]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
[41]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
42 shutting down `MESD`
|
|
45 requested file action not taken `MENT`
|
|
50 syntax error `MESY`
|
|
[51]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
[52]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
|
55 server error `MESE`
|
|
:::
|
|
|
|
Il client invia per primo la dimensione del messaggio come `long`, poi
|
|
il messaggio. Il server risponde prima con due caratteri che
|
|
simboleggiano is response number, seguito dal errno se si è riscontrato
|
|
un errore, oppure i file richiesti o espulsi. Il server per inviare i
|
|
file richiesti o espulsi, prima invia il numero di file, poi per ogni
|
|
file la dimensione come `int64_t` e poi successivamente il file.
|