Conclusioni


Tutto sommato questo progetto può essere considerato come una grossa esercitazione che ha consentito di analizzare alcuni argomenti del corso utilizzando gli strumenti di comunicazione forniti da UNIX. La specifica di toleration fault ha consentito di poter analizzare da un punto di vista progettuale  due modelli di replicazione dei quali il modello a copia fredda dà un' idea, seppure specifica all' applicazione, di cosa significhi la necessità di ricostruire lo stato di un sistema  in seguito ad un crash  di un suo componente.

In merito al protocollo di elezione va detto che si deve  considerare l' eventualità che al NS CALDO giungano contemporaneamente due Bcast di elezione per i quali occorre effettuare una discriminazione per impedire che entrambi vengano eletti come NS FREDDI. Tale discriminazione si è  ottenuta facendo riferimento al tempo di invio del Bcast di elezione. Non nego il fatto che si tratti di una scelta poco felice a causa della impossibilità di definire il concetto di tempo in rete, però si può osservare che in questo caso ciò che importa è che il NS CALDO abbia un parametro di valutazione che gli consenta di scegliere un NS in elezione da eleggere come NS FREDDO.

L' impiego delle sockets, in un progetto nel quale la comunicazione tra processi scorrelati ha un ruolo fondamentale, ha evidenziato l' importanza nel considerare il tempo come paremetro di programmazione. Sembra paradossale ma in alcune circostanze è fondamentale imporre la sequenzialità in un codice orientato alla concorrenza. Il punto è che in termini di qualità di servizio è bene realizzare servers paralleli, cioè implementati con codice concorrente, però è fondamentale imporre la sequenzialità nell' esecuzione durante le interazioni con i Cs.

 Mi riferisco in particolare alle sockets STREAM per le quali la send() del C deve seguire temporalmente la accept() del server, altrimenti il codice del server si sospende indefinitamente su di una recv() che attende un messaggio che in realtà il C ha già trasmesso. Questo errore di programmazione produce un comportamento del codice non deterministico in quanto dipende dallo scheduling del kernel e ciò comporta  errori intermittenti difficili da individuare.  

Anche la shared memory (SM) se non è usata correttamente produce errori subdoli in quanto determinano  dei segmentation faults  difficili da individuare. Ciò succede quando si ampia la struttura della SM senza preventivamente averla deallocata. Questo errore, effettivamente grossolano, è molto facile da commettere  in quanto la primitiva di creazione della SM che faccia riferimento alla medesima chiave non determina la creazione ex novo del segmento di SM ma comporta semplicemente il suo riferimento. La primitiva di create per la SM non ha un comportamento analogo della create per i files.

 

Il progetto mi ha dato modo di verificare con mano quanto sia vantaggioso sviluppare il progetto in un ambiente standard che consenta una completa trasportabilità del codice. In merito ad un progetto in ambiente UNIX, che purtroppo standard non è, è fondamentale individuare la versione alla quale l' applicazione è destinata, per evitare di avere spiacevoli sorprese in termini di funzionalità di comunicazione non supportate (vedi socket asincrone in Sys.V): segnali non riconosciuti, sintassi e compotramento delle stesse primitive differente.

Inoltre l' aver sviluppato il codice sia in LAB2 che in LINUX sul PC di casa (quindi in ambiente locale) mi ha permesso di constatare che simulare codice di rete in ambiente locale non solo richiede esperienza in termini di programmazione di rete, ma comunque richiede una prova del codice su di una rete vera, prova che generalmente mette in evidenza errori di programmazione.

Concludo con un osservazione sui demoni. Un server di rete che si rispetti deve essere implementato come demone. Lo sganciarsi dal terminale è sicuramente una tutela nei confronti di interruzioni accidentali da tastiera ma diviene un inconveniente in ternimi di verifica del funzionamento a meno che non si usi un debugger. Tale problema si presenta anche quando è necessario riprendere il codice in un secondo tempo per apportarne qualche modifica . A tale proposito credo sia opportuno mantenere aggionate sempre due versioni del progetto: quella ufficiale realizzata con i denoni ed una ufficiosa che non utilizza i demoni, molto utile nella fase di debugging se non altro perchè basta una interruzione da tastiera per uccidere un processo piuttosto che una serie di primitive che consentono di ricercare il particolare processo da uccidere.