ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 3ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 19 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1
Διαχείριση εργασιών ◦ fork(), exit(), exec() Επικοινωνία διεργασιών ◦ signals, sockets και client/server, shared memory Συγχρονισμός διεργασιών ◦ semaphores - 2 -
Θεωρούµε ότι: ◦ Μία διεργασία διαχειρίζεται τις κοινές πληροφορίες – την ονοµάζουµε ‘εξυπηρετητή’ (server) ◦ Μια διεργασία ϑέλει να αποκτήσει πρόσβαση στις πληροφορίες – την ονοµάζουµε ‘πελάτη’ (client) ◦ ΄Οταν ο ‘πελάτης’ ζητάει µια πληροφορία από τον ‘εξυπηρετητή’ λέµε ότι κάνει µια ‘αίτηση’ Μια διεργασία ‘πελάτης’ µπορεί να κάνει αιτήσεις σε διαφορετικές διεργασίες ‘εξυπηρετητές’ Μια διεργασία ‘εξυπηρετητής’ µπορεί να δέχεται αιτήσεις από διαφορετικές διεργασίες ‘πελάτες’ Μια διεργασία µπορεί να είναι ταυτόχρονα ‘πελάτης’ και ‘εξυπηρέτητής’ Το σχήµα µας επιτρέπει να υλοποιήσουµε ένα µεγάλο εύρος λειτουργιών - 3 -
Ο καθιερωµένος τρόπος επικοινωνίας είναι µε µηνύµατα Ορίστηκε αρχικά για τα συστήµατα UNIX -- πλέον υποστηρίζεται από όλα τα λειτουργικά συστήµατα Μοιάζει µε τον χειρισµό αρχείων – ένα κανάλι επικοινωνίας λειτουργεί σαν ένα αρχείο στο δίσκο ◦ Μια ενέργεια write µετατρέπεται σε send ◦ Μια ενέργεια read µετατρέπεται σε receive Μπορεί να υλοποιήσει είτε σύγχρονες είτε ασύγχρονες µεταφορές δεδοµένων Υποστηρίζει TCP και UDP ◦ UDP -- σαν ταχυδροµείο ◦ TCP -- σαν κλήση σε τηλέφωνο - 4 -
∆ύο διεργασίες που εκτελούνται στην ίδια υπολογιστική µονάδα µπορούν να χρησιµοποιήσουν µια απλουστευµένη έκδοση των sockets, γνωστά και ως pipes ◦ Εύκολα µπορούµε να τα µετατρέψουµε σε socket που λειτουργούν σε TCP/IP δίκτυα Το socket αποκτά την µορφή ενός εικονικού αρχείου ΄Ενα socket name µπορεί να ‘ελέγχεται’ από το πολύ µια διεργασία Μια διεργασία µπορεί να ‘ελέγχει’ πολλά socket names Οι αποστολείς πρέπει να ξέρουν το όνοµα του socket στο οποίο ‘ακούει’ ο παραλήπτης ◦ ΄Οποια διεργασία γνωρίζει το όνοµα του socket µπορεί να στείλει µηνύµατα ◦ Συνήθως είναι γνωστό εκ των προτέρων - 5 -
Για να χειριστούµε ένα socket δουλεύουµε µε socket descriptors Η αρχικοποίηση ενός socket descriptor (sd) γίνεται ως εξής: sd = socket(format, type, protocol); ◦ ∆ηµιουργεί ένα νέο sd Ο πελάτης για να ‘συνδεθεί’ µε τον εξυπηρέτη χρησιµοποιεί την κλήση συστήµατος connect connect(sd, address, length); Η αποστολή µηνυµάτων γίνεται µε την χρήση της write write(sd, "hello", 5); Σαν να ήταν αρχείο - 6 -
Ο εξυπηρέτης πρέπει να ‘συνδέσει’ το sd µε ένα local port του ΛΣ ως εξής: bind(sd, address, length); ◦ Το πεδίο address ορίζει το local port που ‘ακούει’ η διεργασία – δηλαδή το όνοµα του εικονικού αρχείου ◦ Το πεδίο length είναι το µήκος του πεδίου address Για να δεχθεί µια νέα σύνδεση χρησιµοποιεί την κλήση συστήµατος listen ως εξής: listen(sd, qlength); ◦ Ο εξυπηρέτης δέχεται τις κλήσεις σειριακά – άρα οι κλήσεις τοποθετούνται σε µία ουρά έως ότου η διεργασία τις εξυπηρετήσει ◦ Η παράµετρος qlength ορίζει το µέγεθος της ουράς - 7 -
- 8 -
#include main() { int sd, ns, fromlen; struct sockaddr sockaddr; char buf[256]; sd = socket(AF_UNIX, SOCK_STREAM, 0); // bind name -- don’t include null char bind(sd, "sockname", sizeof("sockname")-1); // listen for new connections (blocks) listen(sd, 1);
... for(;;) { // new connection established ns = accept(sd, &sockaddr, &fromlen); // wait for message (blocks) read(ns, buf, sizeof(buf)); printf("server read ’%s’\n, buf); // send message write(ns, "OK", 2); // close connection & wait for next close(ns); }
#include main() { int sd; char buf[256]; sd = socket(AF_UNIX, SOCK_STREAM, 0); // connect to name -- don’t include null char connect(sd, "sockname", sizeof("sockname")-1);
... // send message write(sd, "hello", 5); // wait for message (blocks) read(sd, buf, sizeof(buf)); printf("client read ’%s’\n, buf); // close connection close(sd); exit(0); }