Skip to content

EXARTeo/Network_File_System-NFS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Network File System

Περιγραφή:

Στο συγκεκριμένο φάκελο, περιλαμβάνεται τα εξής:

  • Ένα απλό makefile (πατώντας make, κάνουμε compile όλα τα απαραίτητα προγράμματα, ενώ με make clean μπορούμε να διαγράψουμε τα αντικείμενα αρχεία που δημιουργήσαμε).
  • Ο φάκελος Modules, περιλαμβάνει όλα τα κύρια .c αρχεία (τρία από αυτά βρίσκονται μέσα στο folder nfs_manager).
  • Ο φάκελος Includes, περιλαμβάνει όλα τα .h αρχεία.
  • Ο φάκελος Common, περιλαμβάνει τα αρχεία τα οποία έχουν κάποιες κοινές συναρτήσεις μεταξύ των κυρίων προγραμμάτων.

Εκτέλεση:

Για να εκτελέσουμε το πρόγραμμα πατάμε make, και στη συνέχεια ./nfs_manager -l <manager_logfile> -c <config_file> -n <worker_limit> -p <port_number> -b <bufferSize> (όπου το -n είναι optional και αν δεν δοθεί ή δοθεί όχι θετικός αριθμός τίθεται σε 0, ενώ το -b, πρέπει να είναι θετικός αριθμός διαφορετικά θα ξανά ζητηθεί input από τον χρήστη). Ο "nfs_manager" μπορεί να τρέξει ανεξάρτητα του "nfs_console", αν θελήσουμε να τον τρέξουμε όμως (πατώντας ./nfs_console -l <console-logfile> -h <host_IP> -p <host_port>) επιτυγχάνετε σωστά και ομαλά η επικοινωνία μεταξύ "nfs_manager" - "nfs_console", μέσο των παραμέτρων -h <host_IP> και -p <host_port>. Ο κάθε client, τον εκτελούμε ./nfs_client -p <port_number> και υποθέτουμε πως τρέχει για το δοσμένο port_number επ αόριστον και έχει ξεκινήσει πριν από την εκκίνηση του nfs_manager. Επίσης θεωρούμε πως ο τερματισμός ενός client γίνεται πατώντας ^C στο τερματικό του.

Τρόπος Υλοποίησης:

nfs_console:

Απαρτίζεται από το nfs_console.c (και το common.c του φακέλου Common).

  • nfs_console.c: Eκτελεί όλες τις λειτουργίες για αποστολή εντολών (κάνοντας χρήση της συνάρτησης write_all() για σωστή και σίγουρη γραφή όλης της εντολής στο socket) και λήψη μηνυμάτων προς και από τον nfs_manager. Πριν στείλει κάποια εντολή, ελέγχει αν το "format" της εντολής είναι αποδεκτό, και έπειτα την στέλνει. Από εκεί και στο εξής αν προκύψει κάποιο λάθος, θα το εντοπίσει ο nfs_manager. Έπειτα αναμένει να πάρει απάντηση από τον nfs_manager με το αποτέλεσμα της εντολής, το καταγραφεί στο logfile του (ο nfs_console) και είναι πάλι έτοιμος να καταχωρίσει νέα εντολή.

nfs_manager:

Απαρτίζεται από τα αρχεία του φακέλου nfs_manager (μέσα στο Modules) και τα header του φακέλου Includes (και το common.c του φακέλου Common).

  • nfs_manager.c: είναι το κύριο αρχείο το οποίο εκτελεί την main().

  • manager_utils.c/h: περιέχει όλες τις επιμέρους συναρτήσεις για:

    • να γράφει τα αντίστοιχα μηνύματα στο terminal, manager-log-file και console socket.
    • να διαχειρίζεται σωστά LIST-PULL-PUSH commands του κάθε client.
    • να διαχειρίζεται σωστά το cancel και add command του console.
  • sync_info_mem_store.c/h: περιέχει τις δομές sync_info (αποθηκεύει τις πληροφορίες κάθε file), buffer_t (είναι ο τύπος του κυκλικού μας buffer) και worker_argv (αποθηκεύονται μόνο τα arguments που πρέπει να λάβει ο κάθε worker). Επίσης περιέχονται και οι συναρτήσεις των δομών για δημιουργία/διαγραφή κάποιου sync_info, αρχικοποίηση/διαγραφή ενός buffer_t αλλά και προσθήκης/αφαίρεσης/εύρεσης κάποιου στοιχείου sync_info στο buffer.

  • Λειτουργία:

Αρχικά, ελέγχει αν έχει γίνει σωστή εκκίνηση με σωστές παραμέτρους. Έπειτα, φτιάχνει το socket επικοινωνίας με τον nfs_console, για να μπορεί αμέσως ο cosnole να γράψει σε αυτό. Ανοίγει το manager-log-file (για να μπορεί να καταγράψει σε αυτό οποιαδήποτε στιγμή) και αρχικοποιεί τον βασικό κυκλικό buffer (ονομασία προγράμματος: sync_buffer). Ο buffer, αποτελεί την ουρά αναμονής των αρχείων που τίθενται για συγχρονισμό. Δημιουργεί τα αντίστοιχα worker threads τα οποία θα είναι όσο είναι και το worker_limit.

Έπειτα ξεκινάει την ανάγνωση του config. Για κάθε μια γραμμή του config, κάνει connect στο socket του αντίστοιχου client και στέλνει την εντολή "LIST". Διαβάζει όσα bytes μπορεί από το socket και λαμβάνει ορθά, "γραμμή-γραμμή", τα files του συγκεκριμένου <source>. Ελέγχει, ξεχωριστά, αν υπάρχουν ήδη στο buffer και αν δεν υπάρχουν τα προσθέτει ή αν είναι γεμάτο το buffer περιμένει μέχρι να έχει χώρο να τα βάλει (ΑΚΑ εκτελεί putitem() στον buffer).

Παράλληλα ο κάθε worker (τα threads που εκτελούν την δουλειά ενός worker) αφαιρεί κάθε φορά από ένα στοιχείο ή αν είναι άδειο το buffer περιμένει μέχρι να υπάρξει στοιχείο για να πάρει (ΑΚΑ εκτελεί getitem() στο buffer) και ξεκινάει αντίστοιχα τον συγχρονισμό του αρχείου. Κάνει connect στο socket του αντίστοιχου client (για το <source_file>) και στέλνει την εντολή "PULL". Διαβάζει από το socket, μέχρι να βρει τον πρώτο "space" χαρακτήρα για να καθορίσει το filesize. Έπειτα αν δεν προέκυψε κάποιο λάθος και έχει γίνει -1 το filesize(άρα ενημερώνει για το αντίστοιχο λάθος και προχωράει στο επόμενο item), κάνει connect στο socket για το <target_file>. Στέλνει αρχικά την εντολή PUSH /target_dir/file -1 για να διαγράψει ο,τι υπήρχε στο <target_file> και στη συνέχεια στέλνει συνεχόμενα PUSH /target_dir/file chunk_size data μέχρι, το άθροισμα όλων των chunk_size που έστειλε να είναι ίσο με το filesize. Τέλος ενημερώνει τον client του target, με PUSH /target_dir/file 0 για να τερματίσει την διαδικασία.

Σε όλη την παραπάνω διαδικασία υπάρχουν τα αντίστοιχα μηνύματα με το σωστό format και αν προκύψει κάποιο σφάλμα, ο συγκεκριμένος worker που βλέπει το λάθος, ενημερώνει τον χρήστη, μόνο αν επιτρέπετε με βάση το format των μηνυμάτων και απλά συνεχίζει να λάβει το επόμενο item.

Ο manager, είναι σε θέση να εκτελέσει οποιαδήποτε εντολή έχει λάβει από τον console, αφού τελειώσει την προσθήκη των αρχείων του confing στο buffer. Διαβάζει από το socket του console και εκτελεί την αντίστοιχη εντολή:

  • Υλοποίηση της add <source> <target>: Αν διαβάζει add, τότε εκτελεί την ίδια διαδικασία που εκτελεί για κάθε γραμμή του confing.

  • Υλοποίηση της cancel <source>: Αν διαβάζει cancel, τότε πηγαίνει στο κάθε item του buffer και ελέγχει αν έχουν το ίδιο source name με αυτό της εντολής cancel. Αν ναι, τότε αλλάζει το όνομα του source και του file, σε μια αδύνατη ονομασία, σε "\0". Έτσι πλέον το συγκεκριμένο item, είναι ένα "special item", το οποίο όταν θα το δει ένας worker θα ξέρει να το διαγράψει άμεσος και να συνεχίσει κατευθείαν στο επόμενο.

  • Υλοποίηση της shutdown: Αν διαβάσει shutdown, τότε σταματάει να λαμβάνει άλλες εντολές. Στη συνέχεια, προσθέτει στο buffer κάποια νέα "special item", ένα για κάθε worker. Στο πρόγραμμα αυτά ισούνται με NULL. Όταν ένας worker, δει ένα τέτοιο item, γνωρίζει να σταματήσει την εκτέλεση του. Έπειτα, κάνει pthread_join() τα worker threads, απελευθερώνει οποιαδήποτε δεσμευμένη μνήμη και τερματίζει ομαλά.

nfs_client:

Απαρτίζεται από το nfs_client.c (και το common.c του φακέλου Common).

  • nfs_client.c: Ο κάθε nfs_client ενός directory θα πρέπει να εκτελείτε πριν εκτελέσουμε τον nfs_manager. Δεν προκύπτει κάποιο σφάλμα σε άλλη περίπτωση, απλά δεν θα ληφθεί υπόψη κάποιο από τα αρχεία του για οποιαδήποτε διεργασία. Κάθε nfs_client, δημιουργεί το socket επικοινωνίας του με τον nfs_manager και περιμένει μέχρι να κάνει είτε ο nfs_manager είτε κάποιο worker thread connect σε αυτό. Μόλις βρεθεί κάποιο connection, δημιουργεί ένα thread για την εκτέλεση της εισερχόμενης εντολής (στο πρόγραμμα εκτελείται η συνάρτηση exec_request()) και έπειτα το κάνει pthread_detach() και το αφήνει να εκτελεστεί μόνο του. Καθένα από αυτά τα threads διαβάζει από το socket του nfs_manager, αρχικά τους 5 πρώτους χαρακτήρες (την εντολή + 1 για το "space") και εκτελεί την αντίστοιχη εντολή:
  • Υλοποίηση της LIST : Διαβάζει από το socket, όσους χαρακτήρες μπορεί μέχρι να μην έχει άλλους να διαβάσει. Στη συνέχεια, ανοίγει το directory που διάβασε, διαβάζει όλα τα ονόματα των αρχείων του περιέχει και γράφει στο socket το κάθε file_name προσθέτοντας '\n' στο τέλος του. Στη συνέχεια, ενημερώνει ότι δεν υπάρχει άλλο αρχείο να πάρει ο nfs_manager, γράφοντας στο socket ".\n" και τερματίζει η διαδικασία και το thread.

  • Υλοποίηση της PULL : Διαβάζει από το socket, όσους χαρακτήρες μπορεί μέχρι να μην έχει άλλους να διαβάσει. Στη συνέχεια, αποσπάει το συνολικό μέγεθος του αρχείου (που ζητήθηκε για PULL) και το στέλνει στο socket με την μορφή filesize <space character>. Έπειτα, αφού έχει ενημερώσει τον worker πόσους χαρακτήρες θα διαβάσει, απλά ανοίγει το file, διαβάζει κάθε φορά όσους χαρακτήρες μπορεί και τους γράφει όλους στο socket μέχρι να μην υπάρχει αλλος χαρακτήρας να διαβάσει. Τέλος, τερματίζει η διαδικασία και το thread.

  • Υλοποίηση της PUSH : Διαβάζει από το socket, όσους χαρακτήρες μπορεί μέχρι να βρει τα δύο πρώτα "space characters" (το πρώτο καθορίζει το όνομα του target_file και το δεύτερο το chunksize). Αφού πάρει τιμή το chunksizeελέγχουμε την τιμή του.

Αν είναι -1, ανοίγουμε απλά το <target_file> για τις επόμενες επαναλήψεις που θα γράψουμε σε αυτό και ξεκινάμε πάλι από την αρχή την διαδικασία αναγνώρισης του command (γνωρίζουμε ότι θα είναι PUSH ). Υπάρχει περίπτωση να έχει ήδη γίνει η ανάγνωση του επόμενου command από το socket, σε αυτή την περίπτωση, έχουμε φροντίσει να το διατηρήσουμε στη μνήμη, οπότε ελέγχουμε ορθά το επόμενο command κάθε φορά οποία στιγμή και να το έχουμε διαβάσει.

Αν είναι απλά θετικός αριθμός, διαβάζουμε από το socket και γράφουμε στο <target_file>, μέχρι να γράψουμε chunksize χαρακτήρες και όμοια συνεχίζουμε με το επόμενο PUSH.

Αν είναι 0 μόνο τότε κλείνουμε το <target_file> και πηγαίνουμε για να τερματίσει η διαδικασία και το thread.

Τέλος, να αναφέρω πώς σε όλο τον κώδικα υπάρχουν παντού αναλυτικά σχόλια με περισσότερες λεπτομέρειες για την κάθε μια λειτουργία.

Σας ευχαριστώ πάρα πολύ για τον χρόνο που αφιερώσατε!

About

A custom Network File System (NFS) implemented in C. Client/Server file system over TCP sockets with command-driven sync operations (LIST/PULL/PUSH) and a multi-worker manager for concurrency.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors