9.1 KiB
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.
Si può accedere usando le credenziali:
::: center Username Password
Guest CNs7Vbx3vLJQ2aK
:::
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, 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. Si sarebbe potuto usare la
libreria 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)
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.