Επικοινωνία Διεργασιών (ΙnterProcess Communication, IPC) Τουλάχιστον δύο διεργασίες (ο αποστολέας και ο παραλήπτης) Σύγχρονη (synchronous) Aσύγχρονη (asynchronous) Χρονική σύνδεση Δε χρειάζεται χρονική σύνδεση π.χ. π.χ. τηλέφωνο ταχυδρομείο chat e-mail Ραντεβού (Rendez vous) --- --- Ενταμίευση (Buffering) Χωρητικότητα ενταμιευτή = 0 Περιορισμένη χωρητικότητα ενταμιευτή Ταχύτητα διεργασιών Υπερχείλιση ενταμιευτή Κυκλικοί ενταμιευτές Δεξαμενή ενταμιευτών
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ (Producer-Consumer Problem) Διεργασία παραγωγός παράγει αντικείμενα και τα τοποθετεί σε έναν κοινό ενταμιευτή (shared buffer) Διεργασία καταναλωτής Παίρνει αντικείμενα από τον κοινό ενταμιευτή και τα χρησιμοποιεί π.χ. εκτύπωση, πληκτρολόγηση, ΠΡΟΒΛΗΜΑ: Πρέπει Να εμποδιστεί ο παραγωγός όταν ο ενταμιευτής είναι γεμάτος Να εμποδιστεί ο καταναλωτής όταν ο ενταμιευτής είναι άδειος
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ I. ENTAMIEYTHΣ XΩΡΗΤΙΚΟΤΗΤΑΣ 1 ΛΥΣΗ δύο δυαδικοί σηματοφορείς διαθέσιμος και πλήρης αρχικά διαθέσιμος =1; πλήρης =0; process παραγωγός; { while (true) process καταναλωτής; { while (true) { παράγαγε α; { wait(πλήρης); wait(free); πάρε α; τοποθέτησε α; signal(διαθέσιμος); signal (πλήρης); κατανάλωσε α;
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ IΙ. ΚΥΚΛΙΚΟΣ ΕΝΤΑΜΙΕΥΤΗΣ ΧΩΡΗΤΙΚΟΤΗΤΑΣ N Ενταμιευτής: buffer[] Β = new buffer[n]; ΛΥΣΗ δύο γενικοί σηματοφορείς άδειες (θέσεις) γεμάτες (θέσεις) δύο μεταβλητές in: επόμενη άδεια θέση για τοποθέτηση (παραγωγός) out: επόμενη γεμάτη θέση για ανάκτηση (καταναλωτής) αρχικά: άδειες = N; γεμάτες = 0; in = 0; out = 0; process παραγωγός; { while (true) { παράγαγε α; wait(άδειες); Β[in]= α; in= (in+1) % N; signal (γεμάτες); process καταναλωτής; { while (true) { wait(γεμάτες); α= B[out]; out= (out+1) % N; signal(άδειες);
κατανάλωσε α; Ι. ΜΗΛΗΣ ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ - 2 - ΤΑΥΤΟΧΡΟΝΕΣ ΔΙΕΡΓΑΣΙΕΣ 5
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ IΙΙ. ΔΕΞΑΜΕΝΗ Ν (ΟΜΟΕΙΔΩΝ) ΕΝΤΑΜΙΕΥΤΩΝ Δεξαμενή = Λίστα ενταμιευτών (σύνδεση με δείκτες) Ένας ενταμιευτής αποσπάται από τη δεξαμενή όταν πρόκειται να χρησιμοποιηθεί Κυκλικός ενταμιευτής χωρητικότητας Ν ~ δεξαμενή Ν ενταμιευτών χωρητικότητας 1 Παραγωγός: παράγει πλήρεις ενταμιευτές για τον καταναλωτή Καταναλωτής: αφήνει άδειους ενταμιευτές για τον παραγωγό Παραγωγός: αποσπά ενταμιευτές (επεξεργασία δεικτών) Καταναλωτής: επιστρέφει ενταμιευτές (επεξεργασία δεικτών) Πιθανότητα ταυτόχρονης επεξεργασίας ίδιων δεικτών
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ IΙΙ. ΔΕΞΑΜΕΝΗ Ν (ΟΜΟΕΙΔΩΝ) ΕΝΤΑΜΙΕΥΤΩΝ ΛΥΣΗ δύο γενικοί σηματοφορείς διαθέσιμοι (ενταμιευτές) πλήρεις (ενταμιευτές) ένας επιπλέον δυαδικός σηματοφορέας Β για να εξασφαλίσουμε τον αμοιβαίο αποκλεισμό των κρίσιμων περιοχών επεξεργασίας δεικτών Απλούστευση: χωρητικότητα ενταμιευτών =1 αρχικά διαθέσιμοι =N; πλήρεις =0; Β = 1; process παραγωγός; { while (true) { παράγαγε α; wait(διαθέσιμοι); wait(β); τοποθέτησε α; <επεξ. δείκτες> signal(β); signal(πλήρεις); process καταναλωτής; { while (true) { wait(πλήρεις); wait(β); αφαίρεσε α; <επεξ. δείκτες> signal(β); signal(διαθέσιμοι); κατανάλωσε α;
Ι. ΜΗΛΗΣ ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ - 2 - ΤΑΥΤΟΧΡΟΝΕΣ ΔΙΕΡΓΑΣΙΕΣ 8
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ IΙΙ. ΔΕΞΑΜΕΝΗ Ν (ΟΜΟΕΙΔΩΝ) ΕΝΤΑΜΙΕΥΤΩΝ Απόδειξη ορθότητας λύσης Έστω p το πλήθος των αντικειμένων που έχουν παραχθεί και c το πλήθος των αντικειμένων που έχουν καταναλωθεί. Θα πρέπει να αποδείξουμε ότι N p - c 0. Από το πρόγραμμα του παραγωγού προκύπτει ότι: #wait(διαθέσιμοι) p # signal(πλήρεις) (1) Από το πρόγραμμα του καταναλωτή: #wait(πλήρεις) c # signal(διαθέσιμοι) (2) (1) & (2) => #wait(διαθέσιμοι) - #signal (διαθέσιμοι) p - c #signal(πλήρεις) - #wait(πλήρεις) (3) Αλλά για όλους τους σηματοφορείς ισχύει: wait(s) #signal(s) + s0 Άρα για τον διαθέσιμοι: #wait(διαθέσιμοι) - #signal (διαθέσιμοι) Ν (4)
για τον πλήρεις: # signal(πλήρεις) - #wait(πλήρεις) 0 (5) (3) & (4) & (5) => N p - c 0 ο.ε.δ.
ΠΡΟΒΛΗΜΑ ΠΑΡΑΓΩΓΟΥ-ΚΑΤΑΝΑΛΩΤΗ ΛΥΣΗ ΚΥΚΛΙΚΟΥ ΕΝΤΑΜΙΕΥΤΗ (πίνακας) Οικονομικότερη σε χρόνο Η συχνότερα χρησιμοποιούμενη ΛΥΣΗ ΔΕΞΑΜΕΝΗΣ (δείκτες) Χρησιμοποιείται όταν: Δεν υπάρχουν εντολές ώστε η πρώτη θέση του ενταμιευτή να μπορεί να υπολογιστεί ως επόμενη της τελευταίας (π.χ. μερικοί δίαυλοι ή ελεγκτές δε διαθέτουν τέτοιες εντολές) Υπάρχει μεγάλη χρονική διακύμανση στις ανάγκες επικοινωνίας (π.χ. σ έναν επεξεργαστή κόμβου δικτύου) Τα περιεχόμενα του ενταμιευτή χρειάζεται να μεταφερθούν σε άλλες θέσεις της μνήμης
ΜΕΧΡΙ ΤΩΡΑ Επικοινωνία με χρήση κοινών μεταβλητών και δομών δεδομένων και σηματοφορέων ΠΡΟΒΛΗΜΑ μεγάλη προσοχή στη χρήση τους πιθανότητα λαθών ΙΔΕΑ Μηχανισμοί υψηλότερου επιπέδου
Προτάθηκαν από τους Hoare και Brinch Hansen. Παρακολουθητές ή Γραμματείς (Monitors ή Secretaries) Ένας παρακολουθητής (ένα αντικείμενο τύπου monitor) είναι ένας Αφηρημένος Τύπος Δεδομένων (ADT) που περιλαμβάνει: Τοπικές [στατικές] μεταβλητές/δομές [Δε χάνουν την τιμή τους κατά την έξοδο από τη διαδικασία στην οποία δηλώνονται (heap vs. stack)] Τυχαίνουν επεξεργασίας ΜΟΝΟ από τα υποπρογράμματα υλοποίησης των πράξεων Κάθε κλήση υποπρογράμματος χρησιμοποιεί τις τιμές των μεταβλητών που παρέμειναν από την προηγούμενη κλήση Πράξεις Ιδιωτικές διαδικασίες/υποπρογράμματα υλοποίησης πράξεων Μόνο μία εκτελείται σε μια δεδομένη χρονική στιγμή Υλοποιούνται σαν αμοιβαία αποκλειόμενες περιοχές Με χρήση δυαδικών σηματοφορέων από το μεταγλωττιστή Αρχικές τιμές Εντολές που δίνουν αρχικές τιμές στις μεταβλητές/δομές του και που εκτελούνται μια μόνο φορά, όταν το αντικείμενο δηλώνεται/δημιουργείται
ΛΕΙΤΟΥΡΓΙΑ Παρακολουθητές ή Γραμματείς Οι διεργασίες καλούν τις διαδικασίες του παρακολουθητή Ο παρακολουθητής υλοποιεί (αυτόματα) τον αμοιβαίο αποκλεισμό γιατί: Μόνο μια διεργασία μπορεί να χρησιμοποιήσει τον παρακολουθητή σε μια χρονική στιγμή, και ΑΡΑ Οι εσωτερικές μεταβλητές και δομές δεν μπορούν να προσπελαστούν συγχρόνως Ο παρακολουθητής επιστρέφει τον έλεγχο στις διεργασίες (όταν εξυπηρετήσει τις κλήσεις τους) ΠΛΕΟΝΕΚΤΗΜΑΤΑ-ΜΕΙΟΝΕΚΤΗΜΑΤΑ ανάλογα με τις διαδικασίες στις γλώσσες υψηλού επιπέδου απόκρυψη λεπτομερειών του πόρου αύξηση χρόνου εκτέλεσης Παρόμοιοι με τις τάξεις της Java, μόνο που οι πράξεις τους εκτελούνται αμοιβαίως αποκλειστικά.
Παράδειγμα (στοιχειώδες) Παρακολουθητές ή Γραμματείς type monitor bridge // ορισμός { procedure pass { pass the brigde Έστω G ένα αντικείμενο που έχει δηλωθεί ως τύπου bridge; process train1; {... G.pass;... Κύριο Πρόγραμμα: process train2; {... G.pass;... {... start (train1); start (train2);... process trainν; {... G.pass;... start(trainν);
Παρακολουθητές ή Γραμματείς ΠΡΟΒΛΗΜΑ Ο παρακολουθητής δεν εκτελείται ταυτόχρονα με τις διεργασίες Σύγχρονη επικοινωνία! ΛΥΣΕΙΣ χαμηλού επιπέδου: επεξεργασία σηματοφορέων από τις διαδικασίες του παρακολουθητή (αυτό θέλαμε να αποφύγουμε αρχικά!) υψηλού επιπέδου: συνθήκες (conditions)
Παρακολουθητές ή Γραμματείς Συνθήκες (Conditions) Νέος τύπος μεταβλητών Πράξεις (έστω ότι εκτελούνται από τη διεργασία P) cwait(c) η P εμποδίζεται πάνω στην c και παύει να χρησιμοποιεί τον παρακολουθητή csignal(c) Αφυπνίζεται μια από τις εμποδισμένες πάνω στην c διεργασίες (ποια? τυχαία ή FIFO) Αλλιώς η csignal(c) δεν έχει κανένα αποτέλεσμα Δύο επιλογές Η P εμποδίζεται και αφήνει άμεσα τον παρακολουθητή στην αφυπνισμένη διεργασία (Hοare) H csignal (c) είναι τελευταία εντολή της P του παρακολουθητή (Hansen) cnon_empty(c) συνάρτηση τύπου boolean true: αν υπάρχουν διεργασίες εμποδισμένες πάνω στη συνθήκη c false: διαφορετικά
Συνθήκες vs semaphores Παρακολουθητές ή Γραμματείς Οι συνθήκες δεν έχουν τιμή/μνήμη (memoryless) cwait(c): εμποδίζει πάντα την Ρ csignal(c): έχει αποτέλεσμα μόνο αν υπάρχει εμποδισμένη διεργασία πάνω στην c H cwait(c) πρέπει να προηγείται της csignal(c)
Παρακολουθητές ή Γραμματείς Πρόβλημα παραγωγού-καταναλωτή (Κυκλικός ενταμιευτής) program παραγωγός_καταναλωτής; const N=...; type buffer = monitor { B: array [0..N-1] of item; in, out, count: 0..N-1; μη_γεμάτος, μη_άδειος: condition; procedure πρόσθεσε { if (count == N) cwait(μη_γεμάτος); B[in] = x; in =(in+1) % N; count++; csignal(μη_άδειος); (x: item); in =0; out =0; count=0; // αρχικές τιμές procedure αφαίρεσε { if (count == 0) cwait(μη_άδειος); x = B[out]; out = (out+1) % N; count--; csignal(μη_γεμάτος); (var x: item);
Παρακολουθητές ή Γραμματείς Πρόβλημα παραγωγού-καταναλωτή (Κυκλικός ενταμιευτής) Έστω ότι η μεταβλητή BUF έχει δηλωθεί ως τύπου buffer process παραγωγός; α: item; { while (true) { παράγαγε α; BUF.πρόσθεσε (α); {... start (παραγωγός);... start (καταναλωτής);... process καταναλωτής; α: item; { while (true) { BUF.αφαίρεσε(α); κατανάλωσε α;
ΜΕΧΡΙ ΤΩΡΑ Επικοινωνία με χρήση ΣΗΜΑΤΟΦΟΡΕΩΝ πιθανότητα λαθών ΠΑΡΑΚΟΛΟΥΘΗΤΩΝ Πολύ λίγες γλώσσες διαθέτουν το μηχανισμό του παρακολουθητή Πολύ λίγα ΛΣ χρησιμοποιούν παρακολουθητές ΓΡΑΜΜΑΤΕΙΣ + ΠΑΡΑΚΟΛΟΥΘΗΤΕΣ Απαιτούν πρόσβαση διεργασιών σε κοινή μνήμη Τι γίνεται σε χαλαρά συνδεδεμένα συστήματα που δε διαθέτουν κοινή μνήμη?
Μοντέλο πελάτη-εξυπηρετητή (Client-Server model) Διεργασία εξυπηρετητής: προσφέρει υπηρεσίες /εξυπηρετεί αιτήσεις των διεργασιών-πελατών Διεργασίες πελάτες: ζητούν υπηρεσίες/εξυπηρέτηση από τη διεργασία-εξυπηρετητή process εξυπηρετητής; while (true) { περίμενε αίτηση; λάβε αίτηση; εξυπηρέτησε αίτηση; Παραδείγματα υπηρεσιών που προσφέρουν οι εξυπηρετητές Ώρα και Ημερομηνία Εκτύπωση αρχείων Ανάγνωση - Εγγραφή αρχείων (εξυπηρετητής αρχείων) Επικοινωνία πελάτες-εξυπηρετητής στην ίδια μηχανή πελάτες-εξυπηρετητής σε διαφορετικές μηχανές
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων Μηχανισμός υψηλότερου επιπέδου από τους σηματοφορείς Προσφέρεται από πολλές γλώσσες προγραμματισμού Οι διεργασίες μπορεί να εκτελούνται σε διαφορετικούς επεξεργαστές δύο πρωτογενείς πράξεις (primitives) send(διεργασία προορισμού, message); - μονοεκπομπή (unicast) - στέλνει ένα μήνυμα σε ένα δεδομένο προορισμό - παραλλαγές - πολυεκπομπή (multicast) - send(many, message); - πανεκπομπή (broadcast) - send(all, message); receive(διεργασία πηγής, message); - Αν το μήνυμα δεν είναι διαθέσιμο η διεργασία παραλήπτης εμποδίζεται - παραλλαγή - δέχεται ένα μήνυμα από οποιονδήποτε προορισμό - receive(ανυ, message); κλήσεις συστήματος (σαν τους σηματοφορείς) όχι ευκολίες της γλώσσας (όπως οι παρακολουθητές) παρέχονται συνήθως ως διαδικασίες της βιβλιοθήκης
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων Πρόβλημα παραγωγού-καταναλωτή process παραγωγός; {... while (true) { παράγαγε α;... send(καταναλωτής,α);... process καταναλωτής; {... while (true) {receive(παραγωγός,α);... κατανάλωσε α;... Αν η send εκτελεστεί πριν από τη receive η διεργασία producer εμποδίζεται Σύγχρονη επικοινωνία
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων Ασύγχρονη επικοινωνία με χρήση ενταμιευτή μηνυμάτων Ενταμιευτής μηνυμάτων = ουρά μηνυμάτων (μια για κάθε διεργασία) χωρητικότητα καναλιού επικοινωνίας Η διεργασία δ μπορεί να στείλει ένα μήνυμα στη διεργασία δ' αν η ουρά της δ' έχει κενές θέσεις, αλλιώς εμποδίζεται Η διεργασία δ' μπορεί να παραλάβει ένα μήνυμα από την διεργασία δ αν στην ουρά της υπάρχουν μηνύματα από την δ, αλλιώς η δ' εμποδίζεται Υλοποίηση: γραμματοκιβώτια (mailboxes)
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων Ενταμίευση Δεξαμενή Ν ενταμιευτών ΔΙΑΔΙΚΑΣΙΑ send process p1; {... send(p2, m);... Αν υπάρχει κενός ενταμιευτής, τότε δεσμεύεται αποθηκεύονται σ' αυτόν το m και η ταυτότητα της p1 αν η p2 έχει εμποδιστεί περιμένοντας μήνυμα από την p1 ελευθερώνεται η p1 συνεχίζει την εκτέλεσή της Αν ΔΕΝ υπάρχει κενός ενταμιευτής, τότε Η p1 εμποδίζεται ΔΙΑΔΙΚΑΣΙΑ receive process p2; {... receive(p1, m);... Αν έχει σταλεί μήνυμα από την p1, τότε τo μήνυμα γίνεται δεκτό (accepted) δηλαδή αντιγράφεται ο ενταμιευτής επιστρέφεται στη δεξαμενή Αν ΔΕΝ έχει σταλεί μήνυμα από την p1, τότε Η p2 εμποδίζεται
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων ~ ΥΛΟΠΟΙΗΣΗ των send και receive Προβλήματα: Γεμάτος - άδειος ενταμιευτής Αμοιβαίος αποκλεισμός επεξεργασίας δεικτών Ταυτότητα αποστολέα Ουρές μηνυμάτων διεργασιών (μία ανά διεργασία) Ειδοποίηση παραλήπτη buffer { m: m_type; priority: p_type; timestamp: t_type; sender: id_type; link: ^buffer δύο γενικοί σηματοφορείς διαθέσιμοι (ενταμιευτές) πλήρεις (ενταμιευτές) ένας δυαδικός σηματοφορέας B αμοιβαίος αποκλεισμός κρίσιμων περιοχών επεξεργασίας δεικτών ένας δυαδικός σηματοφορέας m2r (message to r)
ειδοποίηση της διεργασίας παραλήπτη r ότι κάποια διεργασία έβαλε μήνυμα στην ουρά της χωρητικότητα ενταμιευτών = 1
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων ~ ΥΛΟΠΟΙΗΣΗ send και receive αρχικά διαθέσιμοι =N; πλήρεις =0; B=1; m2r= 0; process s process r procedure send (r: id_type; m: m_type); { signal (πλήρεις); signal (m2r); wait (διαθέσιμοι); wait (Β); κτίσε έναν ενταμιευτή b; πρόσθεσε τον b στην ουρά του παραλήπτη; ενημέρωσε τους δείκτες; signal (Β); procedure receive (s: id_type; m: m_type); temp: ^buffer; accept: boolean; { do { wait (πλήρεις); wait (Β); temp = κεφαλή της ουράς του παραλήπτη; accept = false; while (temp<>nil) && (!accept)
{ if (temp.sender == s) { αντίγραψε το μήνυμα m από τον κόμβο temp; ενημέρωσε τους δείκτες; accept:= true; temp:= temp^.link; signal (Β); if (!accept) else signal while (!accept); wait(m2r); (διαθέσιμοι);
Επικοινωνία Διεργασιών με μεταβίβαση μηνυμάτων Πρόβλημα παραγωγού-καταναλωτή process παραγωγός; { παράγαγε α; κτίσε ένα μήνυμα m; send(καταναλωτής,m); process καταναλωτής; { receive(παραγωγός,m); απόσπασε α από το m; κατανάλωσε α; Αν το μήνυμα (το α) είμαι μεγάλο τότε πρέπει αντ αυτού να μεταβιβαστεί η διεύθυνση του (ενταμιευτή του) Στην περίπτωση αυτή: ο αποστολέας δεν μπορεί να χρησιμοποιήσει τον ενταμιευτή μέχρις ότου ο παραλήπτης να καταναλώσει το μήνυμα και πρέπει να εμποδιστεί έτσι, στην περίπτωση αυτή χρειάζεται κι ένας δυαδικός σηματοφορέας!
ΜΕΧΡΙ ΤΩΡΑ Επικοινωνία διεργασιών σε μια μηχανή Σηματοφορείς Παρακολουθητές Ουρές μηνυμάτων Rendez-vous (Ada) Διεργασίες σε διαφορετικές μηχανές Διαφορετικοί μηχανισμοί send-receive Υποδοχές (sockets) UNIX Κλήσεις απομακρυσμένων διαδικασιών (Remote Procedure Calls, PRCs) Rendez-vous (Ada)