Κατασκευή ενός Job Dispatcher
Εισαγωγή
Ένα σύστημα GRID αποτελείται από πολλές υπολογιστικές μηχανές, συνδεδεμένες μεταξύ τους, οι οποίες έχουν ως στόχο την εκτέλεση προγραμμάτων (jobs). Για την ορθή λειτουργία ενός GRID, συνήθως χρησιμοποιείται ένα λογισμικό (GRID middleware), το οποίο, μεταξύ άλλων, αναλαμβάνει την αντιστοίχηση ενός προγράμματος σε κάποια από τις διαθέσιμες μηχανές, σύμφωνα με κάποια κριτήρια (διαθεσιμότητα σε πόρους μνήμης, υπολογιστικής ισχύς κ.λπ.). Στα πλαίσια αυτής της άσκησης, ονομάζουμε εκλαϊκευμένα, το εν λόγω κομμάτι του GRID middleware ως 'Job Dispatcher'.
Όταν ένας χρήστης θέλει να εκτελέσει το πρόγραμμά του σε ένα GRID, συνήθως το αναθέτει, μέσω κάποιου interface, στον Job Dispatcher. Ο Job Dispatcher επιλέγει κάποιο διαθέσιμο μηχάνημα από το GRID, αποστέλλει το πρόγραμμα, το οποίο και εκτελείται, και τελικώς αποστέλλει τα αποτελέσματά του στον αρχικό χρήστη.
Στόχος της άσκησης είναι να κατασκευάσετε έναν "εικονικό" Job Dispatcher, ο οποίος θυμίζει, σε γενικές γραμμές, τη λειτουργικότητα ενός εκ των βασικών μηχανισμών ενός λογισμικού για συστήματα GRID.
Κατά την εκπόνηση της άσκησης θα έχετε την ευκαιρία να εξοικειωθείτε με τις ακόλουθες βασικές έννοιες σύγχρονων λειτουργικών συστημάτων: processes, daemon processes, fork(), files, named pipes (FIFOs), signals και signal handlers.
Αρχιτεκτονική του Job Dispatcher (jobd)
Ο Job Dispatcher (jobd) που θα κατασκευάσετε θα πρέπει να είναι ένα daemon process. Ως daemon process στο Unix ορίζεται ένα process που εκτελείται μόνιμα στο υπόβαθρο (background) του συστήματος, χωρίς να έχει άμεση αλληλεπίδραση με το χρήστη μέσω του standard input/output ή κάποιου γραφικού interface. Πολλές υπηρεσίες στο Unix (για παράδειγμα η υπηρεσία που εκτυπώνει έγγραφα ή που αναλαμβάνει την αποστολή/υποδοχή της ηλεκτρονικής αλληλογραφίας) είναι υλοποιημένες ως daemon processes.
Ο jobd θα έχει δύο (2) ξεχωριστά resources, τα οποία θα πρέπει να χειρίζεται περιοδικά:
Ας δούμε πιο αναλυτικά πώς θα χειρίζεται τα παραπάνω resources.
Task 1 - Έλεγχος για προς εκτέλεση scripts
Ο Job Dispatcher θα πρέπει να ελέγχει ανά ένα σταθερό χρονικό διάστημα (POLL_JOB_INTERVAL) το directory jobd_in_dir για αρχεία. Σε περίπτωση που βρει κάποιο, θα πρέπει να το εκτελέσει και να αποστείλει την έξοδο του script σε ένα νέο αρχείο, το οποίο θα αποθηκευθεί στο directory jobd_out_dir. Μετά το τέλος της εκτέλεσης θα πρέπει να μεταφέρει το script από το jobd_in_dir στο jobd_dispatched_dir directory.
Υποθέστε ότι κάθε script που εκτελείται λαμβάνει ένα μοναδικό αριθμό. Το αρχείο που θα περιέχει την έξοδο του script που εκτελέσθηκε θα έχει όνομα (file name) τη σύνθεση ενός σταθερού προθέματος (JOBD_PREFIX) και του μοναδικού αριθμού του script.Αυτό θα συμβαίνει και με τα αρχεία που έχουν ήδη εξυπηρετηθεί και είναι αποθηκευμένα στο jobd_dispatched_dir directory.
Task 2 - Έλεγχος του jobd μέσω ενός named pipe (FIFO)
Επειδή ο jobd δεν μπορεί να αλληλεπιδρά με χρήστες μέσω του standard input/output θα πρέπει να χρησιμοποιηθεί κάποιος εναλλακτικός τρόπος επικοινωνίας. Ένας από τους πλέον συνηθισμένους τρόπους για InterProcess Communication (IPC) είναι η χρήση named pipes. Τα named pipes είναι κλασσικά pipes, αλλά παράλληλα έχουν αναπαράσταση στο file system, ως κλασσικά αρχεία. Επίσης υποστηρίζουν την επικοινωνία (read/write) οποιονδήποτε processes, τα οποία έχουν τα κατάλληλα permissions για την αλληλεπίδραση με το named pipe.
Κατά την εκκίνησή του, o jobd θα πρέπει (αν δεν έχει κατασκευαστεί ήδη) να δημιουργήσει ένα νέο named pipe, το οποίο και θα ελέγχει για νέα μηνύματα περιοδικά ανά ένα σταθερό χρονικό διάστημα (POLL_CMD_INTERVAL). Ο jobd είναι υπεύθυνος για την υποστήριξη δύο μηνυμάτων:
S:x := Όπου το 'S' προκύπτει από τη λέξη 'Status' και το x είναι ένας αριθμός (π.χ. S:1, S:2, κ.λπ.).
Σε αυτή την περίπτωση, ο jobd θα πρέπει να τυπώνει την έξοδο του script που έχει μοναδικό αριθμό τον αριθμό 'x'.
Q := Όπου το 'Q' προκύπτει από τη λέξη 'Quit'.
Σε αυτή την περίπτωση, ο jobd θα πρέπει να τερματίζει τη λειτουργία του.
Για να ελέγξετε τη λειτουργία του named pipe μπορείτε να ανακατευθύνετε σε αυτό εντολές μέσω του shell. Για παράδειγμα:
% echo S:2 >> comm
Υποθέτουμε ότι το named pipe αναπαρίσταται στο file system με το αρχείο 'comm'.
Job Dispatcher API
Παρακάτω σας δίνουμε ένα API για την κατασκευή του jobd. Είστε ελεύθεροι να χρησιμοποιήσετε το παρακάτω API ή κάποιο δικό σας.
/* jobd API */
/* Constants. */
const char *jobd_fifo = "/home/elathan/uoc/345/1/comm";
const char *jobd_in_dir = "/home/elathan/uoc/345/1/jobs/";
const char *jobd_out_dir = "/home/elathan/uoc/345/1/out/";
const char *jobd_dispatched_dir = "/home/elathan/uoc/345/1/dispatched/";
const char *jobd_main_dir = "/home/elathan/uoc/345/1/";
#define POLL_JOB_INTERVAL 10 /* Poll for waiting jobs every 10 secs. */
#define POLL_CMD_INTERVAL 5 /* Poll for status cmds every 5 secs. */
#define JOBD_PREFIX "jobd__out__"
#define JOBD_QUEUE_MAX 500
/*
Initialization routine. Create needed directories if they don't
exist, establish any needed signal handlers, establish the named pipe for
external communication and call jobd_daemonize() to become a
daemon.
*/
void jobd_init(void);
/*
Clean up and exit. You should remove everything done in
jobd_out_dir.
*/
void jobd_finalize(void);
/*
Become a daemon.
*/
int jobd_daemonize(void);
/*
Signal handler, which should be invoked every
POLL_JOB_INTERVAL seconds. It should call jobd_dir_check()
in jobd_in_dir to check for available jobs.
*/
void jobd_dispatcher(int sig);
/*
Check a directory for available jobs (scripts). If a script
is found, call jobd_dispatch to execute the script.
*/
void jobd_dir_check(const char *dir);
/*
Dispatch a job (script). Build a command string which should
execute the script and redirect the output to a unique file
in jobd_out_dir. Use system() to execute the command string.
After a correct execution, you should move the original
script from jobd_in_dir to jobd_dispatched_dir.
*/
int jobd_dispatch(char *jobname);
/*
Handle an incoming message from jobd_fifo. This function should
be called every POLL_CMD_INTERVAL.
*/
void jobd_handle_msg(char *msg);
Daemon Coding Rules
Tεχνικές λεπτομέριες για την κατασκευή της jobd_daemonize() από το Advanced Programming in the UNIX Environment (W. Richard Stevens).
Reference - man pages
Ένα man page περιγράφει τον τρόπο λειτουργίας ενός προγράμματος, ενός system call ή μιας library function. Η εμφάνιση ενός man page γίνεται με τη χρήση της εντολής:
man(1)
Για να δείτε το man page (σε SunOS) που αναφέρεται στη foo(N), κάνετε:
% man -s N foo
Για να δείτε το man page (σε Linux) που αναφέρεται στη foo(N), κάνετε:
% man -S N foo
Ο συμβολισμός foo(N) αναφέρεται στο man page που περιγράφει τη foo στη κατηγορία (section) 'Ν'.
Λίστα με χρήσιμα man pages για την άσκηση 1
Σας παραθέτουμε man pages με system calls και κάποιες συναρτήσεις που μπορεί να χρειαστείτε για την υλοποίηση της άσκησης. Η παρακάτω λίστα δεν είναι δεσμευτική. Μπορείτε να χρησιμοποιήσετε και εναλλακτικούς τρόπους.
fork(2)
setsid(2)
chdir(2)
umask(2)
opendir(3C)
readdir(3C)
fstat(3C)
system(3C)
alarm(2)
signal(3C)
mkfifo(3C)
sleep(3C)