Professional Documents
Culture Documents
Παναγιώτα Φατούρου
Τµήµα Πληροφορικής, Πανεπιστήµιο Ιωαννίνων
Παύλος Σπυράκης
Τµήµα Μηχανικών Η/Υ και Πληροφορικής, Πανεπιστήµιο Πατρών
&
Ινστιτούτο Τεχνολογίας Υπολογιστών
Σεπτέµβριος 2004
ΑΚΟΥΓΕ ΟΤΑΝ ΜΙΛΑΣ
Μη λες πολύ συχνά ότι έχεις δίκιο δάσκαλε!
Άσε να το δουν κι οι µαθητές!
Μην πιέζεις πολύ την αλήθεια,
∆εν το αντέχει.
Άκουγε όταν µιλάς!
Μπέρτολτ Μπρεχτ
από τη συλλογή «76 Ποιήµατα»
Πίνακας Περιεχοµένων
1 ΚΕΦΑΛΑΙΟ ΕΙΣΑΓΩΓΗ ............................................................................................................ 8
1.1 ΤΙ ΕΊΝΑΙ ΛΕΙΤΟΥΡΓΙΚΌ ΣΎΣΤΗΜΑ – ΣΚΟΠΌΣ & ΠΑΡΕΧΌΜΕΝΕΣ ΥΠΗΡΕΣΊΕΣ ............................... 9
1.2 ΣΎΝΤΟΜΗ ΑΝΑΣΚΌΠΗΣΗ ΥΛΙΚΟΎ ΥΠΟΛΟΓΙΣΤΏΝ .....................................................................10
1.3 ΘΈΜΑΤΑ ΑΠΌ∆ΟΣΗΣ ...................................................................................................................12
1.3.1 Ενδιάµεση Προσωρινή Αποθήκευση (Buffering) ....................................................................13
1.3.2 Παροχέτευση (Spooling) ........................................................................................................13
1.3.3 Πολυ-προγραµµατισµός (Multiprogramming)........................................................................14
1.3.4 ∆ιαµοιρασµός Χρόνου (Time Sharing)...................................................................................15
1.4 ΠΡΟΣΤΑΣΊΑ .................................................................................................................................16
1.4.1 Προστασία Ε/Ε.......................................................................................................................16
1.4.2 Προστασία ΚΜΕ ...................................................................................................................17
1.4.3 Προστασία ∆ιευθυνσιοδότησης Μνήµης .................................................................................17
1.5 ΤΑ ΣΥΣΤΑΤΙΚΆ ΕΝΌΣ ΛΕΙΤΟΥΡΓΙΚΟΎ ΣΥΣΤΉΜΑΤΟΣ ..................................................................18
1.6 ΚΑΤΑΝΕΜΗΜΈΝΑ ΣΥΣΤΉΜΑΤΑ & ΣΥΣΤΉΜΑΤΑ ΠΡΑΓΜΑΤΙΚΟΎ ΧΡΌΝΟΥ ..................................21
1.7 ΓΙΑ ΠΕΡΙΣΣΌΤΕΡΗ ΜΕΛΈΤΗ .........................................................................................................21
1.8 ΜΟΡΦΉ ΨΕΥ∆ΟΚΏ∆ΙΚΑ ΠΕΡΙΓΡΑΦΉΣ ΠΡΟΓΡΆΜΜΑΤΟΣ ∆ΙΕΡΓΑΣΊΑΣ ..........................................24
1.8.1 Πρόταση ∆ήλωσης Μεταβλητών ............................................................................................24
1.8.2 Πρόταση Καταχώρησης..........................................................................................................24
1.8.3 Βρόγχοι ..................................................................................................................................25
1.8.4 Προτάσεις Ελέγχου.................................................................................................................27
1.8.5 Πρόταση goto.........................................................................................................................28
1.8.6 Πίνακες και ∆οµές..................................................................................................................28
1.8.7 Typedef ..................................................................................................................................29
1.8.8 Συναρτήσεις ...........................................................................................................................29
1.8.9 Άλλα Θέµατα ..........................................................................................................................29
2 ΚΕΦΑΛΑΙΟ ∆ΙΕΡΓΑΣΙΕΣ – ΧΡΟΝΟ∆ΡΟΜΟΛΟΓΗΣΗ ∆ΙΕΡΓΑΣΙΩΝ ............................30
2.1 Η ΈΝΝΟΙΑ ΤΗΣ ∆ΙΕΡΓΑΣΊΑΣ .........................................................................................................31
2.2 ΚΑΤΑΣΤΆΣΕΙΣ ∆ΙΕΡΓΑΣΙΏΝ ..........................................................................................................31
2.3 ΤΟ ΜΠΛΟΚ ΕΛΈΓΧΟΥ ∆ΙΕΡΓΑΣΙΏΝ .............................................................................................32
2.4 ΛΕΙΤΟΥΡΓΊΕΣ ΕΠΊ ∆ΙΕΡΓΑΣΙΏΝ.....................................................................................................33
2.5 ∆ΙΑΚΟΠΈΣ ...................................................................................................................................34
2.6 ΧΡΟΝΟ∆ΡΟΜΌΛΟΓΗΣΗ ................................................................................................................39
2.7 ΑΛΓΌΡΙΘΜΟΙ ΧΡΟΝΟ∆ΡΟΜΟΛΌΓΗΣΗΣ .........................................................................................43
2.7.1 Πρώτη Εισερχόµενη – Πρώτη Εξυπηρετούµενη (First Come – First Served, FCFS).............43
2.7.2 Εκ Περιτροπής (Round Robin, RR) ........................................................................................44
2.7.3 Προτεραιοτήτων (Priority).....................................................................................................45
2.7.4 Θεωρητική Μελέτη Απόδοσης Χρονοδροµολογητών ..............................................................46
3 ΚΕΦΑΛΑΙΟ ∆ΙΑ∆ΙΕΡΓΑΣΙΑΚΗ ΕΠΙΚΟΙΝΩΝΙΑ & ΣΥΓΧΡΟΝΙΣΜΟΣ ..........................58
3.1 ΤΑΥΤΌΧΡΟΝΗ ΕΚΤΈΛΕΣΗ ∆ΙΕΡΓΑΣΙΏΝ ........................................................................................59
3.2 Η ΑΝΆΓΚΗ ΣΥΓΧΡΟΝΙΣΜΟΎ ........................................................................................................69
3.3 ΤΟ ΠΡΌΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΊΟΥ ΑΠΟΚΛΕΙΣΜΟΎ .......................................................................73
3.4 ΛΎΣΕΙΣ ΜΕ ΤΗ ΒΟΉΘΕΙΑ ΤΟΥ ΥΛΙΚΟΎ ........................................................................................75
3.5 ΛΎΣΕΙΣ ΜΕ ΧΡΉΣΗ ΛΟΓΙΣΜΙΚΟΎ ΜΌΝΟ ......................................................................................82
3.5.1 Πρώτες Προσπάθειες προς την λύση ......................................................................................83
3.5.2 Λύση του Peterson..................................................................................................................90
3.6 ΣΗΜΑΦΌΡΟΙ ................................................................................................................................99
3.7 ΠΡΟΒΛΉΜΑΤΑ ∆ΙΑ∆ΙΕΡΓΑΣΙΑΚΉΣ ΕΠΙΚΟΙΝΩΝΊΑΣ ....................................................................105
3.7.1 Για Προθέρµανση................................................................................................................105
3.7.2 Το Πρόβληµα του Ζωολογικού Κήπου (Θέµα 6, Α’ Τελική Εξέταση, Ιούνιος 2003).............113
3.7.3 Το Πρόβληµα του Κουρέα (Θέµα 3Β, Α’ Τελική Εξέταση, Μάιος 2001) ..............................117
3.7.4 Το Πρόβληµα των Αναγνωστών-Εγγραφέων........................................................................119
3.7.5 Για περισσότερη εξάσκηση ...................................................................................................121
3.7.6 Γενικές Παρατηρήσεις..........................................................................................................125
3.8 ΚΡΊΣΙΜΕΣ ΠΕΡΙΟΧΈΣ ΚΑΙ ΚΡΊΣΙΜΕΣ ΠΕΡΙΟΧΈΣ ΥΠΌ ΣΥΝΘΉΚΗ ................................................125
3.9 ΛΊΓΟ ΠΙΟ ∆ΎΣΚΟΛΑ ..................................................................................................................133
3.9.1 Λύση του Lamport (Bakery Algorithm) ................................................................................133
3.9.2 Λύση του Dekker ..................................................................................................................135
3.9.3 ∆ικαιοσύνη ...........................................................................................................................136
3.9.4 Προσοµοιώσεις ....................................................................................................................139
Πίνακας Σχηµάτων
ΣΧΗΜΑ 1: ΣΧΕΣΗ ΤΟΥ ΛΣ ΜΕ ΤΟ ΥΛΙΚΟ ΤΟΥ ΥΠΟΛΟΓΙΣΤΗ ΚΑΙ ΤΟΥΣ ΧΡΗΣΤΕΣ............................................... 9
ΣΧΗΜΑ 2: ΙΕΡΑΡΧΙΑ ΜΟΝΑ∆ΩΝ ΑΠΟΘΗΚΕΥΣΗΣ ............................................................................................12
ΣΧΗΜΑ 3: ΠΡΟΣΤΑΣΙΑ ∆ΙΕΥΘΥΝΣΙΟ∆ΟΤΗΣΗΣ ΜΝΗΜΗΣ ΜΕ ΧΡΗΣΗ ΤΩΝ ΚΑΤΑΧΩΡΗΤΩΝ ΒΑΣΗΣ ΚΑΙ ΟΡΙΟΥ.....18
ΣΧΗΜΑ 4: ΓΡΑΦΗΜΑ ΚΑΤΑΣΤΑΣΕΩΝ ∆ΙΕΡΓΑΣΙΑΣ. ..........................................................................................32
ΣΧΗΜΑ 5: ΤΟ PCB ΜΙΑΣ ∆ΙΕΡΓΑΣΙΑΣ..............................................................................................................33
ΣΧΗΜΑ 6: ΕΝΑΛΛΑΓΗ ∆ΙΕΡΓΑΣΙΩΝ.................................................................................................................37
ΣΧΗΜΑ 7: Η ΟΥΡΑ ΕΤΟΙΜΩΝ ∆ΙΕΡΓΑΣΙΩΝ. ......................................................................................................40
ΣΧΗΜΑ 8: ΣΧΗΜΑΤΙΚΗ ΠΕΡΙΓΡΑΦΗ ΤΗΣ ΧΡΟΝΟ∆ΡΟΜΟΛΟΓΗΣΗΣ ∆ΙΕΡΓΑΣΙΩΝ. ...............................................41
ΣΧΗΜΑ 9: ΕΙ∆ΟΣ ∆ΡΟΜΟΛΟΓΗΣΗΣ ΠΟΥ ΠΑΡΕΧΕΤΑΙ ΑΠΟ ΤΟΥΣ ΧΡΟΝΟ∆ΡΟΜΟΛΟΓΗΤΕΣ ΚΜΕ ΚΑΙ ΜΑΚΡΑΣ
∆ΙΑΡΚΕΙΑΣ.............................................................................................................................................43
ΣΧΗΜΑ 10: ΠΙΝΑΚΑΣ ΧΡΟΝΟ∆ΡΟΜΟΛΟΓΗΣΗΣ ΓΙΑ ΤΟΝ ΑΛΓΟΡΙΘΜΟ RR........................................................52
ΣΧΗΜΑ 11: ∆ΙΑΓΡΑΜΜΑΤΑ GANNT ΓΙΑ ΤΟΥΣ ΑΛΓΟΡΙΘΜΟΥΣ ΧΡΟΝΟ∆ΡΟΜΟΛΟΓΗΣΗΣ ΤΟΥ ΠΑΡΑ∆ΕΙΓΜΑΤΟΣ 6.
.............................................................................................................................................................54
ΣΧΗΜΑ 12: 1Ο ΠΑΡΑ∆ΕΙΓΜΑ ∆ΥΝΑΤΗΣ ΤΑΥΤΟΧΡΟΝΗΣ ΕΚΤΕΛΕΣΗΣ ..............................................................61
ΣΧΗΜΑ 13: 1Ο ΠΑΡΑ∆ΕΙΓΜΑ ∆ΥΝΑΤΗΣ ΤΑΥΤΟΧΡΟΝΗΣ ΕΚΤΕΛΕΣΗΣ: ΠΙΟ ΕΥΑΝΑΓΝΩΣΤΗ ΠΑΡΟΥΣΙΑΣΗ .......62
ΣΧΗΜΑ 14: 2Ο ΠΑΡΑ∆ΕΙΓΜΑ ∆ΥΝΑΤΗΣ ΤΑΥΤΟΧΡΟΝΗΣ ΕΚΤΕΛΕΣΗΣ ..............................................................63
ΣΧΗΜΑ 15: ΚΩ∆ΙΚΕΣ ΠΟΥ ΕΚΤΕΛΟΥΝ ΟΙ ∆ΙΕΡΓΑΣΙΕΣ Α ΚΑΙ Β. .......................................................................64
ΣΧΗΜΑ 16: ΣΕΝΑΡΙΟ ΠΟΥ Ο∆ΗΓΕΙ ΣΤΗΝ ΜΙΚΡΟΤΕΡΗ ΤΙΜΗ ΓΙΑ ΤΗ ∆ΙΑΜΟΙΡΑΖΟΜΕΝΗ ΜΕΤΑΒΛΗΤΗ TALLY. ...68
ΣΧΗΜΑ 17: ΟΥΡΑ ΕΤΕΡΟΧΡΟΝΙΣΜΕΝΗΣ ΕΚΤΥΠΩΣΗΣ......................................................................................70
ΣΧΗΜΑ 18: ΣΕΝΑΡΙΟ ΠΟΥ Ο∆ΗΓΕΙ ΣΕ ΑΝΑΛΗΨΗ 1000€ ΑΠΟ ΕΝΑ ΛΟΓΑΡΙΑΣΜΟ ΠΟΥ ΠΕΡΙΕΧΕΙ ΜΟΝΟ 500€....72
ΣΧΗΜΑ 19: ΕΝΑΛΛΑΚΤΙΚΕΣ ΙΣΟ∆ΥΝΑΜΕΣ ΓΕΝΙΚΕΣ ΜΟΡΦΕΣ ΚΩ∆ΙΚΑ ∆ΙΕΡΓΑΣΙΩΝ. ........................................74
ΣΧΗΜΑ 20: ∆ΙΑΚΟΠΕΣ ΕΝΟΣΩ ΜΙΑ ∆ΙΕΡΓΑΣΙΑ ΕΚΤΕΛΕΙ ΤΟ ΚΡΙΣΙΜΟ ΤΜΗΜΑ ΤΗΣ. ..........................................75
ΣΧΗΜΑ 21: Η ΕΝΤΟΛΗ TEST&SET()...............................................................................................................76
ΣΧΗΜΑ 22: ΛΥΣΗ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΜΕ ΧΡΗΣΗ ΤΗΣ ΕΝΤΟΛΗΣ TEST&SET()...76
ΣΧΗΜΑ 23: ΚΩ∆ΙΚΑΣ ΠΟΥ ΕΚΤΕΛΕΙΤΑΙ ΣΤΗΝ ΠΡΑΞΗ ΑΠΟ ∆ΥΟ ∆ΙΕΡΓΑΣΙΕΣ 0 ΚΑΙ 1 ΟΤΑΝ Η TEST&SET()
ΧΡΗΣΙΜΟΠΟΙΕΙΤΑΙ ΓΙΑ ΕΠΙΤΕΥΞΗ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ...............................................................77
ΣΧΗΜΑ 24: ΕΚΤΕΛΕΣΗ ΠΟΥ ΠΑΡΑΒΙΑΖΕΙ ΤΗ ΣΥΝΘΗΚΗ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΑΝ Η TEST&SET() ∆ΕΝ
ΕΚΤΕΛΕΙΤΑΙ ΑΤΟΜΙΚΑ...........................................................................................................................78
ΣΧΗΜΑ 25: ΑΤΟΜΙΚΗ ΕΚ∆ΟΣΗ ΤΗΣ DEPOSIT()................................................................................................79
ΣΧΗΜΑ 26: ΑΤΟΜΙΚΗ ΕΚ∆ΟΣΗ ΤΗΣ WITHDRAW()...........................................................................................79
ΣΧΗΜΑ 27: ΕΚΤΕΛΕΣΗ ΠΟΥ Ο∆ΗΓΕΙ ΣΕ ΠΑΡΑΤΕΤΑΜΕΝΗ ΣΤΕΡΗΣΗ ΓΙΑ ΤΗ ∆ΙΕΡΓΑΣΙΑ 1 ΣΤΗ ΛΥΣΗ ΠΟΥ
ΧΡΗΣΙΜΟΠΟΙΕΙ ΤΗΝ TEST&SET(). .........................................................................................................80
ΣΧΗΜΑ 28: Η ΕΝΤΟΛΗ FETCH&ADD(). ..........................................................................................................81
ΣΧΗΜΑ 29: Η ΕΝΤΟΛΗ SWAP().......................................................................................................................81
ΣΧΗΜΑ 30: Η ΕΝΤΟΛΗ RMW(). .....................................................................................................................82
ΣΧΗΜΑ 31: ΠΡΩΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ......................................................................................................................................83
ΣΧΗΜΑ 32: ΣΕΝΑΡΙΟ ΠΟΥ ΚΑΤΑΣΤΡΑΤΗΓΕΙ ΤΗ ΣΥΝΘΗΚΗ ΤΟΥ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΓΙΑ ΤΗΝ 1Η
ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ...........................................................................................................................84
ΣΧΗΜΑ 33: ∆ΕΥΤΕΡΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ......................................................................................................................................84
ΣΧΗΜΑ 34: ΣΕΝΑΡΙΟ ΠΟΥ ΚΑΤΑΣΤΡΑΤΗΓΕΙ ΤΗ ΣΥΝΘΗΚΗ ΤΗΣ ΠΡΟΟ∆ΟΥ ΓΙΑ ΤΗ 2Η ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ. ....85
ΣΧΗΜΑ 35: ΤΡΙΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ (ΛΥΣΗ ΤΗΣ ΑΥΣΤΗΡΗΣ ΕΝΑΛΛΑΓΗΣ) ΣΤΟ
ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ. .......................................................................................86
ΣΧΗΜΑ 36: ΣΕΝΑΡΙΟ ΠΟΥ ΚΑΤΑΣΤΡΑΤΗΓΕΙ ΤΗ ΣΥΝΘΗΚΗ ΤΗΣ ΠΡΟΟ∆ΟΥ ΓΙΑ ΤΗΝ 3Η ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ...87
ΣΧΗΜΑ 37: ΤΕΤΑΡΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ ΓΙΑ ∆ΥΟ ∆ΙΕΡΓΑΣΙΕΣ......................................................................................................87
ΣΧΗΜΑ 38: Η ΛΥΣΗ ΤΟΥ PETERSON. ..............................................................................................................90
ΣΧΗΜΑ 39: ΠΕΜΠΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ ΓΙΑ ∆ΥΟ ∆ΙΕΡΓΑΣΙΕΣ......................................................................................................92
ΣΧΗΜΑ 40: ΈΚΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ......................................................................................................................................93
ΣΧΗΜΑ 41: ΣΕΝΑΡΙΟ ΠΟΥ ΚΑΤΑΣΤΡΑΤΗΓΕΙ ΤΗ ΣΥΝΘΗΚΗ ΤΟΥ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΓΙΑ ΤΗΝ 5Η
ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ...........................................................................................................................95
ΣΧΗΜΑ 42: ΕΛΑΦΡΩΣ ΤΡΟΠΟΠΟΙΗΜΕΝΗ ΕΚ∆ΟΣΗ ΤΗΣ ΛΥΣΗΣ ΤΟΥ PETERSON................................................95
ΣΧΗΜΑ 43: ΈΝΑ ΣΕΝΑΡΙΟ ΠΟΥ Ο∆ΗΓΕΙ ΣΕ ΠΑΡΑΤΕΤΑΜΕΝΗ ΣΤΕΡΗΣΗ ΤΗΝ ΕΛΑΦΡΩΣ ΤΡΟΠΟΠΟΙΗΜΕΝΗ
ΕΚ∆ΟΣΗ ΤΗΣ ΛΥΣΗΣ ΤΟΥ PETERSON......................................................................................................96
ΣΧΗΜΑ 44: ΈΒ∆ΟΜΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ......................................................................................................................................97
ΣΧΗΜΑ 45: ΌΓ∆ΟΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ
ΑΠΟΚΛΕΙΣΜΟΥ......................................................................................................................................97
ΣΧΗΜΑ 46: ΠΙΟ ΑΝΑΛΥΤΙΚΗ ΠΕΡΙΓΡΑΦΗ ΤΟΥ ΚΩ∆ΙΚΑ ΤΩΝ ∆ΙΕΡΓΑΣΙΩΝ ΤΗΣ ΟΓ∆ΗΣ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗΣ. 98
ΣΧΗΜΑ 47: ΈΝΑΤΗ ΠΡΟΤΕΙΝΟΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΓΙΑ ∆ΥΟ
∆ΙΕΡΓΑΣΙΕΣ............................................................................................................................................98
ΣΧΗΜΑ 48: ΥΛΟΠΟΙΗΣΗ ΛΕΙΤΟΥΡΓΙΩΝ UP() ΚΑΙ DOWN() ΜΕ ΧΡΗΣΗ ΛΟΓΙΣΜΙΚΟΥ........................................100
ΣΧΗΜΑ 49: ΛΥΣΗ ΠΡΟΒΛΗΜΑΤΟΣ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΜΕ ΣΗΜΑΦΟΡΟΥΣ. ....................................100
ΣΧΗΜΑ 50: ΈΝΑ ΚΟΙΝΟ ΛΑΘΟΣ ΣΤΗ ΧΡΗΣΗ ΣΗΜΑΦΟΡΩΝ.............................................................................101
ΣΧΗΜΑ 51: ΈΝΑ ΑΚΟΜΗ ΚΟΙΝΟ ΛΑΘΟΣ ΣΤΗ ΧΡΗΣΗ ΣΗΜΑΦΟΡΩΝ. ...............................................................102
ΣΧΗΜΑ 52: ΥΛΟΠΟΙΗΣΗ ΣΗΜΑΦΟΡΩΝ ΧΡΗΣΙΜΟΠΟΙΩΝΤΑΣ ΑΚΕΡΑΙΕΣ ∆ΙΑΜΟΙΡΑΖΟΜΕΝΕΣ ΜΕΤΑΒΛΗΤΕΣ ΚΑΙ
ΤΗΝ ΑΤΟΜΙΚΗ ΛΕΙΤΟΥΡΓΙΑ TEST&SET() ΠΟΥ ΠΑΡΕΧΕΤΑΙ ΑΠΟ ΤΟ ΥΛΙΚΟ...........................................104
ΣΧΗΜΑ 53: ΠΕΡΙΓΡΑΦΗ ΕΝΕΡΓΕΙΩΝ ΠΟΥ ΕΚΤΕΛΟΥΝ ΟΙ ∆ΙΕΡΓΑΣΙΕΣ ΠΕΛΑΤΩΝ ΚΑΙ ΕΞΥΠΗΡΕΤΗ. ..................107
ΣΧΗΜΑ 54: ΜΙΑ ΠΡΩΤΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΠΕΛΑΤΩΝ-ΕΞΥΠΗΡΕΤΗ. .......................................................109
ΣΧΗΜΑ 55: ΜΙΑ ΒΕΛΤΙΣΤΟΠΟΙΗΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΠΕΛΑΤΩΝ-ΕΞΥΠΗΡΕΤΗ. .................................110
ΣΧΗΜΑ 56: ΜΙΑ ΛΑΘΟΣ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΠΕΛΑΤΩΝ-ΕΞΥΠΗΡΕΤΗ........................................................110
ΣΧΗΜΑ 57: ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΩΝ ΚΑΠΝΙΣΤΩΝ-ΠΩΛΗΤΗ. ....................................................................112
ΣΧΉΜΑ 58: PING-PONG................................................................................................................................113
ΣΧΗΜΑ 59: ΠΕΡΙΓΡΑΦΗ ΑΠΛΩΝ ΕΝΕΡΓΕΙΩΝ ΠΟΥ ΕΚΤΕΛΟΥΝ ΟΙ ∆ΙΕΡΓΑΣΙΕΣ ΕΠΙΒΑΤΩΝ ΚΑΙ ΑΥΤΟΚΙΝΗΤΩΝ..114
ΣΧΗΜΑ 60: ΑΠΛΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ............................................................114
ΣΧΗΜΑ 61: ΠΡΩΤΗ ΒΕΛΤΙΩΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ. ..................................115
ΣΧΗΜΑ 62: ∆ΕΥΤΕΡΗ ΒΕΛΤΙΩΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ.................................116
ΣΧΗΜΑ 63: ΛΥΣΗ ΣΤΗΝ ΑΠΛΗ ΕΚ∆ΟΣΗ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΤΟΥ ΚΟΥΡΕΑ...................................................117
ΣΧΗΜΑ 64: ΜΙΑ ΑΚΟΜΗ ΣΩΣΤΗ ΛΥΣΗ ΣΤΗΝ ΑΠΛΗ ΕΚ∆ΟΣΗ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΤΟΥ ΚΟΥΡΕΑ. ...................118
ΣΧΗΜΑ 65: ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΚΟΥΡΕΑ. .........................................................................................119
ΣΧΗΜΑ 66: ΠΡΩΤΗ ΠΡΟΣΠΑΘΕΙΑ ΕΠΙΛΥΣΗΣ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΑΝΑΓΝΩΣΤΩΝ-ΕΓΓΡΑΦΕΩΝ. .....................119
ΣΧΗΜΑ 67: ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΑΝΑΓΝΩΣΤΩΝ-ΕΓΓΡΑΦΕΩΝ.....................................................................120
ΣΧΗΜΑ 68: ΣΚΕΛΕΤΟΣ ΚΩ∆ΙΚΑ ∆ΙΕΡΓΑΣΙΑΣ ΑΥΤΟΚΙΝΗΤΟΥ. ........................................................................121
ΣΧΗΜΑ 69: ΠΕΡΙΓΡΑΦΗ ΤΩΝ ΕΝΕΡΓΕΙΩΝ ΤΩΝ ∆ΙΕΡΓΑΣΙΩΝ ΤΗΣ ΆΣΚΗΣΗΣ ΑΥΤΟΑΞΙΟΛΟΓΗΣΗΣ 44. ..............123
ΣΧΗΜΑ 70: ΣΚΕΛΕΤΟΣ ΚΩ∆ΙΚΑ ∆ΙΕΡΓΑΣΙΩΝ ΓΙΑ ΤΟ ΠΡΟΒΛΗΜΑ ΠΟΥ ΠΕΡΙΓΡΑΦΕΤΑΙ ΣΤΗΝ ΆΣΚΗΣΗ
ΑΥΤΟΑΞΙΟΛΟΓΟΓΗΣΗΣ 45...................................................................................................................125
ΣΧΗΜΑ 71: ΓΛΩΣΣΙΚΗ ΈΚΦΡΑΣΗ ΤΟΥ HANSEN. ...........................................................................................126
ΣΧΗΜΑ 72: ΑΤΟΜΙΚΗ ΕΚ∆ΟΣΗ ΤΩΝ DEPOSIT() ΚΑΙ WITHDRAW() ΧΡΗΣΙΜΟΠΟΙΩΝΤΑΣ ΤΗ ΓΛΩΣΣΙΚΗ ΕΚΦΡΑΣΗ
ΤΟΥ HANSEN. ...................................................................................................... □ 127
ΣΧΗΜΑ 73: ΓΛΩΣΣΙΚΗ ΈΚΦΡΑΣΗ ΚΡΙΣΙΜΗΣ ΠΕΡΙΟΧΗΣ ΥΠΟ ΣΥΝΘΗΚΗ. ......................................................127
ΣΧΗΜΑ 74: ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ ΜΕ ΧΡΗΣΗ ΚΡΙΣΙΜΩΝ ΠΕΡΙΟΧΩΝ ΥΠΟ ΣΥΝΘΗΚΗ
(ΑΝΤΙΣΤΟΙΧΗ ΕΚΕΙΝΗΣ ΤΟΥ ΣΧΗΜΑΤΟΣ 60). .......................................................................................129
ΣΧΗΜΑ 75: ΠΡΩΤΗ ΒΕΛΤΙΩΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ ΜΕ ΧΡΗΣΗ ΚΡΙΣΙΜΩΝ
ΠΕΡΙΟΧΩΝ ΥΠΟ ΣΥΝΘΗΚΗ (ΑΝΤΙΣΤΟΙΧΗ ΕΚΕΙΝΗΣ ΤΟΥ ΣΧΗΜΑΤΟΣ 61)...............................................129
ΣΧΗΜΑ 76: ∆ΕΥΤΕΡΗ ΒΕΛΤΙΩΜΕΝΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΖΩΟΛΟΓΙΚΟΥ ΚΗΠΟΥ ΜΕ ΧΡΗΣΗ ΚΡΙΣΙΜΩΝ
ΠΕΡΙΟΧΩΝ ΥΠΟ ΣΥΝΘΗΚΗ (ΑΝΤΙΣΤΟΙΧΗ ΕΚΕΙΝΗΣ ΤΟΥ ΣΧΗΜΑΤΟΣ 62)...............................................130
ΣΧΗΜΑ 77: ΛΥΣΗ ΑΝΤΙΣΤΟΙΧΗ ΕΚΕΙΝΗΣ ΤΟΥ ΣΧΗΜΑΤΟΣ 76 ΑΛΛΑ ΠΟΥ ΕΠΙΤΥΓΧΑΝΕΙ ΜΕΓΑΛΥΤΕΡΟ
ΠΑΡΑΛΛΗΛΙΣΜΟ. □ .............................................................................................................................131
ΣΧΗΜΑ 78: ΠΡΩΤΗ ΠΡΟΣΠΑΘΕΙΑ ΕΠΙΛΥΣΗΣ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΑΝΑΓΝΩΣΤΩΝ-ΕΓΓΡΑΦΕΩΝ ΜΕ ΧΡΗΣΗ
ΚΡΙΣΙΜΩΝ ΠΕΡΙΟΧΩΝ ΥΠΟ ΣΥΝΘΗΚΗ...................................................................................................131
ΣΧΗΜΑ 79: ΣΩΣΤΗ ΛΥΣΗ ΤΟΥ ΠΡΟΒΛΗΜΑΤΟΣ ΑΝΑΓΝΩΣΤΩΝ-ΕΓΓΡΑΦΕΩΝ ΜΕ ΧΡΗΣΗ ΚΡΙΣΙΜΩΝ ΠΕΡΙΟΧΩΝ
ΥΠΟ ΣΥΝΘΗΚΗ. ...................................................................................................................................132
ΣΧΗΜΑ 80: Ο ΑΛΓΟΡΙΘΜΟΣ ΤΟΥ ΑΡΤΟΠΩΛΕΙΟΥ. .........................................................................................134
ΣΧΗΜΑ 81: Η ΛΥΣΗ ΤΟΥ DEKKER. ...............................................................................................................135
ΣΧΗΜΑ 82: ∆ΙΚΑΙΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΟΥ ΑΜΟΙΒΑΙΟΥ ΑΠΟΚΛΕΙΣΜΟΥ ΜΕ ΧΡΗΣΗ TEST&SET()...........137
ΣΧΗΜΑ 83: ∆ΙΚΑΙΗ ΛΥΣΗ ΣΤΟ ΠΡΟΒΛΗΜΑ ΤΩΝ ΑΝΑΓΝΩΣΤΩΝ-ΕΓΓΡΑΦΕΩΝ. ................................................139
ΣΧΗΜΑ 84: ΕΝΕΡΓΕΙΕΣ ΠΟΥ ΕΠΙΤΕΛΟΥΝ ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ SEM_DOWN() ΚΑΙ GSEM_UP()...............................141
ΣΧΗΜΑ 85: ΥΛΟΠΟΙΗΣΗ ΓΕΝΙΚΟΥ ΣΗΜΑΦΟΡΟΥ ΑΠΟ ∆ΥΑ∆ΙΚΟ ΣΗΜΑΦΟΡΟ. ................................................141
ΣΧΗΜΑ 86: ΣΚΕΛΕΤΟΣ ΚΩ∆ΙΚΑ ΓΙΑ ΤΗΝ ΥΛΟΠΟΙΗΣΗ ΚΡΙΣΙΜΩΝ ΠΕΡΙΟΧΩΝ ΥΠΟ ΣΥΝΘΗΚΗ ΧΡΗΣΙΜΟΠΟΙΩΝΤΑΣ
ΣΗΜΑΦΟΡΟΥΣ. ....................................................................................................................................143
1ο Κεφάλαιο
Εισαγωγή
1ο Κεφάλαιο Εισαγωγή
Βάση ∆εδοµένων
Επεξεργαστής
Κειµένου Προγράµµατα Συστήµατος &
Εφαρµογών
Μεταγλωττιστής
Εκδότης
Λειτουργικό Σύστηµα
Υλικό
9
1ο Κεφάλαιο Εισαγωγή
10
1ο Κεφάλαιο Εισαγωγή
της ΚΜΕ), αλλά είναι εξαιρετικά ακριβοί. Έτσι, οι σηµερινοί υπολογιστές περιέχουν
έναν µικρό µόνο αριθµό καταχωρητών που µπορούν να αποθηκεύουν µόνο µερικές
δεκάδες ή µερικές εκατοντάδες bytes. Ωστόσο, απαιτείται η ύπαρξη πολύ µεγαλύτερου
χώρου αποθήκευσης προκειµένου να αποθηκεύονται εκεί ολόκληρα προγράµµατα,
καθώς και τα δεδοµένα που χρησιµοποιούνται από αυτά. Για το λόγο αυτό, το δεύτερο
βασικό συστατικό κάθε υπολογιστή είναι η µνήµη. Η µνήµη χρησιµοποιείται ως χώρος
αποθήκευσης και επεξεργασίας από τα προγράµµατα και περιέχει προσωρινά δεδοµένα
(που χάνονται όταν πάψει να λειτουργεί ο υπολογιστής). Μπορεί να αποθηκεύει πολύ
περισσότερα δεδοµένα από ότι οι καταχωρητές, αλλά είναι και αυτή πεπερασµένη. Ένα
πρόγραµµα δεν µπορεί να εκτελεστεί αν δεν βρίσκεται στη µνήµη του υπολογιστή. Έτσι,
προκειµένου να εκτελεστεί ένα πρόγραµµα θα πρέπει να φορτωθεί στη µνήµη.
Η ΚΜΕ επικοινωνεί µε τη µνήµη (και τις υπόλοιπες µονάδες του υπολογιστή) διαµέσου
του διαύλου του συστήµατος (system bus). Ο δίαυλος αποτελείται από έναν αριθµό
καναλιών µέσω των οποίων γίνεται η ανταλλαγή των πληροφοριών µεταξύ των
διαφορετικών µονάδων του συστήµατος (π.χ., µεταξύ µνήµης και ΚΜΕ).
Ένας υπολογιστής χρησιµοποιεί πολλά είδη µνήµης. Τα δύο βασικότερα χαρακτηριστικά
µιας µονάδας µνήµης είναι η ταχύτητα προσπέλασής της και το κόστος της. ∆υστυχώς,
τα δύο αυτά χαρακτηριστικά αντιπαλεύονται το ένα το άλλο. Γρήγορες µνήµες είναι
εξαιρετικά ακριβές για να µπορούν να παρέχονται σε µεγάλες ποσότητες. Έτσι, οι
σηµερινοί υπολογιστές χρησιµοποιούν πολλές διαφορετικές µονάδες µνήµης που
δηµιουργούν µια ιεραρχία. Η ιεραρχία αυτή φαίνεται στο Σχήµα 2.
Στη βάση της ιεραρχίας βρίσκονται οι µονάδες εξωτερικής αποθήκευσης, όπως π.χ.,
µονάδες ταινίας, µαγνητικοί δίσκοι, οπτικοί δίσκοι, κλπ. Οι µονάδες αυτές έχουν το
µικρότερο κόστος αλλά το µεγαλύτερο χρόνο προσπέλασης. Παρέχονται σε µεγάλες
ποσότητες αλλά είναι εξαιρετικά αργές. Στο επόµενο επίπεδο της ιεραρχίας είναι οι
(ηλεκτρονικοί) δίσκοι που είναι σχετικά φθηνοί αλλά και σχετικά αργοί (παρότι σαφώς
πιο γρήγοροι από τις µονάδες εξωτερικής αποθήκευσης). Στη συνέχεια, βρίσκεται η
κύρια µνήµη (main memory). Στη µονάδα αυτή αποθηκεύονται τα εκτελέσιµα
προγράµµατα, καθώς και τα δεδοµένα που απαιτούνται για την εκτέλεση τους. Ο χρόνος
προσπέλασης της κύριας µνήµης είναι σχετικά µικρός και σταθερός (αν και αρκετά
µεγαλύτερος από το χρόνο προσπέλασης των καταχωρητών). Όπως είναι αναµενόµενο,
οι µονάδες κύριας µνήµης είναι αρκετά ακριβότερες από τις µονάδες βοηθητικής
αποθήκευσης (δίσκοι, µονάδες εξωτερικής αποθήκευσης). Το µέγεθος της κύριας µνήµης
επηρεάζει σηµαντικά την ισχύ ενός υπολογιστή. Περισσότερη µνήµη σηµαίνει πως ο
υπολογιστής µπορεί να εκτελεί περισσότερο απαιτητικά σε µνήµη προγράµµατα και
µάλιστα περισσότερα του ενός από αυτά ταυτόχρονα.
Όταν ένα πρόγραµµα εκτελείται, τα δεδοµένα που χρειάζονται θα πρέπει να
τοποθετηθούν στους καταχωρητές του συστήµατος. Η ΚΜΕ λειτουργεί σε υψηλότερες
ταχύτητες από τη µνήµη. Έτσι, πολύς χρόνος καταναλώνεται για την µετακίνηση
δεδοµένων µεταξύ ΚΜΕ και κύριας µνήµης. Για τη µείωση του χρόνου αυτού, κάθε
υπολογιστικό σύστηµα χρησιµοποιεί µία ή περισσότερες κρυφές µνήµες. Η κρυφή µνήµη
βρίσκεται µεταξύ του επιπέδου της κύριας µνήµης και των καταχωρητών στην ιεραρχία
του Σχήµατος 2. Οι κρυφές µνήµες είναι πολύ γρήγορες, αλλά έχουν µεγάλο κόστος και
εποµένως το µέγεθος τους είναι περιορισµένο (αρκετά µικρότερο από εκείνο της κύριας
µνήµης).
11
1ο Κεφάλαιο Εισαγωγή
Καταχωρητές
Κρυφή Μνήµη
Κύρια Μνήµη
(Ηλεκτρονικοί) ∆ίσκοι
12
1ο Κεφάλαιο Εισαγωγή
13
1ο Κεφάλαιο Εισαγωγή
από το χώρο παροχέτευσης στο δίσκο και αντίστοιχα γράφει τα δεδοµένα εξόδου του στο
χώρο αυτό, αντί να διαβάζει κα να γράφει απευθείας στις συσκευές Ε/Ε. Παρόµοια, µια
συσκευή Ε/Ε διαβάζει τα δεδοµένα από το χώρο παροχέτευσης στο δίσκο και γράφει τα
δεδοµένα εισόδου επίσης στο χώρο αυτό.
Με τον τρόπο αυτό, οι λειτουργίες Ε/Ε ενός προγράµµατος µπορούν να εκτελεστούν
ακόµη και µετά τον τερµατισµό του (offline). Π.χ., θεωρήστε ένα πρόγραµµα που εκτελεί
µια εντολή εκτύπωσης σε κάποιον εκτυπωτή. Τα προς εκτύπωση δεδοµένα γράφονται
πρώτα στο δίσκο. Η αποστολή τους στον εκτυπωτή µπορεί να γίνει αρκετά αργότερα,
όταν π.χ., ο εκτυπωτής θα πάψει να είναι απασχοληµένος. Η τεχνική αυτή επιτρέπει στις
λειτουργίες Ε/Ε ενός προγράµµατος να εκτελούνται ενώ η ΚΜΕ χρησιµοποιείται για την
εκτέλεση ενός άλλου προγράµµατος. Παρατηρήστε ότι αυτό δεν µπορεί να επιτευχθεί µε
την απλούστερη τεχνική χρήσης προσωρινής αποθήκευσης που συζητήθηκε πιο πάνω, η
οποία επιτρέπει ταυτόχρονη λειτουργία της ΚΜΕ και της συσκευής Ε/Ε µόνο κατά την
εκτέλεση του ίδιου προγράµµατος. Η τεχνική της παροχέτευσης συνεπάγεται εποµένως
πιο αποδοτική χρήση της ΚΜΕ και των συσκευών Ε/Ε.
Είναι σηµαντικό να τονιστεί ότι η τεχνική της παροχέτευσης επιτρέπει σε πολλά
προγράµµατα να έχουν διαβαστεί και να έχουν αποθηκευτεί στο δίσκο. ∆ίνεται έτσι η
δυνατότητα στο λειτουργικό να διαλέξει ποιο από αυτά θα είναι το επόµενο που θα
τοποθετηθεί στη µνήµη προκειµένου να µπορέσει να εκτελεστεί.
14
1ο Κεφάλαιο Εισαγωγή
διαχείρισης της µνήµης είναι ένα ακόµη σηµαντικό πρόβληµα που θα µελετηθεί σε
κάποιο από τα επόµενα κεφάλαια. Ο διαχειριστής µνήµης αποτελεί ένα πολύ σηµαντικό
µέρος των λειτουργικών συστηµάτων.
15
1ο Κεφάλαιο Εισαγωγή
1.4 Προστασία
Ένας σηµαντικός στόχος ενός λειτουργικού συστήµατος είναι η παροχή προστασίας στα
προγράµµατα που εκτελούνται. Η τεχνική της παροχέτευσης επιφέρει τη συνύπαρξη
δεδοµένων πολλών προγραµµάτων στο δίσκο. Οι τεχνικές του πολυπρογραµµατισµού
και του διαµοιρασµού χρόνου οδηγούν στη συνύπαρξη πολλών ενεργών προγραµµάτων
στη µνήµη, καθώς και στην ταυτόχρονη διεκδίκηση της ΚΜΕ και άλλων πόρων από
πολλά ενεργά προγράµµατα (δηλαδή προγράµµατα των οποίων η εκτέλεση έχει ξεκινήσει
αλλά δεν έχει τερµατίσει).
Κάποια από τα προγράµµατα αυτά µπορεί, είτε λόγω κάποιου σφάλµατος, ή κακόβουλα,
να προσπαθήσουν να διαβάσουν ή να τροποποιήσουν τα δεδοµένα (ή/και τον κώδικα)
άλλων προγραµµάτων, αλλά και του ίδιου του λειτουργικού συστήµατος. Το ΛΣ θα
πρέπει να λαµβάνει κατάλληλες ενέργειες, προκειµένου η ορθή εκτέλεση ενός
προγράµµατος να µην εξαρτάται από την εκτέλεση άλλων προγραµµάτων που µπορεί να
γίνεται ταυτόχρονα.
16
1ο Κεφάλαιο Εισαγωγή
ζητώντας του να την εκτελέσει εκ µέρους του. Όλα τα ΛΣ παρέχουν µια συλλογή από
ρουτίνες, κάθε µια από τις οποίες αντιστοιχεί σε µια προνοµιακή εντολή που µπορεί να
εκτελείται από τα προγράµµατα των χρηστών. Οι ρουτίνες αυτές ονοµάζονται κλήσεις
συστήµατος.
Κάθε φορά που ένα πρόγραµµα χρήστη καλεί µια κλήση συστήµατος, γίνεται διακοπή
προς το ΛΣ και το σύστηµα µεταπίπτει από κατάσταση χρήστη σε κατάσταση
επιβλέποντος. Το ΛΣ αρχίζει να εκτελείται, αποκρυπτογραφεί το είδος της διακοπής και
αναλαµβάνει να διεκπεραιώσει την προνοµιακή εντολή που του ζητήθηκε, αφού φυσικά
πρώτα ελέγξει ότι αυτή είναι έγκυρη και ότι δεν παρουσιάζεται κανένα πρόβληµα
προστασίας. Εν τω µεταξύ, το ΛΣ µπορεί να αποφασίσει πως θα πρέπει να ανασταλεί η
εκτέλεση του προγράµµατος που περιµένει την διεκπεραίωση της προνοµιακής εντολής
και να αρχίσει να εκτελείται κάποιο άλλο πρόγραµµα, προκειµένου η ΚΜΕ να µην
παραµείνει ανενεργή. Παράλληλα, ίσως έχει ζητηθεί από κάποια από τις συσκευές Ε/Ε
να εκτελέσει τις απαραίτητες λειτουργίες για την διεκπεραίωση της προνοµιακής
εντολής. Όταν η συσκευή Ε/Ε τελειώσει την εκτέλεση της λειτουργίας που της ζητήθηκε,
διακόπτει και πάλι την ΚΜΕ, το σύστηµα εισέρχεται σε κατάσταση επιβλέποντος και το
ΛΣ λαµβάνει τις κατάλληλες ενέργειες εξυπηρέτησης της διακοπής. Αν η εκτέλεση της
προνοµιακής εντολής έχει τελειώσει, το ΛΣ έχει τη δυνατότητα να ξανα-αποδώσει την
ΚΜΕ στο πρόγραµµα που ζήτησε την προνοµιακή εντολή για να συνεχιστεί η εκτέλεσή
του (προφανώς µε µετάπτωση σε κατάσταση χρήστη). Εναλλακτικά, το ΛΣ µπορεί να
αποφασίσει ότι είναι προτιµότερο να συνεχιστεί η εκτέλεση του προγράµµατος που
απασχολούσε την ΚΜΕ όταν έγινε η διακοπή, ή µε οποιοδήποτε άλλο ενεργό
πρόγραµµα.
17
1ο Κεφάλαιο Εισαγωγή
Οι κίνδυνοι που εγκυµονούν αν αυτό δεν επιτευχθεί είναι φανεροί. Ένα κακόβουλο
πρόγραµµα χρήστη µπορεί να τροποποιήσει άλλα προγράµµατα, ώστε αυτά αντί να
επιτελούν τις εργασίες που ήταν προγραµµατισµένα να επιτελούν να εκτελούν
λειτουργίες προς όφελος του κακόβουλου προγράµµατος. Ακόµη µεγαλύτεροι θα ήταν οι
κίνδυνοι που θα µπορούσαν να προκύψουν, αν ένα πρόγραµµα χρήστη είχε τη
δυνατότητα να τροποποιεί τα δεδοµένα και τον κώδικα του ΛΣ. Τότε το πρόγραµµα αυτό
θα µπορούσε να χειριστεί όλους τους πόρους του συστήµατος κατά βούληση, π.χ.,
χρησιµοποιώντας όλη τη µνήµη για τις δικές του ανάγκες, µην αφήνοντας κανένα άλλο
πρόγραµµα να χρησιµοποιήσει την ΚΜΕ, κ.ο.κ.
Προκειµένου να επιτευχθεί τέτοιου είδους προστασία, συνήθως το υλικό παρέχει δύο
καταχωρητές, τον καταχωρητή βάσης (base register) και τον καταχωρητή ορίου (limit
register). Κάθε φορά που η ΚΜΕ αποδίδεται σε ένα πρόγραµµα, το ΛΣ αποθηκεύει στον
καταχωρητή βάσης τη διεύθυνση που καθορίζει την αρχή του χώρου διευθύνσεων του
προγράµµατος αυτού, ενώ στον καταχωρητή ορίου αποθηκεύεται το µέγεθος του χώρου
αυτού. Κάθε φορά που το πρόγραµµα ζητά να προσπελάσει κάποια θέση µνήµης,
γίνονται οι εξής έλεγχοι. Η ζητούµενη διεύθυνση (µε τη βοήθεια του υλικού) συγκρίνεται
πρώτα µε τον καταχωρητή βάσης. Αν είναι µεγαλύτερη, συγκρίνεται µε το άθροισµα του
καταχωρητή βάσης και του καταχωρητή ορίου, από το οποίο θα πρέπει να είναι
µικρότερη. Αν οι δύο αυτοί έλεγχοι επιτύχουν, η διεύθυνση είναι έγκυρη και η εκτέλεση
της εντολής που περιέχει την πρόσβαση σε αυτή τη διεύθυνση συνεχίζεται κανονικά.
∆ιαφορετικά, η διεύθυνση είναι άκυρη και προκαλείται διακοπή σφάλµατος που
ονοµάζεται παγίδευση (trap). Τα περισσότερα ΛΣ ενηµερώνουν το χρήστη για το σφάλµα
και διακόπτουν την εκτέλεση του προγράµµατος. Η διαδικασία αυτή περιγράφεται στο
Σχήµα 3. Με τον τρόπο αυτό, το ΛΣ µπορεί να εγγυηθεί ότι παρέχεται προστασία
διευθυνσιοδότησης.
Καταχωρητής Βάσης
Καταχωρητής +
Βάσης Καταχωρητής Ορίου
Σχήµα 3: Προστασία διευθυνσιοδότησης µνήµης µε χρήση των καταχωρητών βάσης και ορίου.
18
1ο Κεφάλαιο Εισαγωγή
19
1ο Κεφάλαιο Εισαγωγή
παροχή κλήσεων συστήµατος για τη διαχείριση αρχείων, την αποθήκευση αρχείων στα
διάφορα µέσα αποθήκευσης που παρέχονται στο σύστηµα, κ.α.
Ένα άλλο σηµαντικό συστατικό ενός ΛΣ είναι ο διαχειριστής του συστήµατος Ε/Ε. Σε
γενικές γραµµές, το µέρος αυτό είναι υπεύθυνο για την απόκρυψη από το χρήστη των
ιδιαιτεροτήτων κάθε συσκευής Ε/Ε. Κάθε συσκευή χρειάζεται, προκειµένου να
λειτουργήσει, ειδικό λογισµικό που ονοµάζεται οδηγός της συσκευής (device driver). Το
ΛΣ παρέχει µια σειρά από οδηγούς για τις συσκευές Ε/Ε του υπολογιστικού συστήµατος
στο οποίο εκτελείται. Θα πρέπει να τονιστεί ωστόσο, πως πολλές φορές οι οδηγοί
συσκευών δεν θεωρούνται µέρος του ΛΣ, εκτελούνται σε κατάσταση χρήστη και
µπορούν να αντικατασταθούν από άλλους της αρεσκείας του χρήστη.
Ο διαχειριστής δίσκου είναι ένα ακόµη συστατικό κάθε λειτουργικού συστήµατος. Ρόλος
του είναι να διαχειρίζεται αποδοτικά και δίκαια το χώρο του δίσκου, ο οποίος είναι το
σηµαντικότερο µέσο δευτερεύουσας αποθήκευσης. Τα περισσότερα ΛΣ παρέχουν επίσης
ειδικούς δικτυακούς µηχανισµούς που επιτρέπουν σε δύο ή περισσότερες µηχανές να
επικοινωνήσουν µεταξύ τους. Ένα ακόµη σηµαντικό µέρος ενός ΛΣ είναι και το
σύστηµα προστασίας που παρέχει, κάποιοι από τους µηχανισµούς του οποίου
συζητήθηκαν σε προηγούµενη ενότητα.
Κάθε ΛΣ ακολουθείται από λογισµικό που είναι γνωστό ως διερµηνευτής εντολών ή
φλοιός. Παρότι ο φλοιός δεν αποτελεί µέρος του ΛΣ, είναι ίσως το σηµαντικότερο
πρόγραµµα που τρέχει πάνω από αυτό, αφού παρέχει τη διεπιφάνεια χρήσης µεταξύ του
χρήστη και του ΛΣ. Ο φλοιός δέχεται εντολές του χρήστη και ζητά από το ΛΣ να τις
εκτελέσει. Ο φλοιός των Windows είναι το παραθυρικό περιβάλλον χρήσης που
εµφανίζεται όταν εκτελούνται τα Windows. Σε άλλα ΛΣ, ο φλοιός δεν είναι τόσο
εύχρηστος. Ο φλοιός εκτελείται σε κατάσταση χρήστη και µπορεί να αντικατασταθεί από
οποιονδήποτε φλοιό της αρεσκείας του χρήστη.
Ένα µικρό µέρος του ΛΣ ονοµάζεται πυρήνας. Ο πυρήνας χτίζεται κατευθείαν πάνω στο
υλικό του Η/Υ και είναι το περισσότερο εξαρτώµενο από τη µηχανή µέρος του ΛΣ. Στον
πυρήνα συνήθως ανήκουν τα µέρη εκείνα του ΛΣ που υλοποιούν τις παρακάτω
λειτουργίες:
• Χειρισµός διακοπών.
• ∆ηµιουργία και τερµατισµός διεργασιών.
• Μέρος του χρονοδροµολογητή.
• Συντονισµός διεργασιών.
• Αναστολή και αφύπνιση διεργασιών.
• Υποστήριξη δραστηριοτήτων Ε/Ε.
• Υποστήριξη δέσµευσης και αποδέσµευσης µνήµης, κ.α.
Μέρος του πυρήνα είναι συχνά γραµµένο σε γλώσσα µηχανής (assembly). Τα τελευταία
χρόνια παρουσιάζεται η τάση κάποια µέρη των ΛΣ να γράφονται σε µικροκώδικα
(κώδικα στοιχειωδών εντολών, ενσωµατωµένο στο υλικό).
20
1ο Κεφάλαιο Εισαγωγή
21
1ο Κεφάλαιο Εισαγωγή
Λύση
1. Βασικές ιδιότητες:
α) ΛΣ Οµαδικής Επεξεργασίας.
• Οι εργασίες υποβάλλονται στον Η/Υ κατά οµάδες.
• ∆εν είναι δυνατή η αλληλεπίδραση ανάµεσα στον χρήστη και στο πρόγραµµα
κατά τη διάρκεια της επεξεργασίας.
• Ο χρόνος απόκρισης για κάθε χρήστη είναι το χρονικό διάστηµα από την
υποβολή ως την παραλαβή της εργασίας.
β) ΛΣ ∆ιαµοιρασµού Χρόνου.
• Επιτρέπει την παράλληλη εξυπηρέτηση πολλών χρηστών.
• Επιτρέπει την αλληλεπίδραση των χρηστών µε τα προγράµµατά τους.
• ∆ιαµοιράζει τον χρόνο της ΚΜΕ µεταξύ των χρηστών, δηµιουργώντας την
εντύπωση της παράλληλης επεξεργασίας.
γ) ΛΣ Πραγµατικού Χρόνου.
• Εξυπηρετεί εργασίες που απαιτούν αυστηρά καθορισµένο όριο χρόνου
απόκρισης.
2. Είναι φανερό ότι τα συστήµατα οµαδικής επεξεργασίας είναι τα απλούστερα, ενώ τα
συστήµατα πραγµατικού χρόνου είναι τα συνθετότερα. Μια απλή εφαρµογή που
µπορεί να εξυπηρετηθεί από ένα σύστηµα οµαδικής επεξεργασίας, είναι επόµενο να
µπορεί επίσης να εξυπηρετηθεί και από ένα σύστηµα διαµοιρασµού χρόνου και από
ένα σύστηµα πραγµατικού χρόνου. Έτσι, το ερώτηµα ουσιαστικά ζητάει τον
απλούστερο τύπο ΛΣ που µπορεί να εξυπηρετήσει ικανοποιητικά την κάθε
εφαρµογή. Λέµε «ικανοποιητικά» γιατί µια εξίσου σηµαντική παράµετρος για την
επιλογή µας είναι η αποδοτικότητα. Για παράδειγµα, ένα σύστηµα που χρησιµοποιεί
εξεζητηµένους αλγόριθµους χρονοδροµολόγησης, µπορεί πράγµατι να εξυπηρετήσει
µια απλή εφαρµογή, όµως ενδεχοµένως να σπαταλά πολύ χρόνο για τη
22
1ο Κεφάλαιο Εισαγωγή
23
1ο Κεφάλαιο Εισαγωγή
24
1ο Κεφάλαιο Εισαγωγή
Εκτός από τον τελεστή ‘+’ της πρόσθεσης, άλλοι γνωστοί τελεστές είναι ο ‘–‘ της
αφαίρεσης, ο ‘*’ του πολλαπλασιασµού και ο ‘/’ της διαίρεσης. Όλοι χρησιµοποιούνται
µε αντίστοιχο τρόπο όπως ο ‘+’.
1.8.3 Βρόγχοι
Μια πρόταση repeat έχει τρία µέρη. Τις λέξεις κλειδιά (repeat, begin, end, until), την
ελεγχόµενη συνθήκη µετά το until, και τις προτάσεις που εκτελούνται επαναληπτικά όσο
η συνθήκη δεν είναι αληθής:
repeat
begin
<προτάσεις>;
end
until <συνθήκη>;
Οι προτάσεις που περιέχονται µεταξύ των begin, end ονοµάζονται µπλοκ εντολών της
repeat. Με λίγα λόγια η repeat καθορίζει ότι οι προτάσεις του µπλοκ εντολών της
εκτελούνται ξανά και ξανά. Μετά από κάθε εκτέλεσή τους, η συνθήκη που ακολουθεί το
until εκτιµάται. Αν η συνθήκη είναι ψευδής, η εκτέλεση των προτάσεων
επαναλαµβάνεται. ∆ιαφορετικά, η repeat τερµατίζει και η εκτέλεση συνεχίζει µε την
επόµενη εντολή στο πρόγραµµα (δηλαδή την εντολή που ακολουθεί την γραµµή «until
<συνθήκη>»). Η συνθήκη µπορεί να είναι οποιαδήποτε boolean έκφραση, όπου το ‘==’
συµβολίζει την δυαδική πράξη της ισότητας, το ‘!=’ συµβολίζει τη δυαδική πράξη της
διαφοράς (όχι ισότητα), ενώ τα ‘<’, ‘>’, ‘≤’, ‘≥’ συµβολίζουν τις γνωστές δυαδικές
πράξεις ανισοτήτων.
Με αντίστοιχο τρόπο λειτουργεί και η πρόταση while.
while (<συνθήκη>) do
begin
<προτάσεις>;
end
Όσο η συνθήκη της while αποτιµάται σε TRUE, εκτελείται το µπλοκ εντολών. Η while
ξεκινά µε αποτίµηση της συνθήκης της. Αν η συνθήκη είναι αληθής, εκτελείται το µπλοκ
της. Στη συνέχεια η συνθήκη αποτιµάται ξανά, κ.ο.κ.
Θα πρέπει να γίνει κατανοητό πως τα µπλοκ εντολών των repeat και while µπορεί να
περιέχουν είτε απλές προτάσεις, όπως π.χ., προτάσεις καταχώρησης, ή δοµηµένες
προτάσεις, όπως π.χ., άλλες προτάσεις repeat ή while. Στη δεύτερη περίπτωση έχουµε τη
δηµιουργία φωλιασµένων βρόγχων (δηλαδή βρόγχων που περιέχουν άλλους βρόγχους).
Συχνά θα χρησιµοποιηθεί και η ακόλουθη µορφή της repeat:
repeat
begin
<προτάσεις>;
end
forever;
25
1ο Κεφάλαιο Εισαγωγή
που υποδηλώνει την επαναληπτική εκτέλεση των προτάσεων για πάντα (αφού δεν
υπάρχει συνθήκη τερµατισµού, η repeat δεν θα τερµατίσει ποτέ). Εντελώς αντίστοιχη
είναι και η ακόλουθη µορφή της repeat:
repeat
begin
<προτάσεις>;
end
until FALSE;
Εφόσον η συνθήκη FALSE είναι πάντα ψευδής (εξ ορισµού), η συνθήκη της repeat δεν
θα γίνει ποτέ αληθής και άρα η repeat δεν θα τερµατίσει ποτέ.
Αντίστοιχη είναι επίσης και η ακόλουθη µορφή της while:
while (TRUE) do
begin
<προτάσεις>;
end
H συνθήκη των repeat, while µπορεί να είναι απλά µια µεταβλητή. Αν η µεταβλητή έχει
µη µηδενική τιµή, η συνθήκη αποτιµάται σε TRUE. ∆ιαφορετικά, η συνθήκη αποτιµάται
σε FALSE. Επίσης, η συνθήκη µπορεί να είναι της µορφής “not <µεταβλητή>”. Σε αυτή
την περίπτωση, η συνθήκη αποτιµάται σε TRUE, αν η τιµή της µεταβλητής είναι 0 και σε
FALSE στην αντίθετη περίπτωση.
Όταν το σύνολο των προτάσεων που περιέχονται σε έναν βρόγχο αποτελείται από µια
µόνο πρόταση, τα begin, end µπορούν να παραληφθούν.
Τέλος, ενδιαφέρον παρουσιάζουν οι ακόλουθες µορφές των repeat, while:
repeat noop; until <συνθήκη>;
while (<συνθήκη>) do noop;
Η εντολή noop δεν προκαλεί καµία ενέργεια (δηλαδή λέει στο µεταγλωτιστή να µην
κάνει τίποτα). Έτσι, η εντολή αυτή είναι χρήσιµη, µόνο όταν χρησιµοποιείται µε τον
τρόπο που παρουσιάστηκε παραπάνω. Στις παραπάνω προτάσεις, απλά εκτελείται
επαναληπτικά ο έλεγχος που καθορίζεται από τη συνθήκη και δεν γίνεται καµία άλλη
χρήσιµη ενέργεια. Με άλλα λόγια, η παραπάνω µορφή της repeat ή της while χρησιµεύει
για να κάνουµε ένα πρόγραµµα να µπλοκάρει σε κάποιο σηµείο (δηλαδή να µην
προχωράει η εκτέλεση του πιο κάτω από µια συγκεκριµένη εντολή ανακύκλωσης µε
άδειο µπλοκ εντολών), µέχρι µια συνθήκη να αποτιµηθεί σε TRUE ή σε FALSE,
αντίστοιχα.
Παράδειγµα 1
Θεωρήστε το ακόλουθο απλό πρόγραµµα:
int num = 0;
while (num < 3)
num = num+1;
print num;
26
1ο Κεφάλαιο Εισαγωγή
Ας εξετάσουµε τι θα εκτελέσει το πρόγραµµα αυτό. Η num έχει αρχικά την τιµή 0. Άρα,
η συνθήκη (num < 3) αποτιµάται σε TRUE και εκτελείται η πρόταση της while, η οποία
αλλάζει την τιµή του num σε 1. Στη συνέχεια, η συνθήκη αποτιµάται ξανά. Αφού 1 < 3 η
συνθήκη είναι και πάλι TRUE. Έτσι, η τιµή του num αυξάνει και πάλι σε 2. Η συνθήκη
αποτιµάται ξανά και είναι και πάλι TRUE. Η τιµή του num γίνεται στη συνέχεια 3 και η
συνθήκη αποτιµάται για άλλη µια φορά. Αυτή τη φορά, η συνθήκη αποτιµάται σε
FALSE (αφού δεν ισχύει ότι 3 < 3). Έτσι, η while τερµατίζει και εκτελείται η επόµενη
εντολή, δηλαδή η πρόταση «print num;». Τυπώνεται το 3 και το πρόγραµµα τερµατίζει.
Πως θα µπορούσαµε να πετύχουµε να εκτελεστούν ακριβώς οι ίδιες ενέργειες, αλλά µε
χρήση της repeat αντί της while;
int num = 0;
repeat
num = num + 1
until (num >= 3);
print num;
Ο αναγνώστης θα πρέπει να πείσει τον εαυτό του πως το πρόγραµµα αυτό εκτελεί τις
ίδιες λειτουργίες µε το προηγούµενο.
Παρατηρήστε ότι τα δύο προγράµµατα είναι εντελώς αντίστοιχα εκτός από τη συνθήκη
της repeat, η οποία είναι η άρνηση της συνθήκης της while. Αυτό ισχύει γιατί η while
εκτελεί επαναληπτικά τις προτάσεις της όσο η συνθήκη της είναι TRUE, ενώ η repeat
εκτελεί επαναληπτικά τις προτάσεις της µέχρι η συνθήκη της να γίνει TRUE (δηλαδή
όσο είναι FALSE). Μια άλλη διαφορά των repeat, while είναι πως η πρώτη αποτιµά τη
συνθήκη της αφού εκτελέσει τις προτάσεις της, ενώ η δεύτερη πριν συµβεί αυτό. Έτσι, οι
προτάσεις της repeat θα εκτελεστούν τουλάχιστον µια φορά, αλλά αυτό µπορεί να µην
συµβεί στην περίπτωση της while (αφού, αν, την πρώτη φορά, η συνθήκη της while
αποτιµηθεί σε FALSE, οι προτάσεις της δεν θα εκτελεστούν καµία φορά). □
Άλλες χρήσιµες εντολές, που καλό θα ήταν να γνωρίζει ο αναγνώστης, είναι οι for,
break, continue, κ.α. (αλλά δεν θα επεκταθούµε εδώ).
27
1ο Κεφάλαιο Εισαγωγή
εντολών παρέχεται από την if else της οποίας η γενική µορφή περιγράφεται στη
συνέχεια:
if (<συνθήκη>) then
begin
<προτάσεις 1>;
end
else
begin
<προτάσεις 2>;
end
Η συνθήκη αποτιµάται. Αν είναι TRUE εκτελείται το µπλοκ εντολών 1, ενώ διαφορετικά
εκτελείται το µπλοκ εντολών 2.
28
1ο Κεφάλαιο Εισαγωγή
Μεταβλητές δοµών τύπου 2fields αποτελούνται από δύο πεδία, µια ακέραια µεταβλητή a
και µια boolean µεταβλητή flag. Για να δηλώσουµε µια µεταβλητή τύπου δοµής 2fields
γράφουµε τα εξής:
struct 2fields b;
Η b είναι µια µεταβλητή τύπου δοµής 2fields. Η b αποτελείται από δύο πεδία, την
ακέραια µεταβλητή b.a και την boolean µεταβλητή b.flag.
1.8.7 Typedef
Η typedef επιτρέπει τη δηµιουργία ενός νέου ονόµατος για κάποιο τύπο. Ας επανέλθουµε
στον τύπο δοµής 2fields. Ο ορισµός:
typedef struct 2fields 2FIELDS;
υποδηλώνει ότι το όνοµα 2FIELDS µπορεί να χρησιµοποιηθεί αντί των δύο λέξεων struct
2fields. Έτσι, η δήλωση:
2FIELDS b;
είναι ένας ακόµη τρόπος να δηλώσουµε µια µεταβλητή b τύπου δοµής 2fields.
Οµοίως, ο ορισµός:
typedef int boolean;
υποδηλώνει ότι η λέξη boolean µπορεί να χρησιµοποιείται αντί του int για τη δήλωση
ακεραίων.
1.8.8 Συναρτήσεις
Συνάρτηση είναι µια αυτοδύναµη µονάδα κώδικα προγράµµατος που έχει σχεδιαστεί να
εκτελεί µια συγκεκριµένη εργασία. Μια συνάρτηση µπορεί να παράγει ενέργειες και να
παρέχει τιµές. Το Κεφάλαιο 3 προϋποθέτει κάποια εξοικείωση του αναγνώστη µε τον
τρόπο λειτουργίας µιας συνάρτησης.
29
2ο Κεφάλαιο
∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
31
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
32
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
έχει το δικό της ξεχωριστό PCB. Το PCB µιας διεργασίας συνήθως περιέχει τις
ακόλουθες πληροφορίες:
• Περιεχόµενα µετρητή προγράµµατος και άλλων καταχωρητών.
• Κατάσταση διεργασίας.
• ∆ιάφορες παραµέτρους χρονοδροµολόγησης.
• Ταυτότητα διεργασίας και άλλα στοιχεία ταυτοποίησης της διεργασίας.
• Χρόνος εκκίνησης της διεργασίας, χρόνος χρήσης της ΚΜΕ και άλλα λογιστικά
στοιχεία.
• ∆είκτες που καταγράφουν τις διευθύνσεις µνήµης στις οποίες βρίσκεται ο κώδικας,
τα δεδοµένα και η στοίβα της διεργασίας.
• ∆είκτες που καταγράφουν τους πόρους που έχει στην κατοχή της η διεργασία (π.χ.,
ανοιχτά αρχεία, συσκευές Ε/Ε, κλπ).
Το PCB µιας διεργασίας φαίνεται στο Σχήµα 5.
33
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Κάθε µια από τις θυγατρικές διεργασίες µπορεί να δηµιουργήσει άλλες θυγατρικές
διεργασίες και έτσι δηµιουργείται µια ιεραρχική δενδροειδής δοµή διεργασιών.
• Τερµατισµός διεργασίας. Εκτός από τον κανονικό τερµατισµό µιας διεργασίας που
γίνεται όταν εκτελεστεί ο κώδικάς της, το ΛΣ µπορεί να αποφασίσει να τερµατίσει
µια διεργασία για άλλους λόγους, π.χ., για λόγους προστασίας. Όταν τερµατίζει µια
διεργασία, το PCB της καταστρέφεται και όλοι οι πόροι που κατείχε ελευθερώνονται.
• Αναστολή-Επανενεργοποίηση διεργασίας. Μια διεργασία µπορεί να ανασταλεί επειδή
π.χ., περιµένει την εκτέλεση κάποιας λειτουργίας Ε/Ε. Μια διεργασία συνήθως
αναστέλλεται κάθε φορά που καλεί µια κλήση συστήµατος. Η επανενεργοποίηση της
διεργασίας είναι αποκλειστική ευθύνη του ΛΣ και γίνεται µόνο µετά από ενέργειες
που πραγµατοποιούνται από αυτό.
• ∆ροµολόγηση διεργασίας. Όπως έχει ήδη αναφερθεί, κάθε ΛΣ παρέχει ένα µηχανισµό
δροµολόγησης διεργασιών. Η δροµολόγηση των διεργασιών επηρεάζει σηµαντικά
την απόδοση ενός συστήµατος. Εποµένως, η σχεδίαση καλών αλγορίθµων
δροµολόγησης αποτελεί ένα σηµαντικό πρόβληµα των ΛΣ.
2.5 ∆ιακοπές
Μια διακοπή µπορεί να προκληθεί για πολλούς λόγους. Οι πιο σηµαντικοί από αυτούς
παρατίθενται στη συνέχεια:
• ∆ιακοπές κλήσεις επιβλέποντος. Πραγµατοποιούνται κάθε φορά που µια διεργασία
καλεί µια κλήση συστήµατος.
• ∆ιακοπές ελέγχου προγράµµατος. Πραγµατοποιούνται κάθε φορά που η εκτέλεση ενός
προγράµµατος οδηγεί σε λάθη, όπως π.χ., στην περίπτωση διαίρεσης µε το µηδέν.
• ∆ιακοπές που προκαλούνται από το ρολόι του συστήµατος, διακοπές ελέγχου του
υλικού του συστήµατος, κ.α.
• ∆ιακοπές Ε/Ε. Είναι διακοπές που προκαλούνται από τις συσκευές Ε/Ε, όταν µια
λειτουργία Ε/Ε περατωθεί, όταν συµβεί ένα λάθος, όταν ένα περιφερειακό είναι
έτοιµο για λειτουργία, κλπ.
• ∆ιακοπές που προκαλούνται από τον χρήστη µε την πίεση κάποιων πλήκτρων, όπως
π.χ., το restart και το INT.
∆ιακοπές µπορεί να προκληθούν για πολλούς ακόµη διαφορετικούς λόγους (οι
παραπάνω κατηγορίες δεν είναι εξαντλητικές). ∆εδοµένου του κώδικα µιας διεργασίας,
είναι πολύ σηµαντικό να γίνει σαφές ποια είναι τα δυνατά σηµεία διακοπής της
εκτέλεσης της διεργασίας. Στο παρακάτω παράδειγµα γίνεται προσπάθεια να
αποσαφηνιστεί το ερώτηµα αυτό.
Παράδειγµα 2
Θεωρείστε µια διεργασία που εκτελεί τον παρακάτω απλό κώδικα.
int b = 3, c = 4, p = 5;
int tmp, flag = 0;
34
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
tmp = 1;
while (p > 0)
begin
if (flag == 0)
tmp = tmp * b;
else
tmp = tmp * c;
flag = 1 – flag;
p = p - 1;
end
print tmp;
Ο παραπάνω κώδικας δεν κάνει κάτι ιδιαίτερα έξυπνο. Απλά υπολογίζει και τυπώνει (µε
λίγο πιο πολύπλοκο τρόπο από ότι ίσως θα έπρεπε) το b*c*b*c*b. Ωστόσο το σηµαντικό
σε αυτό το παράδειγµα δεν είναι να χρησιµοποιήσουµε έναν κώδικα που κάνει κάτι
εξαιρετικά χρήσιµο, αλλά να γίνει κατανοητό ποια είναι τα δυνατά σηµεία διακοπής στην
εκτέλεση του παραπάνω προγράµµατος. Αν το πρόγραµµα εκτελεστεί χωρίς διακοπές, θα
πραγµατοποιηθούν οι 28 λειτουργίες που περιγράφονται στη συνέχεια (µε τη σειρά που
παρουσιάζονται).
1. tmp = 1
2. Έλεγχος της συνθήκης της while (είναι TRUE)
3. Έλεγχος της συνθήκης της if (είναι TRUE)
4. tmp = 1 * 3 = 3
5. flag = 1
6. p = 4
7. Έλεγχος της συνθήκης της while (είναι TRUE)
8. Έλεγξχος της συνθήκης της if (είναι FALSE)
9. tmp = 3 * 4 = 12
10. flag = 0
11. p = 3
12. Έλεγχος της συνθήκης της while (είναι TRUE)
13. Έλεγχος της συνθήκης της if (είναι TRUE)
14. tmp = 12 * 3 = 36
15. flag = 1
16. p = 2
17. Έλεγχος της συνθήκης της while (είναι TRUE)
18. Έλεγχος της συνθήκης της if (είναι FALSE)
19. tmp = 36 * 4 = 144
20. flag = 0
21. p = 1
22. Έλεγχος της συνθήκης της while (είναι TRUE)
23. Έλεγχος της συνθήκης της if (είναι TRUE)
24. tmp = 144 * 3 = 432
35
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
25. flag = 1
26. p = 0
27. Έλεγχος της συνθήκης της while (είναι FALSE)
28. print 432
Εάν πραγµατοποιηθούν µία ή περισσότερες διακοπές, η διεργασία θα πρέπει και πάλι να
εκτελέσει ακριβώς τις ίδιες λειτουργίες µε αυτές που περιγράφτηκαν πιο πάνω. Το
γεγονός ότι µπορεί να συµβούν διακοπές, κατά τη διάρκεια εκτέλεσης µιας διεργασίας,
δεν πρέπει να επιφέρει την επανάληψη εκτέλεσης κάποιων λειτουργιών της διεργασίας,
ούτε την παράλειψη κάποιων από αυτές.
∆ιακοπή µπορεί να συµβεί πριν η µετά από κάθε µια από τις παραπάνω λειτουργίες. Η
διεργασία µπορεί να διακοπεί πριν καν προλάβει να εκτελέσει την πρώτη λειτουργία της.
Ας συζητήσουµε λίγο πιο αναλυτικά την εκτέλεση των δοµηµένων προτάσεων, όπως
π.χ., της if else και της while. Η εκτέλεση της while ξεκινά µε την εκτέλεση της
λειτουργίας 2 και τελειώνει µε την εκτέλεση της λειτουργίας 27. Υπάρχουν 25
διαφορετικά σηµεία στα οποία η διεργασία µπορεί να διακοπεί κατά τη διάρκεια
εκτέλεσης της while. Το πρώτο από αυτά είναι µετά την εκτέλεση της λειτουργίας 2 και
πριν την εκτέλεση της 3, το δεύτερο µεταξύ των λειτουργιών 3 και 4, κ.ο.κ., ενώ το
τελευταίο είναι µετά την εκτέλεση της λειτουργίας 26 και πριν την εκτέλεση της
λειτουργίας 27. Προσέξτε ότι ο έλεγχος µιας συνθήκης θεωρείται ξεχωριστή λειτουργία
και µια διεργασία µπορεί να διακοπεί αµέσως µετά τον έλεγχο κάποιας συνθήκης, αλλά
πριν την εκτέλεση της επόµενης εντολής. Π.χ., αν η διεργασία διακοπεί αφού γίνει ο
έλεγχος της γραµµής 17, η επόµενη εντολή που θα πρέπει να εκτελεστεί, όταν η
διεργασία ανακτήσει και πάλι τον έλεγχο της ΚΜΕ, είναι η εντολή if else. Το ΛΣ θα
πρέπει να θυµάται, ότι έκανε αποτίµηση της συνθήκης της while σε TRUE, ώστε να µην
επαναλάβει τη λειτουργία αυτή, αλλά να εκτελέσει την επόµενη εντολή που είναι η if
else (και πιο συγκεκριµένα η λειτουργία αποτίµησης της συνθήκης της if else της
γραµµής 18). Προφανώς, είναι δυνατόν, η διεργασία να διακοπεί αφού αποτιµήσει τόσο
τη συνθήκη της while, όσο και τη συνθήκη της if. Η περίπτωση αυτή θα συνέβαινε αν η
διακοπή λάµβανε χώρα π.χ., µετά την εκτέλεση και της λειτουργίας 18. Και σε αυτή την
περίπτωση το ΛΣ πρέπει να θυµάται αν θα συνεχίσει µε το µπλοκ προτάσεων του if ή µε
εκείνο του else, όταν η διεργασία αποκτείσει και πάλι τον έλεγχο της KME. □
Είναι σηµαντικό να γίνει κατανοητό, τι ενέργειες πραγµατοποιούνται όταν συµβαίνει µια
διακοπή. Το ΛΣ περιλαµβάνει ρουτίνες που είναι γνωστές ως χειριστές διακοπών
(interrupt handlers). Κάθε µια από αυτές είναι υπεύθυνη για την επεξεργασία κάποιου
είδους διακοπής. Κάθε φορά που προκαλείται µια διακοπή, οι ενέργειες που πρέπει να
πραγµατοποιηθούν περιγράφονται συνοπτικά στη συνέχεια:
• Ο µετρητής προγράµµατος και άλλοι χρήσιµοι καταχωρητές αποθηκεύονται
(συνήθως στο PCB της διεργασίας που ήταν εκτελούµενη όταν συνέβη η διακοπή).
• Το είδος της διακοπής αποσαφηνίζεται και καλείται ο κατάλληλος χειριστής
διακοπής.
• Όταν ο χειριστής διακοπής ολοκληρώσει τις λειτουργίες που πρέπει να επιτελέσει,
αρχίζει να εκτελείται ο χρονοδροµολογητής που αποφασίζει ποια θα είναι η επόµενη
προς εκτέλεση διεργασία.
36
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Παράδειγµα 3
Ας εστιάσουµε και πάλι στον (ψευδο-) κώδικα του Παραδείγµατος 2. Είναι γραµµένος σε
µορφή που θυµίζει κώδικα κάποιας σύγχρονης γλώσσας προγραµµατισµού (αφού
περιέχει δοµηµένες προτάσεις, όπως π.χ., η while και η if else). Προκειµένου να
εκτελεστεί, ο κώδικας θα πρέπει να µετατραπεί σε µορφή που να θυµίζει περισσότερο
γλώσσα µηχανής. Μια τέτοια µορφή παρουσιάζεται στη συνέχεια:
2012 b = 3;
2016 c = 4;
2020 p = 5;
2024 flag = 0;
2028 tmp = 1;
2032 label1:
2036 if (p <= 0) goto label2;
2040 if (flag == 0) goto label3;
2044 tmp = tmp * c;
2048 goto label4;
2052 label3:
2056 tmp = tmp * b;
2060 label4:
2064 flag = 1 – flag;
2068 p = p – 1;
2072 goto label1;
2076 label2:
2080 print tmp;
Κάθε ένας από τους αριθµούς που προηγούνται των εντολών στον κώδικα αποτελεί τη
θέση µνήµης από όπου διαβάζεται αυτή η εντολή (δηλαδή ο κώδικας της διεργασίας
βρίσκεται στις διευθύνσεις µνήµης 2012-2080, ενώ στη διεύθυνση µνήµης 2056
βρίσκεται η εντολή «tmp = tmp * b;»).
37
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Παράδειγµα 4 (Καλλές, Σγάρµπας, Ταµπακάς, «Σηµαντικά Σηµεία στη µελέτη του τόµου
Λειτουργικά Συστήµατα Ι της ΘΕ ΠΛΗ-11: Αρχές Τεχνολογίας Λογισµικού)
Έστω µια διεργασία που στόχος της είναι να πληροφορεί το χρήστη για το ρυθµό µε τον
οποίο αυξοµειώνεται το πλήθος όλων των διεργασιών που εξυπηρετούνται από το ΛΣ σε
κάποιο χρονικό διάστηµα. Έστω ότι ο αριθµός των διεργασιών που εκτελούνται την
τρέχουσα χρονική στιγµή στο σύστηµα δίνεται από τη συνάρτηση count-processes(). Το
πρόγραµµα που περιγράφεται στη συνέχεια επιτελεί 100 δειγµατοληψίες (δηλαδή καλεί
100 φορές τη συνάρτηση count-processes()) και αναφέρει αν ο αριθµός των διεργασιών
παρουσιάζει αύξηση ή µείωση από την τελευταία δειγµατοληψία, καθώς και τον
τρέχοντα αριθµό διεργασιών στο σύστηµα. Όταν εκτελείται αποτελεί και αυτό µια από
τις διεργασίες του συστήµατος.
Το πρόγραµµα δίνεται στη συνέχεια σε µορφή που είναι αρκετά κοντά σε µια
αναπαράσταση σε επίπεδο µηχανής (δηλαδή δεν περιέχει εντολές ανακύκλωσης, όπως
π.χ., while, repeat, for, κλπ.)
1004 print “Start”;
38
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
1008 i = 0;
1012 old_proc = 0;
1016 label1:
1020 if (i == 100)
1024 goto label2;
1028 new_proc = count_processes ();
1032 if (new_proc > old_proc)
1036 print “Up”;
1040 else if (old_proc > new_proc)
1044 print “Down”;
1048 print new_proc;
1052 old_proc = new_proc;
1056 i ++;
1060 goto label1:
1064 label2:
1068 print “End”;
Ας υποθέσουµε ότι οι πρώτες κλήσεις στην count_processes θα επέστρεφαν τους
αριθµούς 37, 41, 21, κλπ.
Έστω ότι η εκτέλεση του προγράµµατος ξεκινά από τη θέση µνήµης 1004 (όπου δείχνει
ο µετρητής προγράµµατος) και συνεχίζεται µέχρι και τη θέση µνήµης 1032. Πριν
εκτελεστεί η εντολή στη διεύθυνση 1036, η διαδικασία µας διακόπτεται και παύει να
χρησιµοποιεί την ΚΜΕ. Όταν αποκτηθεί και πάλι ο έλεγχος της ΚΜΕ από τη διεργασία,
πρέπει να ξέρουµε όχι µόνο το τρέχον σηµείο εκτέλεσης, αλλά και τη µορφή των
δεδοµένων τη στιγµή της διακοπής. Προκειµένου να επιτευχθεί αυτό, θα πρέπει να
φυλαχτούν στο PCB της διεργασίας πληροφορίες της µορφής:
Μετρητής = 1036, i = 0, new_proc = 37, old_proc = 0
Ας υποθέσουµε ότι, µετά την επανεκκίνηση, συµβαίνει και πάλι διακοπή πριν την
εκτέλεση της εντολής που βρίσκεται στη θέση 1052. Τότε στο PCB θα πρέπει να
φυλαχτούν οι ακόλουθες πληροφορίες:
Μετρητής = 1052, i = 0, new_proc = 37, old_proc = 0
Αν η διακοπή γινόταν πριν την εκτέλεση της εντολής που βρίσκεται στη διεύθυνση 1056,
τότε θα είχαµε
Μετρητής = 1056, i = 0, new_proc = 37, old_proc = 37
Και σε αυτό το παράδειγµα τα πράγµατα είναι πιο σύνθετα για λόγους αντίστοιχους
εκείνων που συζητήθηκαν στο προηγούµενο παράδειγµα. Ωστόσο, όπως
προαναφέρθηκε, το σηµαντικό είναι να γίνει κατανοητό πως η οµαλή επανατοποθέτηση
µιας διεργασίας στην ΚΜΕ απαιτεί την αποθήκευση διάφορων χρήσιµων πληροφοριών
τη στιγµή που γίνεται η διακοπή. □
2.6 Χρονοδροµόλογηση
Μια πολύ σηµαντική δοµή δεδοµένων που διατηρείται από το ΛΣ είναι η ουρά έτοιµων
διεργασιών (ready queue) ή η ουρά εκτέλεσης. Η ουρά αυτή περιέχει τα PCB των
39
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
διεργασιών που είναι έτοιµες προς εκτέλεση. Μια ουρά έτοιµων διεργασιών για ένα
σύστηµα µε 5 έτοιµες διεργασίες Α, Β, Γ, ∆ και Ε αναπαρίσταται στο Σχήµα 7.
τέλος
40
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
41
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
διεργασίας είναι ο χρόνος που µεσολαβεί από την είσοδό της στο σύστηµα µέχρι την
περάτωση της εκτέλεσής της. Για παράδειγµα αν µια διεργασία εισέρχεται στο σύστηµα
την χρονική στιγµή t1 και τερµατίζει την χρονική στιγµή t2, o χρόνος διεκπεραίωσης της
διεργασίας είναι Χ∆ = t2 – t1 (αφού t2-t1 χρονικές µονάδες παρέµεινε στο σύστηµα). Ο
χρόνος διεκπεραίωσης συµπεριλαµβάνει οποιαδήποτε καθυστέρηση µπορεί να υπέστη η
διεργασία πριν ή και κατά τη διάρκεια εκτέλεσής της. Ο µέσος χρόνος διεκπεραίωσης n
διεργασιών p1, ..., pn, ορίζεται ως
ΜΧ∆ = (Χ∆1 + Χ∆2 + ... + Χ∆n) / n ,
όπου Χ∆1, ..., Χ∆n είναι οι χρόνοι διεκπεραίωσης των διεργασιών p1, ..., pn, αντίστοιχα.
Ο χρόνος αναµονής µιας διεργασίας είναι ο χρόνος που µια διεργασία ξοδεύει στο
σύστηµα χωρίς να κατέχει την ΚΜΕ. Για παράδειγµα, αν µια διεργασία εισέρχεται στο
σύστηµα την χρονική στιγµή t1, τερµατίζει την χρονική στιγµή t2 και καταναλώνει χρόνο
d στην ΚΜΕ, ο χρόνος αναµονής είναι XA = t2–t1–d. Ο µέσος χρόνος αναµονής n
διεργασιών p1, ..., pn, ορίζεται ως
ΜΧΑ = (ΧΑ1 + ΧΑ2 + ... + ΧΑn) / n ,
όπου ΧΑ1, ..., ΧΑn είναι οι χρόνοι αναµονής των διεργασιών p1, ..., pn, αντίστοιχα.
Στα συστήµατα οµαδικής επεξεργασίας είναι σηµαντικό να γίνεται επίσης βέλτιστη
χρήση της ΚΜΕ και να µεγιστοποιείται ο ρυθµός απόδοσης, δηλαδή ο αριθµός των
διεργασιών που διεκπεραιώνονται στη µονάδα του χρόνου. Αντίθετα, σε ένα σύστηµα
διαµοιρασµού χρόνου, ο σηµαντικότερος στόχος είναι να ελαχιστοποιηθεί ο χρόνος
απόκρισης, δηλαδή ο χρόνος που µεσολαβεί από τη χρονική στιγµή που ο χρήστης
υπέβαλε την διεργασία του µέχρι τη χρονική στιγµή που έλαβε κάποια απόκριση από το
σύστηµα.
Θα πρέπει να τονιστεί πως κάποιοι από τους παραπάνω στόχους µπορεί να είναι
αντικρουόµενοι. ∆εδοµένου ότι τα σηµερινά συστήµατα είναι γενικού σκοπού (και άρα
λειτουργούν, ανάλογα µε τις ανάγκες των χρηστών, άλλοτε ως συστήµατα οµαδικής
επεξεργασίας και άλλοτε ως συστήµατα απόκρισης), ο σχεδιασµός ενός καλού
αλγόριθµου χρονοδροµολόγησης είναι επίπονη εργασία.
Υπάρχουν συστήµατα στα οποία οι διεργασίες είναι τόσες πολλές, ώστε δεν είναι εφικτό
(ή δεν είναι αποδοτικό) να βρίσκονται όλες ταυτόχρονα στη µνήµη. Οι διεργασίες που
δεν χωρούν στη µνήµη κρατούνται σε κάποια δοµή δεδοµένων στο δίσκο και ένας
δεύτερος χρονοδροµολογητής, ο χρονοδροµολογητής µακράς διάρκειας, αποφασίζει σε
ποιες από αυτές θα αποδοθεί η απαραίτητη µνήµη για την εκτέλεση τους. Με άλλα λόγια
ένα σύστηµα µπορεί να έχει περισσότερους από έναν χρονοδροµολογητές και
περισσότερες από µια ουρές χρονοδροµολόγησης. Ο χρονοδροµολογητής ΚΜΕ και ο
χρονοδροµολογητής µακράς διαρκείας είναι δύο από τα σηµαντικότερα παραδείγµατα. Η
κύρια διαφορά τους είναι πως ο χρονοδροµολογητής ΚΜΕ αναλαµβάνει καθήκον πολύ
πιο συχνά από τον χρονοδροµολογητή µακράς διαρκείας. Συχνά, παρόµοιοι αλγόριθµοι
χρονοδροµολόγησης χρησιµοποιούνται και στις δύο περιπτώσεις. Το Σχήµα 9
αναπαριστά το είδος της χρονοδροµολόγησης που παρέχεται από τους
χρονοδροµολογητές ΚΜΕ και µακράς διαρκείας.
42
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Χρονοδροµολογητής Χρονοδροµολογητής
µακράς διάρκειας ΚΜΕ
Μνήµη
∆ίσκος ΚΜΕ
Σχήµα 9: Είδος δροµολόγησης που παρέχεται από τους χρονοδροµολογητές ΚΜΕ και µακράς διάρκειας.
43
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
44
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
45
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Παράδειγµα 5
Θεωρήστε το ακόλουθο σύνολο πέντε διεργασιών.
46
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
∆ 4 10 ms
Ε 2 50 ms
Θεωρήστε ότι όλες οι διεργασίες εισέρχονται στο σύστηµα την χρονική στιγµή 0 µε τη σειρά
που εµφανίζονται στον πίνακα (δηλαδή µε σειρά Α, Β, Γ, ∆, Ε).
Μελετήστε το µέσο χρόνο διεκπεραίωσης και το µέσο χρόνο αναµονής αν ο αλγόριθµος
χρονοδροµολόγησης είναι ο ακόλουθος:
1. FCFS.
2. Μη-προεκχωρητικός Priority (θεωρήστε ότι µεγάλοι αριθµοί σηµατοδοτούν µεγάλες
προτεραιότητες).
3. SJF.
4. RR µε κβάντο χρόνου 10 ms.
Κάνετε την απλουστευτική παραδοχή ότι ο χρόνος εναλλαγής είναι 0.
Για κάθε έναν από τους αλγορίθµους θα φτιάξουµε ένα διάγραµµα που θα δείχνει πως
εκτελούνται οι διάφορες διεργασίες µε το πέρασµα του χρόνου. Το διάγραµµα αυτό είναι
γνωστό ως διάγραµµα Gantt.
1. FCFS
Α Β Γ ∆ Ε
0 70 80 100 110 160
Χρόνος σε ms
O αλγόριθµος FCFS τοποθετεί τις διεργασίες στην ουρά εκτέλεσης βάσει του τρόπου
που φθάνουν στο σύστηµα. Έτσι, αρχικά η ουρά περιέχει τις Α, Β, Γ, ∆, Ε µε αυτή την
σειρά (δηλαδή η Α είναι η πρώτη διεργασία και η Ε είναι η τελευταία). Ο FCFS
χρονοδροµολογεί πρώτα την Α η οποία χρειάζεται 70ms πριν τερµατίσει (ή ζητήσει να
εκτελέσει Ε/Ε). Έτσι, από τη χρονική στιγµή 0 µέχρι την χρονική στιγµή 70 ms
εκτελείται η Α. Στη συνέχεια δροµολογείται η Β για τα 10 ms που χρειάζεται. Η
επόµενη διεργασία που δροµολογείται είναι η Γ, η οποία ζητάει 20 ms. Η Γ αφήνει την
ΚΜΕ την χρονική στιγµή 100, οπότε και την καταλαµβάνει η ∆. Την χρονική στιγµή
110 η Ε αποκτά την ΚΜΕ, η οποία και την κρατά µέχρι τη χρονική στιγµή 160.
Από το διάγραµµα Gantt παρατηρούµε ότι Χ∆Α = 70 ms, Χ∆Β = 80 ms, Χ∆Γ = 100 ms,
Χ∆∆ = 110 ms, Χ∆Ε = 160 ms. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆+Χ∆Ε) / 5 =
(70+80+100+110+160) / 5 ms = 104 ms.
Επίσης, ΧΑΑ = 0 ms, ΧΑΒ = 70 ms, ΧΑΓ = 80 ms, ΧΑ∆ = 100 ms, ΧΑΕ = 110 ms.
Άρα, ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆+ΧΑΕ) / 5 = (0+70+80+100+110) / 5 ms = 72
ms.
2. Μη-προεκχωρητικός Priority.
Το διάγραµµα Gantt είναι το ακόλουθο:
47
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
∆ Α Γ Ε Β
0 10 80 100 150 160
Χρόνος σε ms
Ο αλγόριθµος αυτός δροµολογεί πρώτη τη διεργασία µε την µεγαλύτερη
προτεραιότητα. Έτσι, η ∆ θα είναι η πρώτη που θα εκτελεστεί. Στη συνέχεια, θα
εκτελεστεί µια από τις Α, Γ που έχουν τη δεύτερη µεγαλύτερη προτεραιότητα.
Θεωρούµε ότι εκτελείται πρώτα η Α (που εισήλθε πρώτη στην ουρά εκτέλεσης και στη
συνέχεια η Γ. Θα πρέπει να τονιστεί ότι η σειρά εκτέλεσης Γ, Α είναι εξίσου σωστή,
ενώ θα οδηγούσε και σε καλύτερο µέσο χρόνο διεκπεραίωσης, αφού η Γ είναι
συντοµότερη διεργασία από την Α. Αφού εκτελεστούν οι Α και Γ, εκτελείται η Ε και
τέλος η Β, που έχει τις µικρότερες προτεραιότητες.
Από το διάγραµµα Gantt παρατηρούµε ότι Χ∆Α = 80 ms, Χ∆Β = 160 ms, Χ∆Γ = 100
ms, Χ∆∆ = 10 ms, Χ∆Ε = 150 ms. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆+Χ∆Ε) / 5 =
(80+160+100+10+150) / 5 ms = 100 ms.
Επίσης, ΧΑΑ = 10 ms, ΧΑΒ = 150 ms, ΧΑΓ = 80 ms, ΧΑ∆ = 0 ms, ΧΑΕ = 100 ms.
Άρα, ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆+ΧΑΕ) / 5 = (10+150+80+0+100) / 5 ms = 68
ms.
3. SJF
Β ∆ Γ Ε Α
0 10 20 40 90 160
Χρόνος σε ms
Ο αλγόριθµος αυτός θα δροµολογήσει πρώτα τις σύντοµες διεργασίες Β και ∆ (η
σειρά δεν παίζει κανένα ρόλο). Στη συνέχεια θα δροµολογήσει την Γ που είναι η
επόµενη πιο σύντοµη, την Ε και τέλος την Α που είναι η µεγαλύτερη διεργασία.
Από το διάγραµµα Gantt παρατηρούµε ότι Χ∆Α = 160 ms, Χ∆Β = 10 ms, Χ∆Γ = 40
ms, Χ∆∆ = 20 ms, Χ∆Ε = 90 ms. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆+Χ∆Ε) / 5 =
(160+10+40+20+90) / 5 ms = 64 ms.
Επίσης, ΧΑΑ = 90 ms, ΧΑΒ = 0 ms, ΧΑΓ = 20 ms, ΧΑ∆ = 10 ms, ΧΑΕ = 40 ms. Άρα,
ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆+ΧΑΕ) / 5 = (90+0+20+10+40) / 5 ms = 32 ms.
4. RR
Α Β Γ ∆ Ε Α Γ Ε Α Ε Α Ε Α Ε Α Α
0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160
48
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
Χρόνος σε ms
Ο αλγόριθµος αυτός θα αποδίδει περιοδικά 10 ms του χρόνου της ΚΜΕ σε κάθε µια
από τις διεργασίες της ουράς εκτέλεσης ξεκινώντας από εκείνη που είναι πρώτη
στην ουρά και συνεχίζοντας προς το τέλος της.
Από το διάγραµµα Gantt παρατηρούµε ότι Χ∆Α = 160 ms, Χ∆Β = 20 ms, Χ∆Γ = 70
ms, Χ∆∆ = 40 ms, Χ∆Ε = 140 ms. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆+Χ∆Ε) / 5 =
(160+20+70+40+130) / 5 ms = 86 ms.
Επίσης, ΧΑΑ = (160-70) = 90 ms, ΧΑΒ = 10 ms, ΧΑΓ = (70-20) = 50 ms, ΧΑ∆ = 30
ms, ΧΑΕ = (140-50) = 90 ms. Άρα, ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆+ΧΑΕ) / 5 =
(90+10+50+30+90) / 5 ms = 54 ms.
Παρατηρήστε ότι ο µέσος χρόνος διεκπεραίωσης, αλλά και ο µέσος χρόνος αναµονής για
τον αλγόριθµο SJF είναι αρκετά µικρότεροι από ότι για τους άλλους αλγορίθµους.
Παρατηρήστε επίσης ότι ο αλγόριθµος FCFS παρουσιάζει τους χειρότερους χρόνους
διεκπεραίωσης και αναµονής.
Έστω ΧΕΑ ο χρόνος εκτέλεσης της Α, ΧΕΒ ο χρόνος εκτέλεσης της Β, κ.ο.κ.
Παρατηρήστε ότι, για κάθε αλγόριθµο, ισχύει η σχέση ΜΧ∆ = ΜΧΑ + (ΧΕΑ + ΧΕΒ + ...
+ ΧΕΕ)/5. Ο ΜΧ∆ και ΜΧΕ µπορεί να είναι διαφορετικοί για κάθε έναν από τους
αλγόριθµους, αλλά η ποσότητα (ΧΕΑ + ΧΕΒ + ... + ΧΕΕ)/5 είναι προφανώς ίδια για όλους
τους αλγόριθµους. Άρα, ο αλγόριθµος που έχει το µεγαλύτερο ΜΧ∆ έχει και το
µεγαλύτερο ΜΧΑ και αντίστροφα.
Είναι τέλος αξιοσηµείωτο πως στην περίπτωση που όλες οι διεργασίες είναι διαθέσιµες
εξ αρχής (όπως σε αυτό το παράδειγµα), η µη-προεκχωρητική και η προεκχωρητική
έκδοση του Priority λειτουργούν µε ακριβώς τον ίδιο τρόπο. Το ίδιο προφανώς ισχύει και
για τους SJF και SRTF (αφού αυτοί δεν είναι παρά ειδικές περιπτώσεις των δύο
εκδόσεων του Priority). □
Παράδειγµα 6
Ας µελετήσουµε τώρα µια γενικευµένη µορφή του Θέµατος 7 της 4ης γραπτής εργασίας του
Ακ. Έτους 2003-04.
Θεωρείστε πως οι παρακάτω διεργασίες εισέρχονται στο σύστηµα τις χρονικές στιγµές που
αναγράφονται:
Μελετήστε το µέσο χρόνο διεκπεραίωσης και το µέσο χρόνο αναµονής για τους
ακόλουθους αλγορίθµους:
1. FCFS
49
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
2. SJF
3. SRTF
4. RR µε κβάντο χρόνου 2 χρονικές µονάδες
Κάνετε την απλουστευτική παραδοχή ότι ο χρόνος εναλλαγής είναι 0.
Τα διαγράµµατα Gantt για κάθε έναν από τους παραπάνω αλγόριθµους φαίνονται στο
Σχήµα 11. Κατά την πρώτη χρονική µονάδα το σύστηµα είναι ανενεργό, αφού δεν
υπάρχει καµία διεργασία στο σύστηµα.
1. Ο FCFS δροµολογεί τις διεργασίες µε τη σειρά άφιξής τους. Εποµένως, πρώτα
δροµολογείται η Α, στη συνέχεια η Β, στη συνέχεια η Γ, και τέλος η ∆. Από το 1ο
διάγραµµα Gantt του Σχήµατος 11 παρατηρούµε ότι Χ∆Α = (9-1) = 8 χρονικές
µονάδες, Χ∆Β = (13-2) = 11 χρονικές µονάδες, Χ∆Γ = (22-3) = 19 χρονικές µονάδες,
Χ∆∆ = (27-4) = 23 χρονικές µονάδες. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆) / 4 =
(8+11+19+23) / 4 χρονικές µονάδες = 15,25 χρονικές µονάδες.
Επίσης, ΧΑΑ = (9-8-1) = 0 χρονικές µονάδες, ΧΑΒ = (13-4-2) = 7 χρονικές µονάδες,
ΧΑΓ = (22-9-3) = 10 χρονικές µονάδες, ΧΑ∆ = (27-5-4) = 18 χρονικές µονάδες. Άρα,
ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆) / 4 = (0+7+10+18) / 4 χρονικές µονάδες = 8,75
χρονικές µονάδες.
2. Ο SJF ξεκινά µε την διεργασία Α, τη µόνη διαθέσιµη διεργασία στο σύστηµα την
χρονική στιγµή 1. Όταν η Α τερµατίζει την χρονική στιγµή 9, οι διεργασίες Β, Γ και
∆ βρίσκονται όλες στο σύστηµα. Ο SJF διαλέγει τη µικρότερη από αυτές, δηλαδή την
Β και την εκτελεί. Στη συνέχεια εκτελείται η ∆ και τέλος η Γ.
Από το 2ο διάγραµµα Gantt του Σχήµατος 11 παρατηρούµε ότι Χ∆Α = (9-1) = 8
χρονικές µονάδες, Χ∆Β = (13-2) = 11 χρονικές µονάδες, Χ∆Γ = (27-3) = 24 χρονικές
µονάδες, Χ∆∆ = (18-4) = 14 χρονικές µονάδες. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆)
/ 4 = (8+11+24+14) / 4 χρονικές µονάδες = 14,25 χρονικές µονάδες.
Επίσης, ΧΑΑ = (9-8-1) = 0 χρονικές µονάδες, ΧΑΒ = (13-4-2) = 7 χρονικές µονάδες,
ΧΑΓ = (27-9-3) = 15 χρονικές µονάδες, ΧΑ∆ = (18-5-4) = 9 χρονικές µονάδες. Άρα,
ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆) / 4 = (0+7+15+9) / 4 χρονικές µονάδες = 7,75
χρονικές µονάδες.
Παρατηρήστε ότι ο SJF είναι µόνο οριακά καλύτερος από τον FCFS σε αυτό το
παράδειγµα. Αυτό οφείλεται στο γεγονός ότι οι µικρές διεργασίες εισήλθαν στο
σύστηµα λίγο µετά την απόδοση της ΚΜΕ σε µια µεγάλη διεργασία, την Α.
∆εδοµένου ότι ο SJF δεν είναι προεκχωρητικός, θα συνεχίσει να εκτελεί την Α µέχρι
αυτή να ελευθερώσει την ΚΜΕ. Η εκτέλεση της µεγάλης αυτής διεργασίας πρώτης,
οδηγεί σε άσχηµους χρόνους διεκπεραίωσης και αναµονής (όπως φάνηκε στο
παράδειγµα αυτό).
3. Ο SRTF ξεκινά τη δροµολόγηση µε την διεργασία Α που είναι η µόνη διαθέσιµη την
χρονική στιγµή 1. Όταν όµως τη χρονική στιγµή 2 έρχεται η Β, ο SRTF συγκρίνει τον
χρόνο που αποµένει να εκτελεστεί η Α πριν απελευθερώσει την ΚΜΕ (που είναι 7
µονάδες) µε τον χρόνο που χρειάζεται την ΚΜΕ η Β (που είναι 4 µονάδες) και
διαλέγει να δροµολογήσει την Β, που θα απασχολήσει την ΚΜΕ για λιγότερο χρόνο.
Την χρονική στιγµή 3 γίνεται και πάλι διακοπή και ο SRTF καλείται να διαλέξει εκ
50
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
νέου ποια διεργασία θα εκτελεστεί στην ΚΜΕ. Ο υπολειπόµενος χρόνος της Α είναι 7
µονάδες, ο υπολειπόµενος χρόνος της Β είναι 3 µονάδες και ο απαιτούµενος χρόνος
της Γ είναι 9 µονάδες. Εποµένως, η Β συνεχίζει την εκτέλεσή της µέχρι να προκληθεί
και νέα διακοπή. Αυτό συµβαίνει την χρονική στιγµή 4, που στο σύστηµα εισέρχεται
η ∆. Οι απαιτούµενοι (υπολειπόµενοι) χρόνοι συγκρίνονται και πάλι και ο SRTF
αποφασίζει πως η Β εξακολουθεί να είναι η διεργασία µε τη χαµηλότερη απαίτηση σε
ΚΜΕ, οπότε και εκτελείται ξανά. Όταν η Β τελειώνει τη χρονική στιγµή 6,
εκτελούνται οι ∆, Α και Γ (µε αυτή τη σειρά).
Από το 3ο διάγραµµα Gantt του Σχήµατος 11 παρατηρούµε ότι Χ∆Α = (18-1) = 17
χρονικές µονάδες, Χ∆Β = (6-2) = 4 χρονικές µονάδες, Χ∆Γ = (27-3) = 24 χρονικές
µονάδες, Χ∆∆ = (11-4) = 7 χρονικές µονάδες. Άρα, ΜΧ∆ = (Χ∆Α+Χ∆Β+Χ∆Γ+Χ∆∆) /
4 = (17+4+24+7) / 4 χρονικές µονάδες = 13 χρονικές µονάδες.
Επίσης, ΧΑΑ = (18-8-1) = 9 χρονικές µονάδες, ΧΑΒ = (6-4-2) = 0 χρονικές µονάδες,
ΧΑΓ = (27-9-3) = 15 χρονικές µονάδες, ΧΑ∆ = (11-5-4) = 2 χρονικές µονάδες. Άρα,
ΜΧΑ = (ΧΑΑ+ΧΑΒ+ΧΑΓ+ΧΑ∆) / 4 = (9+0+15+2) / 4 χρονικές µονάδες = 6,5
χρονικές µονάδες.
4. Για να κατανοήσουµε τον τρόπο εκτέλεσης του RR θα πρέπει να µελετήσουµε σε
κάθε χρονική στιγµή τι περιέχει η ουρά έτοιµων εργασιών. Την χρονική στιγµή 0 το
σύστηµα είναι ανενεργό, ενώ την 1 έρχεται η Α και ξεκινά να εκτελείται στην ΚΜΕ.
Την χρονική στιγµή 2 γίνεται διακοπή και στο σύστηµα καταφθάνει η Β. Τώρα
υπάρχουν δύο επιλογές. Είτε ο χρονοδροµολογητής θα τοποθετήσει στην ουρά την Α
τη στιγµή της διακοπής και µετά θα τοποθετηθεί στην ουρά και η Β, ή θα γίνει το
αντίστροφο. Στην πρώτη περίπτωση, η πρώτη διεργασία στην ουρά είναι η Α και η
ΚΜΕ θα αποδοθεί και πάλι σε αυτήν για το επόµενο κβάντο χρόνου, ενώ στη
δεύτερη περίπτωση, η πρώτη διεργασία στην ουρά είναι η Β και άρα η ΚΜΕ θα
αποδοθεί στην Β για το επόµενο κβάντο χρόνου. Θεωρούµε στη συνέχεια ότι ο
χρονοδροµολογητής λειτουργεί όπως καθορίζεται από την πρώτη περίπτωση. Θα
πρέπει να τονιστεί πως εξίσου σωστό είναι και να θεωρηθεί ότι ακολουθείται η
δεύτερη περίπτωση.
Μπορούµε εποµένως να φτιάξουµε έναν πίνακα που θα παρέχει, σε κάθε χρονική
στιγµή, τις διεργασίες που εισέρχονται, τη διεργασία που απασχολεί την ΚΜΕ, τις
διεργασίες που βρίσκονται στην ουρά έτοιµων διεργασιών, καθώς και τις διεργασίες
που τερµατίζουν. Ο πίνακας φαίνεται στο Σχήµα 10. Στη γραµµή i της στήλης
«ΚΜΕ» καταγράφεται κάθε φορά η διεργασία που θα απασχολήσει την ΚΜΕ από τη
χρονική στιγµή i έως την (i+1). Η διεργασία αυτή ήταν η πρώτη διεργασία στην ουρά
έτοιµων διεργασιών την χρονική στιγµή i. Εποµένως, η ουρά διεργασιών την χρονική
στιγµή 4 περιέχει τις διεργασίες Α, Γ, Β, ∆ µε τη σειρά που αναγράφονται (παρότι η
Α δεν συµπεριλαµβάνεται στη στήλη «Ουρά Έτοιµων ∆ιεργασιών» της γραµµής 4).
Η διεργασία Α είναι αυτή που θα κατέχει την ΚΜΕ την χρονική µονάδα που
µεσολαβεί από τη χρονική στιγµή 4 έως τη χρονική στιγµή 5.
Την χρονική στιγµή 1 εισέρχεται η Α και αµέσως καταλαµβάνει την ΚΜΕ. Την
χρονική στιγµή 2 εισέρχεται η Β και τοποθετείται στην ουρά µετά την Α. Την
χρονική στιγµή 3 εισέρχεται η Γ. Τη στιγµή της διακοπής η ουρά περιέχει την Β.
Μετά την διακοπή τοποθετείται στην ουρά και η διεργασία Α και στη συνέχεια η
51
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
52
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
53
1. FCFS
ανενεργό Α Β Γ ∆
0 1 9 13 22 27
ανενεργό Α Α Β Α Γ Β ∆ Α Γ Β ∆ Α Γ Β ∆ Α Γ ∆ Α Γ ∆ Α Γ Γ Γ Γ
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
ΜΧ∆. Προσοχή: Το
ότι ο SJF είναι βέλτιστος ως προς τον µέσο χρόνο διεκπεραίωσης και τον µέσο χρόνο
αναµονής δεν σηµαίνει πως η απάντηση στην ερώτηση «Ποιος αλγόριθµος έχει τον
µικρότερο µέσο χρόνο διεκπεραίωσης (ή αναµονής);», για κάποιο συγκεκριµένο
παράδειγµα, είναι ο SJF. Ο λόγος για αυτό είναι πως και κάποιος άλλος αλγόριθµος
µπορεί να επιτυγχάνει εξίσου καλό µέσο χρόνο διεκπεραίωσης (ή αναµονής) για κάποιο
συγκεκριµένο παράδειγµα. Θα πρέπει εποµένως κάθε φορά να φτιάχνουµε το διάγραµµα
Gantt για να βλέπουµε αν όντως ο SJF υπερέχει έναντι των υπολοίπων ή αν κάποιοι από
τους υπόλοιπους είναι εξίσου καλοί µε αυτόν για το συγκεκριµένο παράδειγµα. □
56
2ο Κεφάλαιο ∆ιεργασίες – Χρονοδροµολόγηση ∆ιεργασιών
∆ιεργασία ∆ιάρκεια
Α 3
Β 1
Γ 2
∆ 1
Ε 1
57
3ο Κεφάλαιο
∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Β: tmp = 1 (γρ. 1)
59
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Α: tmp = 1 (γρ. 1)
Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της if (γρ. 3)
tmp = 1 * 3 = 3 (γρ. 4)
flag = 1 (γρ. 5)
p=4 (γρ. 6)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της if (γρ. 8)
tmp = 3 * 4 = 12 (γρ. 9)
flag = 0 (γρ. 10)
p=3 (γρ. 11)
Έλεγχος της συνθήκης της while (γρ. 12)
Β: Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της if (γρ. 3)
tmp = 1 * 3 = 3 (γρ. 4)
flag = 1 (γρ. 5)
p=4 (γρ. 6)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της if (γρ. 8)
tmp = 3 * 4 = 12 (γρ. 9)
flag = 0 (γρ. 10)
p=3 (γρ. 11)
Έλεγχος της συνθήκης της while (γρ. 12)
Έξεγχξε τη συνθήκη της if (γρ. 13)
tmp = 12 * 3 = 36 (γρ. 14)
flag = 1 (γρ. 15)
p=2 (γρ. 16)
Έλεγχος της συνθήκης της while (γρ. 17)
Έλεγχος της συνθήκης της if (γρ. 18)
tmp = 36 * 4 = 144 (γρ. 19)
flag = 0 (γρ. 20)
Α: Έλεγχος της συνθήκης της if (γρ. 13)
Β: p = 1 (γρ. 21)
Έλεγχος της συνθήκης της while (γρ. 22)
Έλεγχος της συνθήκης της if (γρ. 23)
tmp = 144 * 3 = 432 (γρ. 24)
flag = 1 (γρ. 25)
p=0 (γρ. 26)
Α: tmp = 12 * 3 = 36 (γρ. 14)
flag = 1 (γρ. 15)
p=2 (γρ. 16)
Έλεγχος της συνθήκης της while (γρ. 17)
Έλεγχος της συνθήκης της if (γρ. 18)
tmp = 36 * 4 = 144 (γρ. 19)
flag = 0 (γρ. 20)
p=1 (γρ. 21)
Έλεγχος της συνθήκης της while (γρ. 22)
Έλεγχος της συνθήκης της if (γρ. 23)
tmp = 144 * 3 = 432 (γρ. 24)
Β: Έλεγχος της συνθήκης της while (γρ. 27)
Α: flag = 1 (γρ. 25)
60
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
∆ιεργασία Α ∆ιεργασία Β
tmp = 1 (γρ. 1)
tmp = 1 (γρ. 1)
Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της if (γρ. 3)
tmp = 1 * 3 = 3 (γρ. 4)
flag = 1 (γρ. 5)
p=4 (γρ. 6)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της if (γρ. 8)
tmp = 3 * 4 = 12 (γρ. 9)
flag = 0 (γρ. 10)
p=3 (γρ. 11)
Έλεγχος της συνθήκης της while (γρ. 12)
Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της if (γρ. 3)
tmp = 1 * 3 = 3 (γρ. 4)
flag = 1 (γρ. 5)
p=4 (γρ. 6)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της if (γρ. 8)
tmp = 3 * 4 = 12 (γρ. 9)
flag = 0 (γρ. 10)
p=3 (γρ. 11)
Έλεγχος της συνθήκης της while (γρ. 12)
Χρόνος Έλεγχος της συνθήκης της if (γρ. 13)
tmp = 12 * 3 = 36 (γρ. 14)
flag = 1 (γρ. 15)
p=2 (γρ. 16)
Έλεγχος της συνθήκης της while (γρ. 17)
Έλεγχος της συνθήκης της if (γρ. 18)
tmp = 36 * 4 = 144 (γρ. 19)
flag = 0 (γρ. 20)
Έλεγχος της συνθήκης της if (γρ. 13)
p=1 (γρ. 21)
Έλεγχος της συνθήκης της while (γρ. 22)
Έλεγχος της συνθήκης της if (γρ. 23)
tmp = 144 * 3 = 432 (γρ. 24)
flag = 1 (γρ. 25)
p=0 (γρ. 26)
tmp = 12 * 3 = 36 (γρ. 14)
flag = 1 (γρ. 15)
p=2 (γρ. 16)
61
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
∆ιεργασία Α ∆ιεργασία Β
tmp = 1 (γρ. 1)
tmp = 1 (γρ. 1)
Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της while (γρ. 2)
Έλεγχος της συνθήκης της if (γρ. 3)
Έλεγχος της συνθήκης της if (γρ. 3)
tmp = 1 * 3 = 3 (γρ. 4)
tmp = 1 * 3 = 3 (γρ. 4)
flag = 1 (γρ. 5)
flag = 1 (γρ. 5)
p=4 (γρ. 6)
p=4 (γρ. 6)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της while (γρ. 7)
Έλεγχος της συνθήκης της if (γρ. 8)
Έλεγχος της συνθήκης της if (γρ. 8)
tmp = 3 * 4 = 12 (γρ. 9)
tmp = 3 * 4 = 12 (γρ. 9)
flag = 0 (γρ. 10)
flag = 0 (γρ. 10)
p=3 (γρ. 11)
p=3 (γρ. 11)
Έλεγχος της συνθήκης της while (γρ. 12)
Έλεγχος της συνθήκης της if (γρ. 13)
tmp = 12 * 3 = 36 (γρ. 14)
flag = 1 (γρ. 15)
p=2 (γρ. 16)
Έλεγχος της συνθήκης της while (γρ. 17)
Έλεγχος της συνθήκης της if (γρ. 18)
Έλεγχος της συνθήκης της while (γρ. 12)
62
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Παράδειγµα 7
∆ίνεται το ακόλουθο πρόγραµµα:
shared int tally;
begin
tally := 0;
cobegin
increase();
increase();
coend
63
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
end
όπου increase() είναι µια ρουτίνα (που θα δοθεί στη συνέχεια). Τι συµβαίνει όταν
εκτελείται ο παραπάνω κώδικας;
Με βάση όσα ήδη ειπώθηκαν, όταν η εντολή cobegin coend εκτελείται, δηµιουργούνται
δύο διεργασίες που κάθε µια εκτελεί την ρουτίνα increase(). Οι δύο διεργασίες
εκτελούνται ταυτόχρονα. Ενδιαφέρον ωστόσο παρουσιάζει και το γεγονός ότι η tally
είναι µια διαµοιραζόµενη (shared) µεταβλητή. Μια διαµοιραζόµενη µεταβλητή µπορεί να
γραφτεί ή να αναγνωστεί από περισσότερες από µια διεργασίες που εκτελούνται
ταυτόχρονα. Οι διαµοιραζόµενες µεταβλητές δηλώνονται και αρχικοποιούνται πριν
δηµιουργηθούν οι διεργασίες που θα τις διαχειριστούν. (Στο παράδειγµά µας, οι δύο
αυτές διεργασίες είναι οι διεργασίες που εκτελούν την increase() ταυτόχρονα.)
Η τιµή της tally είναι µια θέση µνήµης που µπορεί να προσπελαστεί και από τις δύο
διεργασίες. Όταν η µία γράφει µια τιµή στην tally, η τιµή αυτή µπορεί να διαβαστεί και
από την άλλη διεργασία. □
Παράδειγµα 8
Έστω ότι ο κώδικας της increase() του Παραδείγµατος 7 είναι ο ακόλουθος:
void increase()
int tmp, i; /* τοπικές µεταβλητές */
begin
tmp = tally;
tmp = tmp+1;
tally = tmp;
end
Έχουµε δύο διεργασίες που εκτελούνται ταυτόχρονα στο σύστηµα µας, έστω ότι η πρώτη
είναι η Α και η δεύτερη η Β. Ο κώδικας που εκτελεί η κάθε διεργασία έχει τη µορφή που
φαίνεται στο Σχήµα 15 (επειδή η µεταβλητή tmp είναι τοπική, έχει αντικατασταθεί στο
Σχήµα 15 από δύο µεταβλητές, tmpA, tmpB, µία για κάθε διεργασία):
Κώδικας διεργασίας A Κώδικας διεργασίας B
1. tmpΑ = tally; 1. tmpB = tally;
2. tmpΑ = tmpΑ +1; 2. tmpB = tmpB +1;
3. tally = tmpΑ; 3. tally = tmpB;
64
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σε αυτή την περίπτωση, η διεργασία Α αποθηκεύει στην µεταβλητή tmpA την τιµή της
tally (δηλαδή το 0). Στη συνέχεια αυξάνει την tmpA κατά 1, αλλά πριν προλάβει να
γράψει την αυξηµένη τιµή στην tally, διακόπτεται και αρχίζει να εκτελείται η διεργασία
Β. Με παρόµοιο τρόπο, η Β αποθηκεύει στην µεταβλητή tmpΒ την τιµή της tally (ας
θυµηθούµε ότι η τιµή της tally δεν έχει ακόµη αλλαχθεί και άρα εξακολουθεί να είναι 0).
Στη συνέχεια αυξάνει την tmpA κατά 1 και διακόπτεται. Η διεργασία Α γράφει την τιµή
της tmpA (δηλαδή το 1) στην tally και τερµατίζει. Τέλος, η διεργασία Β γράφει την τιµή
της tmpB (δηλαδή και πάλι το 1) στην tally και τερµατίζει και αυτή. Άρα, η tally έχει
τελικά την τιµή 1.
Το σενάριο που περιγράφτηκε πιο πάνω δεν είναι το µόνο που έχει σαν αποτέλεσµα την
τελική τιµή 1 για την tally. Τι θα συνέβαινε, π.χ., αν η διεργασία Β εκτελούσε την
τελευταία εντολή της πριν από την εκτέλεση της τελευταίας εντολής της Α; Τι θα
συνέβαινε αν η εκτέλεση των δύο διεργασιών γινόταν εντολή-εντολή εναλλάξ; □
Παράδειγµα 9
Έστω τώρα ότι ο κώδικας της increase() του Παραδείγµατος 7 είναι ο ακόλουθος:
void increase()
int tmp, i; /* τοπικές µεταβλητές */
begin
for i=0 to 5 do
begin
tmp = tally;
tmp = tmp+1;
tally = tmp;
end
end
Έχουµε δύο διεργασίες που εκτελούνται ταυτόχρονα στο σύστηµα µας, έστω ότι η πρώτη
είναι η Α και η δεύτερη η Β. Σε κάθε ανακύκλωση της for εντολής, κάθε διεργασία
εκτελεί µια ανάγνωση και µια εγγραφή στη διαµοιραζόµενη µεταβλητή tally, δηλαδή
συνολικά δύο προσβάσεις στην tally.
Ο κώδικας που εκτελεί η κάθε διεργασία έχει εποµένως την παρακάτω µορφή:
Κώδικας διεργασίας A Κώδικας διεργασίας B
for iΑ = 1 to 5 do for iB = 1 to 5 do
begin begin
65
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
66
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
δεν θα µάθει, αφού στην αµέσως επόµενη εντολή (εντολή 3) πανωγράφει την τιµή
της tally µε την τιµή 1. Πριν προχωρήσει στην εκτέλεση των υπολοίπων 4
ανακυκλώσεών της, εκτελείται ξανά η διεργασία B.
• Η διεργασία B αποµένει να εκτελέσει την τελευταία της ανακύκλωση. ∆ιαβάζει
εποµένως την tally (εντολή 1) στη µεταβλητή tmpB. Προφανώς, η τιµή που βρίσκει
στην tally είναι η τιµή 1 (την οποία η διεργασία A µόλις έγγραψε εκεί). Προσέξτε
πως όλη η δουλειά της διεργασίας B (4 αυξήσεις στην µεταβλητή tally) έχει τώρα
χαθεί. Πριν η διεργασία B εκτελέσει τις εντολές 2 και 3 της τελευταίας της
ανακύκλωσης, εκτελείται και πάλι η διεργασία Α.
• Η διεργασία Α εκτελείται µέχρι τέλους, αλλάζοντας την τιµή της tally σε 5.
• Αποµένει τέλος να εκτελεστούν οι εντολές 2 και 3 της διεργασίας B, προκειµένου και
αυτή να τερµατίσει. Προσέξτε ότι η µεταβλητή tmpB έχει την τιµή 1, ενώ η tally έχει
τιµή 5, κάτι που ποτέ η διεργασία 1 δεν θα µάθει, αφού θα αυξήσει την τιµή της tmpB
σε 2 (εντολή 2), στη συνέχεια θα πανωγράψει την τιµή της tally µε την τιµή 2 (εντολή
3) και θα τερµατίσει και αυτή.
• Η τελική τιµή της µεταβλητής tally είναι 2.
Το παραπάνω σενάριο περιγράφεται συνοπτικά (και µε λίγο συµπυκνωµένο τρόπο) στο
Σχήµα 16.
∆ιεργασία Α ∆ιεργασία Β
tmpΑ = tally;
/* Στην tmpA αποθηκεύεται η τιµή 0 */
tmpB = tally; tmpB = tmpB +1; tally = tmpB;
/* Η tally έχει τιµή 1 */
tmpB = tally; tmpB = tmpB +1; tally = tmpB;
/* Η tally έχει τιµή 2 */
tmpB = tally; tmpB = tmpB +1; tally = tmpB;
/* Η tally έχει τιµή 3 */
tmpB = tally; tmpB = tmpB +1; tally = tmpB;
/* Η tally έχει τιµή 4 */
tmpΑ = tmpΑ +1; tally = tmpΑ;
/* Η tally έχει τιµή 1 */
tmpB = tally;
/* Στην tmpB αποθηκεύεται η τιµή 1 */
67
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 16: Σενάριο που οδηγεί στην µικρότερη τιµή για τη διαµοιραζόµενη µεταβλητή tally.
Παρατήρηση
Το σετ εντολών:
tmp = tally;
tmp = tmp+1;
tally = tmp;
µπορεί να γραφτεί µε πιο συνοπτικό τρόπο ως εξής:
tally = tally + 1;
H tally είναι µια διαµοιραζόµενη µεταβλητή. Η εντολή “tally = tally +1;” προϋποθέτει
δύο προσπελάσεις στην tally. Για να γίνει αυτό, η τιµή της tally θα πρέπει να διαβαστεί
σε µια τοπική µεταβλητή, η τιµή της τοπικής µεταβλητής να αυξηθεί και η νέα τιµή να
γραφτεί στην tally. Εποµένως, κάθε διεργασία που πρέπει να εκτελέσει την εντολή “tally
= tally +1;” θα εκτελέσει, στην πράξη, το µπλοκ των τριών εντολών “tmp = tally; tmp =
tmp +1; tally = tmp;”.
Γενικότερα, ο αναγνώστης θα πρέπει να θυµάται, ότι δεν είναι δυνατόν να εκτελεστούν
σαν µια αδιαίρετη εντολή περισσότερες από µια προσπελάσεις σε µια διαµοιραζόµενη
µεταβλητή (π.χ., ανάγνωση και τροποποίηση της τιµής της). Έτσι, εντολές όπως η “tally =
tally + 1;” υποκρύπτουν την εκτέλεση περισσότερων της µιας εντολών (στην περίπτωσή
µας των τριών εντολών που παρουσιάστηκαν πιο πάνω). □
Άσκηση Αυτοαξιολόγησης 5
1. Θεωρήστε και πάλι τον κώδικα της increase() του Παραδείγµατος 9. Για κάθε τιµή
x, 2 < x ≤ 10, βρείτε ένα σενάριο στο οποίο η tally έχει τελική τιµή x.
2. Θεωρήστε και πάλι τον κώδικα της increase() του Παραδείγµατος 9. Για κάθε τιµή
του x, 1 < x ≤ 10, υπάρχει ένα µόνο σενάριο στο οποίο η tally έχει τελική τιµή x;
Προσπαθήστε να βρείτε όσο το δυνατό περισσότερα τέτοια σενάρια.
3. Έστω ότι ο κώδικας της increase() είναι αυτός που φαίνεται στη συνέχεια:
void increase()
int tmp, i; /* τοπικές µεταβλητές */
begin
for i=0 to 50 do
tally = tally + 1;
end
Ποια είναι η µικρότερη και ποια η µεγαλύτερη τελική τιµή που µπορεί να έχει η tally,
όταν ο παραπάνω κώδικας εκτελείται ταυτόχρονα από δύο διεργασίες; Ποιες είναι οι
δυνατές τελικές τιµές που µπορεί να έχει η tally;
4. Έστω ότι ο κώδικας της increase() του Παραδείγµατος 9 εκτελείται ταυτόχρονα από
3 και όχι από 2 διεργασίες. Ποια είναι η µικρότερη και ποια η µεγαλύτερη τελική
τιµή που µπορεί να έχει η tally σε αυτή την περίπτωση;
68
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Υπόδειξη: ∆είξτε ότι η µικρότερη τελική τιµή που µπορεί να έχει η tally είναι και πάλι 2. To σενάριο
που οδηγεί στην τιµή αυτή δεν είναι πολύ διαφορετικό από αυτό που περιγράφεται στο Σχήµα 16.
Είναι ώστοσο λίγο πιο πολύπλοκο, λόγω της ύπαρξης της τρίτης διεργασίας (της οποίας οι αυξήσεις
στην tally θα πρέπει επίσης να πανωγραφτούν).
5. Ποιες είναι οι δυνατές τελικές τιµές που µπορεί να έχει η tally στην περίπτωση του
ερωτήµατος 3;
6. Επαναλάβετε τα ερωτήµατα 4 και 5 για 4 διεργασίες, 5 διεργασίες και γενικότερα n
διεργασίες.
Υπόδειξη: ∆είξτε ότι ανεξάρτητα από τον αριθµό των διεργασιών που εµπλέκονται, η µικρότερη
τελική τιµή που µπορεί να έχει η tally είναι 2.
Στο παράδειγµά µας, δυο διεργασίες εκτελούνται ταυτόχρονα και χρησιµοποιούν τον ίδιο
πόρο συστήµατος, τον εκτυπωτή. Το ΛΣ οφείλει να συντονίσει τις διεργασίες που
επιχειρούν να χρησιµοποιήσουν τον ίδιο πόρο, ώστε η µία να περιµένει την άλλη.
Ο πολύ προσεκτικός αναγνώστης θα πρέπει να αντιδράσει στο παραπάνω (λίγο
απλουστευτικό) σενάριο (το οποίο περιγράφεται µόνο για λόγους κατανόησης της
ανάγκης συγχρονισµού). Όπως έχουµε ήδη αναφέρει, οι διεργασίες του χρήστη δεν έχουν
από µόνες τους πρόσβαση σε συσκευές Ε/Ε, όπως ο εκτυπωτής, και άρα το παραπάνω
σενάριο µπερδεµένης εκτύπωσης δεν θα µπορούσε να συµβεί, αφού το ΛΣ είναι εκείνο
69
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
που θα έχει τον έλεγχο και των δύο εκτυπώσεων. Επίσης, συνήθως χρησιµοποιείται η
τεχνική της παροχέτευσης για τη διαχείριση του εκτυπωτή. Παρόλα αυτά, η ανάγκη για
συγχρονισµό εξακολουθεί να υφίσταται. Θεωρήστε πως οι διεργασίες του χρήστη (αντί
να έχουν άµεση πρόσβαση στον εκτυπωτή) τοποθετούν πληροφορίες για το αρχείο που
θέλουν να εκτυπώσουν σε µια ουρά, την οποία το ΛΣ εξερευνά στη συνέχεια,
προκειµένου να διεκπεραιώσει την πραγµάτωση των εκτυπώσεων. Έστω ότι η ουρά
υλοποιείται µε πίνακα, όπως φαίνεται στο Σχήµα 17. Η µεταβλητή front δείχνει τη θέση
του πίνακα στην οποία αναγράφονται πληροφορίες για το επόµενο προς εκτύπωση
αρχείο, ενώ η µεταβλητή back δείχνει τη θέση του πίνακα στην οποία θα πρέπει να
καταγραφούν πληροφορίες για το επόµενο αρχείο που θα ζητηθεί να εκτυπωθεί.
.
.
.
front file1.txt 8
file2.txt 9
back 10
.
.
.
Σχήµα 17: Ουρά ετεροχρονισµένης εκτύπωσης.
Θεωρείστε και πάλι ότι δύο διεργασίες Α και Β θέλουν να εκτυπώσουν από ένα αρχείο η
κάθε µια. Η Α διαβάζει την κοινή µεταβλητή back για να βρει σε ποια θέση του πίνακα
θα πρέπει να τοποθετήσει πληροφορίες για το αρχείο που θέλει να εκτυπώσει. Έστω ότι η
µεταβλητή back έχει την τιµή 10. Στη συνέχεια, η Β διαβάζει και εκείνη την back και την
βρίσκει επίσης 10 (αφού η Α δεν πρόλαβε να αλλάξει την τιµή της σε 11). Στη συνέχεια,
η Α τοποθετεί πληροφορίες για το αρχείο της στη θέση 10 του πίνακα και αλλάζει την
τιµή της back σε 11. Το ίδιο ακριβώς θα κάνει όµως και η Β, πανωγράφοντας τις
πληροφορίες που έγραψε η Α στη θέση 10. Το ΛΣ δεν θα παρατηρήσει κανένα
πρόβληµα, αλλά η Α δεν θα πάρει ποτέ την εκτύπωσή της. Για να αποφευχθούν τέτοια
ανεπιθύµητα σενάρια, απαιτείται να υπάρχει συντονισµός των διεργασιών κατά την
πρόσβαση στις κοινές µεταβλητές front και back.
Ας δούµε τώρα ένα άλλο παράδειγµα µε λίγη περισσότερη λεπτοµέρεια. Θεωρείστε έναν
τραπεζικό λογαριασµό που περιέχει 500€ και έστω ότι υπάρχουν δυο κάρτες ΑΤΜ
συνδεδεµένες µε αυτό το λογαριασµό, τις οποίες διαχειρίζονται δύο διαφορετικά άτοµα
(π.χ., ένας χρήστης και η αδελφή του).
Κάθε ΑΤΜ είναι συνδεδεµένο µε έναν κεντρικό υπολογιστή της τράπεζας, ο οποίος
ελέγχει τις κινήσεις των τραπεζικών λογαριασµών. Επειδή ο ίδιος υπολογιστής
εξυπηρετεί µερικές εκατοντάδες ΑΤΜ, χρησιµοποιεί αναγκαστικά την τεχνική της
δροµολόγησης, ώστε να εξυπηρετεί τις διεργασίες που ζητούνται να εκτελεστούν
ταυτόχρονα µέσω των διαφορετικών ΑΤΜ.
Έστω ότι η ρουτίνα που εκτελείται κατά τη λειτουργία της ανάληψης από τον τραπεζικό
λογαριασµό είναι η ακόλουθη:
70
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Το σενάριο αυτό θα οδηγούσε σε ανάληψη συνολικού ποσού 1000€ από ένα λογαριασµό
που περιέχει µόνο 500€ και είναι ένας καλός τρόπος µε τον οποίο ο χρήστης και η
αδελφή του µπορούν να κλέψουν την τράπεζα! Όλα αυτά φυσικά θα συµβούν µόνο στην
περίπτωση που ο προγραµµατιστής του υπολογιστή της τράπεζας δεν γνωρίζει καλά το
αντικείµενο των λειτουργικών συστηµάτων και δεν έχει µεριµνήσει για το συντονισµό
71
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
των διεργασιών που διαχειρίζονται τον κοινό πόρο (που στο παράδειγµα αυτό δεν είναι
άλλος από την διαµοιραζόµενη µεταβλητή balance).
Στο παραπάνω παράδειγµα υποθέσαµε ότι οι εντολές για κάθε ΑΤΜ εκτελούνται µια-µια
εναλλάξ. Υπάρχουν ωστόσο και άλλα σενάρια που οδηγούν στην κλοπή της τράπεζας µε
τον τρόπο που περιγράφτηκε πιο πάνω. Συνίσταται ισχυρά στον αναγνώστη να
προσπαθήσει να βρει µερικές ακόµη τέτοιες εκτελέσεις.
ATM-1 (διεργασία χρήστη) ATM-2 (διεργασία αδελφής χρήστη)
amount = 500
amount = 500
balance = 500
balance = 500
Χρόνος Έλεγχος if (είναι TRUE)
Έλεγχος if (είναι TRUE)
balance = balance – amount
balance = balance – amount
print “Το νέο σας ποσό είναι”
print balance
print “Το νέο σας ποσό είναι”
print balance
Σχήµα 18: Σενάριο που οδηγεί σε ανάληψη 1000€ από ένα λογαριασµό που περιέχει µόνο 500€.
Άσκηση Αυτοαξιολόγησης 6
Έστω ότι ένας τραπεζικός λογαριασµός περιέχει 500€ και έστω ότι υπάρχουν τρεις
κάρτες ΑΤΜ συνδεδεµένες µε αυτόν το λογαριασµό, τις οποίες διαχειρίζονται τρία
διαφορετικά άτοµα (π.χ., ένας χρήστης και οι δυο γονείς του).
Ας θεωρήσουµε ότι ο χρήστης θέλει να κάνει µια αγορά των 1500€ και, επειδή τα 500€
του λογαριασµού δεν επαρκούν, έχει συνεννοηθεί µε τους γονείς του να καταθέσουν τα
υπόλοιπα 1000€ που χρειάζεται. Οι γονείς του έχουν συνεννοηθεί µεταξύ τους να
καταθέσουν ο κάθε ένας από 500€.
Κάθε ΑΤΜ είναι συνδεδεµένο µε έναν κεντρικό υπολογιστή της τράπεζας, ο οποίος
ελέγχει τις κινήσεις των τραπεζικών λογαριασµών. Επειδή ο ίδιος υπολογιστής
εξυπηρετεί µερικές εκατοντάδες ΑΤΜ, χρησιµοποιεί αναγκαστικά την τεχνική της
δροµολόγησης ώστε να εξυπηρετεί τις διεργασίες που ζητούνται να εκτελεστούν
ταυτόχρονα µέσω των διαφορετικών ΑΤΜ.
Έστω ότι η ρουτίνα που εκτελείται κατά τη λειτουργία της κατάθεσης από τον τραπεζικό
λογαριασµό είναι η ακόλουθη:
shared int balance;
boolean deposit(int amount)
begin
balance = balance + amount;
print “Το νέο σας ποσό είναι”;
print balance;
return TRUE;
end
72
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Θεωρείστε ότι και οι δύο γονείς φθάνουν σε διαφορετικά υποκαταστήµατα της τράπεζας
ταυτόχρονα. Περιγράψτε ένα «ατυχές» σενάριο στο οποίο, παρότι και οι δύο γονείς θα
καταθέσουν από 500€ ο καθένας, ο λογαριασµός δεν θα έχει περισσότερα από 1000€ στο
τέλος (αντί για 1500€ που θα έπρεπε να περιέχει). Αυτός θα είναι και ένας καλός τρόπος
για να κλέβει η τράπεζα τους χρήστες της!
73
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
µπορούν να επηρεάζουν την απόφαση του ποια διεργασία θα εισέλθει τελικά στο
κρίσιµο τµήµα της, και η επιλογή αυτή δεν µπορεί να αναβάλλεται επ’ αόριστον
(δηλαδή, τελικά θα πρέπει κάποια διεργασία να εισέλθει στο κρίσιµο τµήµα της).
Η πρώτη συνθήκη υποδηλώνει πως δύο διεργασίες δεν µπορούν να βρίσκονται ποτέ
ταυτόχρονα στο κρίσιµο τµήµα τους. Έτσι, αν µια διεργασία βρίσκεται στο κρίσιµο
τµήµα της, οι υπόλοιπες θα πρέπει είτε να εκτελούν το µη-κρίσιµο τµήµα τους ή να
εκτελούν επαναληπτικά κάποιο µέρος του κώδικα εισόδου (π.χ., κάποια εντολή
ανακύκλωσης της οποίας η συνθήκη θα αλλάξει µόνο όταν η διεργασία που βρίσκεται
στο κρίσιµο τµήµα εξέλθει από αυτό, εκτελώντας τον κώδικα εξόδου). Μια πιο
προσεκτική µελέτη της δεύτερης συνθήκης µας οδηγεί στο συµπέρασµα ότι αν µια
διεργασία εκτελεί το µη-κρίσιµο τµήµα της δεν επιτρέπεται να µπλοκάρει άλλες
διεργασίες από το να εισέλθουν στο κρίσιµο τµήµα τους. Επίσης, η δεύτερη συνθήκη
υποδηλώνει ότι αν µια ή περισσότερες διεργασίες θέλουν να εισέλθουν στο κρίσιµο
τµήµα, τουλάχιστον µία θα πρέπει τελικά να το καταφέρει.
Θεωρούµε ότι η εκτέλεση του κρίσιµου τµήµατος απαιτεί πεπερασµένο χρόνο για κάθε
διεργασία. Το ίδιο απαιτείται να ισχύει για τον κώδικα εξόδου.
Μια λύση που ικανοποιεί τις παραπάνω συνθήκες θεωρείται σωστή (αν και ίσως όχι
δίκαιη) λύση στο πρόβληµα του αµοιβαίου αποκλεισµού. Προφανώς, οι συνθήκες αυτές
θα πρέπει να ισχύουν στην γενική περίπτωση που δεν έχει γίνει κανενός είδους υπόθεση
για το πως δροµολογούνται οι διεργασίες, µε τι ταχύτητα εκτελείται η κάθε µια, κ.ο.κ.
Μια λύση στο πρόβληµα του αµοιβαίου αποκλεισµού µπορεί επιπλέον να ικανοποιεί µια
από τις ακόλουθες συνθήκες δικαιοσύνης:
1. Αποφυγή Παρατεταµένης Στέρησης. ∆εν επιτρέπεται µια διεργασία να περιµένει
επ’ αόριστον για να εισέλθει στην κρίσιµη περιοχή της.
2. Άνω Φραγµένη Καθυστέρηση. ∆εδοµένου ότι µια διεργασία Α ενδιαφέρεται να
εισέλθει στο κρίσιµο τµήµα της, θα πρέπει να υπάρχει ένα άνω όριο στον αριθµό των
φορών που κάθε άλλη διεργασία µπορεί να εισέλθει στο κρίσιµο τµήµα της πριν η Α
τελικά καταφέρει να εισέλθει στο κρίσιµο τµήµα της.
Συνήθως οι διεργασίες εκτελούν επαναληπτικά ένα κρίσιµο τµήµα ακολουθούµενο από
ένα µη-κρίσιµο τµήµα (ή το αντίστροφο). Έτσι, τις περισσότερες φορές η γενική µορφή
του κώδικα των διεργασιών θα έχει κάποια από τις µορφές του Σχήµατος 19 (στη
συνέχεια, συνήθως χρησιµοποιείται η 1η από αυτές).
1η Εναλλακτική 2η Εναλλακτική 3η Εναλλακτική
repeat repeat while (TRUE)
begin begin
begin
<κώδικας εισόδου>; <κώδικας εισόδου>;
<κώδικας εισόδου>;
<κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
<κρίσιµο τµήµα>;
<κώδικας εξόδου>; <κώδικας εξόδου>;
<κώδικας εξόδου>;
<µη κρίσιµο τµήµα>; <µη κρίσιµο τµήµα>;
<µη κρίσιµο τµήµα>;
end end
end
forever; until FALSE;
74
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Θα πρέπει να γίνει σαφές, ότι η ατοµική εκτέλεση του κρίσιµου τµήµατος µιας
διεργασίας δεν συνεπάγεται σε καµία περίπτωση ότι η διεργασία δεν θα διακοπεί ενόσω
εκτελεί το κρίσιµο τµήµα της. Τέτοιες διακοπές µπορούν φυσικά να συµβαίνουν.
Ωστόσο, όλες οι άλλες διεργασίες που ενδιαφέρονται να µπουν στο κρίσιµο τµήµα τους
θα πρέπει να είναι µπλοκαρισµένες στον κώδικα εισόδου, δηλαδή να εκτελούν κάποια
εντολή ανακύκλωσης που είναι σίγουρο πως θα τερµατίσει µόνο αν η διεργασία που
βρίσκεται στο κρίσιµο τµήµα εξέλθει από αυτό. Για παράδειγµα, έστω ότι δύο διεργασίες
Α και Β ενδιαφέρονται να εισέλθουν στο κρίσιµο τµήµα τους. Η εκτέλεση που
παρουσιάζεται στο Σχήµα 20 θα µπορούσε να συµβεί.
∆ιεργασία Α ∆ιεργασία Β
εκτέλεση µέρους του κώδικα εισόδου;
εκτέλεση κώδικα εισόδου;
εκτέλεση µέρους του κρίσιµου τµήµατος;
εκτέλεση µέρους του κώδικα εισόδου;
/* Ο κώδικας εισόδου θα πρέπει να έχει τέτοια
µορφή που να µην είναι δυνατό στην Α να
εισέλθει στο κρίσιµο τµήµα όσο βρίσκεται εκεί
η Β*/
εκτέλεση µέρους του κρίσιµου τµήµατος;
Σχήµα 20: ∆ιακοπές ενόσω µια διεργασία εκτελεί το κρίσιµο τµήµα της.
75
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 22: Λύση του προβλήµατος αµοιβαίου αποκλεισµού µε χρήση της εντολής Test&Set().
Η µεταβλητή lock έχει αρχικά την τιµή FALSE, ενώ το υλικό εγγυάται ότι η Test&Set()
θα εκτελεστεί ατοµικά. Η πρώτη διεργασία, έστω Α, που θα εκτελέσει την Test&Set() θα
πάρει ως αποτέλεσµα FALSE, ενώ η τιµή της διαµοιραζόµενης µεταβλητής lock θα
αλλάξει σε TRUE. Η τιµή FALSE θα αποθηκευτεί στην τοπική µεταβλητή tmp της Α και
η συνθήκη της while για την Α θα αποτιµηθεί σε FALSE. Η Α θα εισέλθει εποµένως στο
κρίσιµο τµήµα της. Η Test&Set() επιστρέφει TRUE σε κάθε άλλη διεργασία που την
καλεί (αφού µετά την περάτωση της πρώτης κλήσης της, που έγινε από την Α, η τιµή της
lock έχει αλλάξει σε TRUE). Έτσι, οι υπόλοιπες διεργασίες θα µπλοκάρουν στη while
του κώδικα εισόδου. Όταν η Α βγει από το κρίσιµο τµήµα της, θα αλλάξει την τιµή της
76
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
lock σε FALSE για να δώσει τη δυνατότητα σε κάποια από τις διεργασίες που
ενδιαφέρονται να εισέλθουν στο κρίσιµο τµήµα τους να το πραγµατοποιήσει.
Σχήµα 23: Κώδικας που εκτελείται στην πράξη από δύο διεργασίες 0 και 1 όταν η Test&Set()
χρησιµοποιείται για επίτευξη αµοιβαίου αποκλεισµού.
Στο Σχήµα 23, έχει αποδοθεί διαφορετικό όνοµα στην τοπική µεταβλητή tmp κάθε
διεργασίας, προκειµένου να γίνει σαφές ότι η tmp δεν αποτελεί διαµοιραζόµενη
µεταβλητή. Αντίθετα, κάθε διεργασία χρησιµοποιεί το δικό της αντίγραφο της tmp. Η
εκτέλεση που παραβιάζει τη συνθήκη του αµοιβαίου αποκλεισµού παρουσιάζεται στο
Σχήµα 24.
∆ιεργασία 0 ∆ιεργασία 1
tmp0 = lock;
tmp1 = lock;
lock = 1;
Έλεγχος while /* είναι FALSE */
<κρίσιµο τµήµα>;
lock = 1;
Έλεγχος while /* είναι FALSE */
<κρίσιµο τµήµα>;
77
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 24: Εκτέλεση που παραβιάζει τη συνθήκη αµοιβαίου αποκλεισµού αν η Test&Set() δεν εκτελείται
ατοµικά.
Η διεργασία 0 αποθηκεύει την αρχική τιµή του lock (δηλαδή την τιµή FALSE) στην
tmp0. Η διεργασία 0 διακόπτεται στο σηµείο αυτό και δροµολογείται η διεργασία 1.
Αφού υποθέσαµε ότι η Test&Set() δεν εκτελείται ατοµικά, η διεργασία 1 µπορεί να
ξεκινήσει και αυτή να εκτελεί την Test&Set() (προσέξτε ότι κάτι τέτοιο δεν ήταν
επιτρεπτό όταν ήταν εγγυηµένο πως η Test&Set() θα εκτελεστεί ατοµικά). Αποθηκεύει
και αυτή την τιµή FALSE στην µεταβλητή tmp1, αλλάζει την τιµή του lock σε FALSE
και εισέρχεται στο κρίσιµο τµήµα της. Στο σηµείο αυτό, η διεργασία 1 διακόπτεται και
συνεχίζει να εκτελείται η διεργασία 0. Η διεργασία 0 αλλάζει µε τη σειρά της το lock σε
TRUE, αποτιµά τη συνθήκη της while σε FALSE (αφού η τοπική µεταβλητή της tmp0
έχει την τιµή FALSE) και εισέρχεται και αυτή στο κρίσιµο τµήµα της. □
78
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Άσκηση Αυτοαξιολόγησης 9
Ικανοποιεί η λύση στο πρόβληµα του αµοιβαίου αποκλεισµού που παρουσιάζεται στο
Σχήµα 15 κάποια από τις συνθήκες δικαιοσύνης;
79
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Λύση
Θα εξετάσουµε πρώτα αν η λύση µπορεί να οδηγήσει σε παρατεταµένη στέρηση. Για να
αποδείξουµε ότι µια λύση µπορεί να οδηγήσει σε παρατεταµένη στέρηση θα πρέπει να
βρούµε σενάριο στο οποίο κάποιες διεργασίες εισέρχονται συνεχώς στο κρίσιµο τµήµα,
ενώ κάποια άλλη παρότι επιθυµεί να εισέλθει στο κρίσιµο τµήµα της θα παραµείνει για
πάντα µπλοκαρισµένη στον κώδικα εισόδου. Ας δοκιµάσουµε να “µαγειρέψουµε” ένα
τέτοιο σενάριο για την σχετικά απλή περίπτωση δύο διεργασιών. Το σενάριο
περιγράφεται στο Σχήµα 27.
∆ιεργασία 0 ∆ιεργασία 1
tmp = Test&Set(lock);
Έλεγχος συνθήκης while /* είναι FALSE */
tmp = Test&Set(lock);
Έλεγχος συνθήκης while /* είναι TRUE */
<κρίσιµο τµήµα>;
lock = FALSE;
<µη-κρίσιµο τµήµα>;
tmp = Test&Set(lock);
Έλεγχος συνθήκης while /* είναι FALSE */
Έλεγχος συνθήκης while /* είναι TRUE */
Έλεγχος συνθήκης while /* είναι TRUE */
Έλεγχος συνθήκης while /* είναι TRUE */
<κρίσιµο τµήµα>;
lock = FALSE;
<µη-κρίσιµο τµήµα>;
tmp = Test&Set(lock);
Έλεγχος συνθήκης while /* είναι FALSE */
Έλεγχος συνθήκης while /* είναι TRUE */
. Έλεγχος συνθήκης while /* είναι TRUE */
. .
. .
.
Σχήµα 27: Εκτέλεση που οδηγεί σε παρατεταµένη στέρηση για τη διεργασία 1 στη λύση που χρησιµοποιεί
την Test&Set().
Στην εκτέλεση του Σχήµατος 27 θεωρούµε ότι ξεκινά να εκτελείται πρώτη η διεργασία 0.
Εκτελεί την Test&Set() και εισέρχεται στο κρίσιµο τµήµα της. Η διεργασία 1
δροµολογείται και προσπαθεί αποτυχηµένα να εισέλθει στο κρίσιµο τµήµα της. Η
διεργασία 1 µπλοκάρεται στη while του κώδικα εισόδου. Όταν η διεργασία 0 εξέλθει
από το κρίσιµο τµήµα της, η τιµή της µεταβλητής lock αλλάζει σε FALSE. Πριν όµως
προλάβει να δροµολογηθεί η 1, η 0 προλαβαίνει και τελειώνει το µη-κρίσιµο τµήµα της,
καλεί εκ νέου την Test&Set() και αλλάζει και πάλι την τιµή της lock σε TRUE. H
διεργασία 1 δροµολογείται και πάλι, χωρίς ωστόσο να καταφέρει και αυτή τη φορά να
εισέλθει στο κρίσιµο τµήµα της. Το σενάριο αυτό επαναλαµβάνεται επ’ άπειρο. Η
διεργασία 0 θα εισέλθει στο κρίσιµο τµήµα της άπειρες φορές, αλλά η διεργασία 1 δεν θα
καταφέρει ποτέ να εκτελέσει το κρίσιµο τµήµα της. Η λύση του Σχήµατος 15 δεν είναι
εποµένως δίκαιη αφού οδηγεί σε παρατεταµένη στέρηση.
80
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Αξίζει να παρατηρήσουµε ότι αν µια λύση ικανοποιεί τη συνθήκη της άνω φραγµένης
καθυστέρησης, τότε ικανοποιεί και τη συνθήκη της αποφυγής παρατεταµένης στέρησης.
Άρα, αν µια λύση δεν ικανοποιεί τη συνθήκη της αποφυγής παρατεταµένης στέρησης,
τότε δεν µπορεί να ικανοποιεί και τη συνθήκη της άνω φραγµένης καθυστέρησης. Αρκεί
εποµένως να αποδείξουµε ότι µια λύση οδηγεί σε παρατεταµένη στέρηση για να
συµπεράνουµε ότι καµία από τις δύο συνθήκες δικαιοσύνης δεν ικανοποιείται. □
Άσκηση Αυτοαξιολόγησης 11
Θεωρείστε ότι το υλικό εγγυάται ότι η εντολή Swap(), που περιγράφεται στο Σχήµα 29
εκτελείται ατοµικά. Λύστε το πρόβληµα του αµοιβαίου αποκλεισµού χρησιµοποιώντας
τη Swap().
int Swap(int a, int b)
int tmp; /* τοπική µεταβλητή */
begin
tmp = a;
a = b;
b = tmp;
return(tmp);
end
81
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Υπόδειξη: Χρησιµοποιείστε µια διαµοιραζόµενη µεταβλητή lock ως πρώτη παράµετρο στη Swap() και µια
τοπική µεταβλητή στην οποία θα αποθηκεύετε πάντα µια σταθερή τιµή πριν καλέσετε τη Swap(), ως
δεύτερη παράµετρο.
Άσκηση Αυτοαξιολόγησης 12
Θεωρείστε ότι το υλικό εγγυάται ότι η εντολή RMW() (Read-Modify-Write()), που
περιγράφεται στο Σχήµα 30 εκτελείται ατοµικά. Λύστε το πρόβληµα του αµοιβαίου
αποκλεισµού χρησιµοποιώντας τη RMW().
int RMW(int a, function f)
int tmp; /* τοπική µεταβλητή */
begin
tmp = a;
a = f(a);
return(tmp);
end
Η RMW() παίρνει δύο παραµέτρους, µια µεταβλητή a και µια συνάρτηση f(). Αλλάζει
την τιµή της a σύµφωνα µε την f (δηλαδή εφαρµόζει την f στην a και αποθηκεύει το
αποτέλεσµα και πάλι στην a), ενώ επιστρέφει την αρχική τιµή της a.
Ικανοποιεί η λύση που παρουσιάσατε κάποια από τις συνθήκες δικαιοσύνης;
Υπόδειξη: Επιλέξτε την κατάλληλη συνάρτηση f().
82
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 31: Πρώτη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού.
Κάθε διεργασία εξετάζει την τιµή της κοινής µεταβλητής in, η οποία υποδηλώνει αν
κάποια διεργασία επιθυµεί να εισέλθει στο κρίσιµο τµήµα της. Αν η τιµή της in είναι
FALSE, η διεργασία την αλλάζει σε TRUE και εισέρχεται στο κρίσιµο τµήµα της. Αν
ωστόσο η τιµή της in είναι TRUE, η διεργασία περιµένει στη while. Ακριβέστερα, µέχρι
η τιµή της in να γίνει FALSE, η διεργασία εκτελεί επαναληπτικά την εντολή noop που
δεν κάνει καµία χρήσιµη ενέργεια (έχει δηλαδή µπλοκάρει στη while).
Είναι σωστή η παραπάνω λύση; Μπορούµε να βρούµε ένα κακό σενάριο στο οποίο και οι
δύο διεργασίες βρίσκονται ταυτόχρονα στο κρίσιµο τµήµα τους; Θα ήταν καλό αν ο
αναγνώστης επενδύσει λίγο χρόνο στην ανεύρεση τέτοιου σεναρίου, πριν διαβάσει το
σενάριο που περιγράφεται στη συνέχεια.
Στο κακό σενάριο, η διεργασία 0 ελέγχει τη συνθήκη της while και βρίσκει την τιµή της
in να είναι FALSE. Αποφασίζει λοιπόν πως η διεργασία 1 δεν προτίθεται να εισέλθει στο
κρίσιµο τµήµα της και άρα πως αυτή µπορεί να αλλάξει την τιµή της in σε TRUE και να
εισέλθει στο κρίσιµο τµήµα της. Πριν προλάβει όµως να κάνει οτιδήποτε, διακόπτεται
και δροµολογείται η 1. Η 1 ελέγχει τη συνθήκη της while, βρίσκει και αυτή την τιµή της
in να είναι FALSE, συµπεραίνει ότι η 0 δεν προτίθεται να εισέλθει στο κρίσιµο τµήµα
της και εποµένως ότι µπορεί να αλλάξει την τιµή της in σε TRUE και να εισέλθει στο
83
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
κρίσιµο τµήµα της. Η συνέχεια είναι εύκολο να προβλεφθεί. Και οι δύο διεργασίες
θέτουν την τιµή της in σε TRUE και εισέρχονται στο κρίσιµο τµήµα τους. Το παραπάνω
σενάριο περιγράφεται πιο αναλυτικά στο Σχήµα 32.
∆ιεργασία Α ∆ιεργασία Β
Έλεγχος συνθήκης while; /* είναι FALSE */
Έλεγχος συνθήκης while; /*είναι FALSE */
free = TRUE;
<κρίσιµο τµήµα>;
free = TRUE;
<κρίσιµο τµήµα>;
Σχήµα 32: Σενάριο που καταστρατηγεί τη συνθήκη του αµοιβαίου αποκλεισµού για την 1η προτεινόµενη
λύση.
Εφόσον υπάρχει σενάριο που µπορεί να οδηγήσει τις δύο διεργασίες να εκτελούν το
κρίσιµο τµήµα την ίδια χρονική στιγµή, η συνθήκη του αµοιβαίου αποκλεισµού δεν
ισχύει για την προτεινόµενη λύση του Σχήµατος 31 και άρα η λύση αυτή δεν είναι
σωστή.
Αξίζει να αναρωτηθούµε ποιο είναι το πρόβληµα στη λύση αυτή και να προσπαθήσουµε
να το διορθώσουµε. Το πρόβληµα φαίνεται να είναι πως κάθε διεργασία πρώτα ελέγχει
αν η in είναι FALSE και µετά αλλάζει την τιµή της in σε TRUE. Με άλλα λόγια, κάθε
διεργασία πρώτα ελέγχει αν προτίθεται η άλλη διεργασία να µπει στο κρίσιµο τµήµα της
(έλεγχος συνθήκης while) και στη συνέχεια εκφράζει τη δική της πρόθεση να µπει στο
κρίσιµο τµήµα (αλλάζοντας την τιµή της in σε TRUE). Ως δεύτερη προτεινόµενη λύση,
ας δοκιµάσουµε εποµένως να κάνουµε ακριβώς το αντίθετο. Ο κώδικας των δύο
διεργασιών φαίνεται στο Σχήµα 33.
Κοινές µεταβλητές
shared boolean flag[2]; /* αρχικά, FALSE */
Κώδικας ∆ιεργασίας 0 Κώδικας ∆ιεργασίας 1
repeat repeat
begin begin
flag[0] = TRUE; flag[1] = TRUE;
while (flag[1] == TRUE) do noop; while (flag[0] == TRUE) do noop;
<κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
flag[0] = FALSE; flag[1] = FALSE;
<µη-κρίσιµο τµήµα>; <µη-κρίσιµο τµήµα>;
end end
forever; forever;
Σχήµα 33: ∆εύτερη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού.
Στη λύση του Σχήµατος 33, χρησιµοποιούνται δύο κοινές µεταβλητές, µία για κάθε
διεργασία. Η µεταβλητή flag[i] χρησιµοποιείται από τη διεργασία i για να δηλώσει την
πρόθεση της να εισέλθει στο κρίσιµο τµήµα. Αρχικά, η διεργασία i θέτει την flag[i] στην
τιµή TRUE για να υποδηλώσει ότι θέλει να εισέλθει στο κρίσιµο τµήµα της. Στη
84
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 34: Σενάριο που καταστρατηγεί τη συνθήκη της προόδου για τη 2η προτεινόµενη λύση.
Αφού εκτελεστούν οι λειτουργίες που περιγράφονται στο Σχήµα 34, οι δύο διεργασίες θα
παραµείνουν µπλοκαρισµένες στην while και καµία δεν θα εισέλθει τελικά στο κρίσιµο
τµήµα της. Το σύστηµα βρίσκεται σε αδιέξοδο αφού η διεργασία 0 περιµένει από τη
διεργασία 1 να αλλάξει την τιµή της µεταβλητής flag[1] σε FALSE, ενώ αντίστοιχα η
διεργασία 1 περιµένει από τη διεργασία 0 να αλλάξει την τιµή της flag[0] σε FALSE.
Άρα και οι δύο διεργασίες θα περιµένουν για πάντα. Εποµένως, το σύστηµα δεν
προοδεύει (καταστρατηγείται η συνθήκη της προόδου), το οποίο σηµαίνει ότι και αυτή η
λύση δεν είναι σωστή.
Ας δούµε τώρα µια ακόµη προτεινόµενη λύση που περιγράφεται στο Σχήµα 35.
Κοινές Μεταβλητές
shared int turn; /* αρχικά, 0 ή 1, δεν παίζει ρόλο */
Κώδικας ∆ιεργασίας 0 Κώδικας ∆ιεργασίας 1
repeat repeat
begin begin
while (turn == 1) do noop; while (turn == 0) do noop;
85
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 35: Τρίτη προτεινόµενη λύση µε χρήση λογισµικού (λύση της αυστηρής εναλλαγής) στο πρόβληµα
του αµοιβαίου αποκλεισµού.
86
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 36: Σενάριο που καταστρατηγεί τη συνθήκη της προόδου για την 3η προτεινόµενη λύση.
Στο σενάριο του Σχήµατος 36, η διεργασία 0 έχει ένα µεγάλο µη-κρίσιµο τµήµα, ενώ
αντίθετα το µη-κρίσιµο τµήµα της 1 είναι πολύ µικρό. Η διεργασία 1 ξεκινά πρώτη.
Εισέρχεται και εξέρχεται από το κρίσιµο τµήµα της. Στη συνέχεια, η διεργασία 0
εισέρχεται και εξέρχεται από το κρίσιµο τµήµα της. Είναι και πάλι η σειρά της 1 να
εισέλθει και να εξέλθει από το κρίσιµο τµήµα της. Όµως, η 1 δεν ενδιαφέρεται άµεσα να
ξανα-εισέλθει στο κρίσιµο τµήµα της γιατί εκτελεί ένα πολύ µεγάλο µη-κρίσιµο τµήµα.
Η 0 που ενδιαφέρεται να ξανα-εισέλθει, πρέπει να περιµένει να εισέλθει και πάλι η 1 πριν
ξαναέρθει η σειρά της. Η 1 παρότι εκτελεί το µη-κρίσιµο τµήµα της αποτρέπει την
είσοδο της 0 στο κρίσιµο τµήµα της, το οποίο καταστρατηγεί τη συνθήκη της προόδου.
Εποµένως, και η λύση αυτή δεν είναι σωστή.
Άσκηση Αυτοαξιολόγησης 14
Σχολιάστε την ορθότητα της λύσης του προβλήµατος του αµοιβαίου αποκλεισµού για
δύο διεργασίες που περιγράφεται στο Σχήµα 37.
Κοινές Μεταβλητές
shared boolean flag[2]; /* αρχικά, TRUE */
Κώδικας ∆ιεργασίας 0 Κώδικας ∆ιεργασίας 1
repeat repeat
begin begin
while (flag[1] == TRUE) do noop; while (flag[0] == TRUE) do noop;
flag[0] = TRUE; flag[1] = TRUE;
<κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
flag[0] = FALSE; flag[1] = FALSE;
<µη-κρίσιµο τµήµα>; <µη-κρίσιµο τµήµα>;
end end
forever; forever;
Σχήµα 37: Τέταρτη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού
για δύο διεργασίες.
Άσκηση Αυτοαξιολόγησης 15
Η Ελένη και ο ∆ήµος είναι γείτονες και µοιράζονται µια αυλή. Η Ελένη έχει µια γάτα,
ενώ ο ∆ήµος έχει ένα σκύλο. Τόσο στη γάτα όσο και στο σκύλο αρέσει να παίζουν στην
αυλή. Ωστόσο, όσες φορές έχει τύχει τα δύο κατοικίδια να συναντηθούν στην αυλή, έχει
γίνει µεγάλος καυγάς. Έτσι, η Ελένη και ο ∆ήµος αποφάσισαν πως δεν πρέπει να
αφήνουν τα κατοικίδια να βρίσκονται την ίδια ώρα στην αυλή. Άρα, θα πρέπει µε κάποιο
τρόπο να συντονίζονται πριν αφήσουν τα κατοικίδια τους να βγαίνουν στην αυλή.
87
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Το ερώτηµα είναι πως θα το πετύχουν αυτό; Η αυλή είναι µεγάλη και δεν είναι δυνατόν
για την Ελένη να ελέγξει αν ο σκύλος του ∆ήµου είναι στην αυλή ρίχνοντας µια µατιά
από το παράθυρο του σπιτιού της. Θα µπορούσε βέβαια να πεταχτεί στο σπίτι του ∆ήµου
και να τον ρωτήσει, αλλά αυτό δεν είναι πολύ βολικό ιδιαίτερα τις πολύ βροχερές µέρες.
Θα µπορούσε επίσης να ανοίξει το παράθυρο και να του φωνάξει, αλλά ο ∆ήµος µπορεί
να µην ακούσει. Τέλος, θα µπορούσε να τον πάρει τηλέφωνο, αλλά ούτε και αυτή η λύση
είναι εγγυηµένο πως θα είναι πάντοτε εξυπηρετική, αφού µπορεί ο ∆ήµος να µην
βρίσκεται στο σπίτι, ή να µην ακούει το χτύπο του τηλεφώνου επειδή βρίσκεται στο
µπάνιο, κλπ.
Αφού ξοδεύουν µερικές ώρες να σκέπτονται ποιος είναι ο πιο σωστός τρόπος,
αποφασίζουν να δοκιµάσουν τον ακόλουθο αλγόριθµο. Καθένας τοποθετεί στο σπίτι του
ένα κοντάρι, όπου µπορεί να ανεβάζει µια σηµαία. Το κοντάρι κάθε ενός έχει
τοποθετηθεί σε κατάλληλο σηµείο του σπιτιού του ώστε να µπορεί να το βλέπει ο άλλος
από το δικό του σπίτι. Κάθε φορά που η Ελένη θέλει να αφήσει τη γάτα της στην αυλή
κάνει τα εξής:
1. Ρίχνει µια µατιά στο κοντάρι του ∆ήµου.
2. Αν δεν υπάρχει σηµαία:
i. Τοποθετεί σηµαία στο δικό της κοντάρι.
ii. Αφήνει τη γάτα της στον κήπο.
3. Αν υπάρχει σηµαία περιµένει λίγο και ξανα-ρίχνει µια µατιά στο κοντάρι του ∆ήµου
(δηλαδή πηγαίνει στο βήµα 1).
4. Όταν η γάτα επιστρέψει κατεβάζει τη σηµαία της από το κοντάρι.
Κάθε φορά που ο ∆ήµος θέλει να αφήσει το σκύλο του στην αυλή κάνει αντίστοιχες
ενέργειες:
1. Ρίχνει µια µατιά στο κοντάρι της Ελένης.
2. Αν δεν υπάρχει σηµαία:
i. Τοποθετεί σηµαία στο δικό του κοντάρι.
ii. Αφήνει τον σκύλο του στον κήπο.
3. Αν υπάρχει σηµαία περιµένει λίγο και ξανα-ρίχνει µια µατιά στο κοντάρι της Ελένης
(δηλαδή πηγαίνει στο βήµα 1).
4. Όταν ο σκύλος επιστρέψει κατεβάζει τη σηµαία του από το κοντάρι.
Επιτυγχάνει η λύση που ακολουθήθηκε από την Ελένη και τον ∆ήµο αµοιβαίο
αποκλεισµό ή θα έχουµε νέα µάχη των κατοικίδιων στην αυλή; Σας θυµίζει κάτι η
παραπάνω προτεινόµενη λύση;
Άσκηση Αυτοαξιολόγησης 16
Η Ελένη και ο ∆ήµος συνεδριάζουν και πάλι. Αυτή τη φορά αποφασίζουν τον ακόλουθο
αλγόριθµο για το γνωστό τους πρόβληµα:
Κάθε φορά που η Ελένη θέλει να αφήσει τη γάτα της στην αυλή κάνει τα εξής:
88
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Άσκηση Αυτοαξιολόγησης 17
Η Ελένη και ο ∆ήµος προσπαθούν για άλλη µια φορά (µήπως θα πρέπει να ανοίξουν ένα
βιβλίο πληροφορικής;). Τη φορά αυτή αποφασίζουν να χρησιµοποιήσουν µία µόνο
σηµαία ως εξής. ∆ένουν ένα σκοινί που ενώνει τα δύο κοντάρια. Το σκοινί µπορεί να
κινείται προς µια µόνο κατεύθυνση µετακινώντας τη σηµαία από το ένα κοντάρι στο
άλλο. Κάθε φορά που η σηµαία φθάνει στο κοντάρι της Ελένης, η Ελένη θα πρέπει να τη
λύσει και να την ξαναδέσει στη σωστή πλευρά του σκοινιού για να µπορέσει να ξανα-
ταξιδέψει πίσω στο ∆ήµο. Το ίδιο θα πρέπει να κάνει και ο ∆ήµος κάθε φορά που παίρνει
τη σηµαία, ώστε να µπορεί η σηµαία να επιστραφεί στην Ελένη.
Κάθε φορά που η Ελένη θέλει να αφήσει τη γάτα της στην αυλή κάνει τα εξής:
5. Αν η σηµαία είναι στο κοντάρι της, αφήνει τη γάτα της στην αυλή.
6. Στην αντίθετη περίπτωση περιµένει τον ∆ήµο να της στείλει τη σηµαία.
7. Όταν η γάτα επιστρέψει από την αυλή, λύνει τη σηµαία από το κοντάρι της, τη δένει
ξανά στη σωστή πλευρά του σκοινιού και στέλνει τη σηµαία στο ∆ήµο (για να
µπορέσει να αφήσει και αυτός το σκύλο του στην αυλή).
Με παρόµοιο τρόπο όταν ο ∆ήµος θέλει να αφήσει το σκύλο του στην αυλή κάνει τα
εξής:
1. Αν η σηµαία είναι στο κοντάρι του, αφήνει τον σκύλο του στην αυλή.
2. Στην αντίθετη περίπτωση περιµένει την Ελένη να του στείλει τη σηµαία.
3. Όταν ο σκύλος επιστρέψει από την αυλή, λύνει τη σηµαία από το κοντάρι του, τη
δένει ξανά στη σωστή πλευρά του σκοινιού και στέλνει τη σηµαία στην Ελένη (για
να µπορέσει να αφήσει και αυτή τη γάτα της στην αυλή).
Θα έχουµε τώρα νέες µάχες των κατοικίδιων στην αυλή; Τι θα συµβεί αν η Ελένη (ή ο
∆ήµος) λείψουν σε ταξίδι για µερικούς µήνες;
89
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Στη λύση του Peterson χρησιµοποιείται τόσο µια κοινή µεταβλητή turn που υποδηλώνει
ποιος έχει σειρά να εισέλθει στο κρίσιµο τµήµα, όσο και από µια µεταβλητή flag για
κάθε µια από τις διεργασίες, προκειµένου αυτές να δηλώνουν την πρόθεσή τους να
εισέλθουν ή όχι στο κρίσιµο τµήµα. Κάθε διεργασία i, που επιθυµεί να εισέλθει στο
κρίσιµο τµήµα, δηλώνει αρχικά την πρόθεσή της αλλάζοντας τη µεταβλητή flag[i] σε
TRUE και τη µεταβλητή turn σε i. Η διεργασία στη συνέχεια γίνεται ευγενική. Ελέγχει
αν είναι η σειρά της να εισέλθει στο κρίσιµο τµήµα και αν επιθυµεί η άλλη να εισέλθει.
Αν και οι δύο αυτές συνθήκες είναι αληθείς, τότε η διεργασία παραχωρεί τη σειρά της
στην άλλη διεργασία και περιµένει µέχρι αυτή να εξέλθει από το κρίσιµο τµήµα της και
να αλλάξει τη µεταβλητή της flag σε FALSE.
Εξηγούµε στη συνέχεια γιατί ο αλγόριθµος του Peterson είναι σωστός. Ας υποθέσουµε
ότι µια διεργασία, π.χ., η 1, εκτελεί πρώτη τον κώδικα εισόδου και εισέρχεται στο
κρίσιµο τµήµα της. Αν η διεργασία 0 εκτελέσει τον κώδικα εισόδου ενόσω η 1 βρίσκεται
στο κρίσιµο τµήµα, θα βρει το turn == 0 και το flag[1] == TRUE και θα µπλοκάρει στη
while. Αντίστοιχες ενέργειες θα συµβούν αν η 0 εκτελέσει πρώτη τον κώδικα εισόδου.
Ας δούµε τώρα τι θα συµβεί αν και οι δύο διεργασίες εκτελέσουν τον κώδικα εισόδου
σχεδόν ταυτόχρονα. Έστω ότι και οι δύο θέτουν την µεταβλητή τους flag σε TRUE. Το
σηµαντικό σηµείο είναι να αναρωτηθούµε ποια διεργασία θα εκτελέσει τελευταία τη
γραµµή 2 του κώδικα. Έστω ότι αυτή είναι η 0 (το σενάριο στην αντίθετη περίπτωση
είναι συµµετρικό). Τότε η 1 θα µπορέσει να εισέλθει στο κρίσιµο τµήµα της, ενώ οι δύο
συνθήκες της while για την 0 θα είναι TRUE και έτσι η 0 θα µπλοκάρει στη while µέχρι
η 1 να εξέλθει από το κρίσιµο τµήµα της (και να αλλάξει τη µεταβλητή flag[1]).
90
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
91
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
δουλεύει σωστά µόνο για δύο διεργασίες. Ο αλγόριθµος του Dekker θα συζητηθεί στην
ενότητα «Λίγο πιο ∆ύσκολα».
Σχήµα 39: Πέµπτη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού
για δύο διεργασίες.
Βρείτε σενάριο κατά το οποίο και οι δύο διεργασίες βρίσκονται στο κρίσιµο τµήµα τους.
Εξηγήστε τις οµοιότητες και τις διαφορές µε τη λύση του Peterson. Τι είναι αυτό που
εγγυάται ορθότητα στη λύση του Peterson, αλλά όχι στην παραπάνω λύση;
92
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
begin begin
flag[0] = TRUE; flag[1] = TRUE;
while (turn != 0) do while (turn != 1) do
begin begin
while (flag[1] == TRUE) do noop; while (flag[0] == TRUE) do noop;
turn = 0; turn = 1;
end end
<κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
flag[0] = FALSE; flag[1] = FALSE;
<µη-κρίσιµο τµήµα>; <µη-κρίσιµο τµήµα>;
end end
forever; forever;
Σχήµα 40: Έκτη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού.
93
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
εξωτερικής while του κώδικα εισόδου, ή να ισχύει (turn ==0) αλλά να ισχύει επίσης
(flag[0] != TRUE), ώστε η 1 να µην µπλοκάρει στην εσωτερική while. Στη συνέχεια, θα
πρέπει η εντολή “turn = 1;” να εκτελεστεί αµέσως πριν γίνει ο έλεγχος της εξωτερικής
while, ώστε να γίνει αποτίµηση της συνθήκης της εξωτερικής while σε FALSE και η 1 να
εισέλθει στο κρίσιµο τµήµα της.
Τώρα που γνωρίζουµε τις συνθήκες εισόδου κάθε διεργασίας στο κρίσιµο τµήµα, ας
κάνουµε µια πρώτη προσπάθεια (όχι απαραίτητα επιτυχηµένη) να βρούµε σενάριο στο
οποίο καταστρατηγείται η συνθήκη του αµοιβαίου αποκλεισµού. Θεωρούµε ότι η turn
έχει αρχικά την τιµή 0 (στην αντίθετη περίπτωση το σενάριο είναι συµµετρικό). Ας
υποθέσουµε ότι ξεκινά πρώτα η 0. Για την 0, η συνθήκη της εξωτερικής while είναι
FALSE και άρα θα µπει απευθείας στο κρίσιµο τµήµα της. Ενόσω είναι τώρα στο
κρίσιµο τµήµα της, ξεκινά να εκτελείται η 1. Η συνθήκη της εξωτερικής while για την 1
είναι TRUE, οπότε εκτελείται το µπλοκ εντολών της. Η συνθήκη της εσωτερικής while
είναι επίσης TRUE (δυστυχώς, γιατί αν ήταν FALSE θα είχαµε βρει το κακό µας
σενάριο) και άρα η 1 θα µπλοκάρει στην εσωτερική while περιµένοντας την 0 να εξέλθει
από το κρίσιµο τµήµα της.
Η προσπάθεια µας, δυστυχώς, δεν οδήγησε σε κακό σενάριο. Οδήγησε όµως σε µια
χρήσιµη παρατήρηση, ότι αν ξεκινήσει πρώτη η διεργασία i για την οποία ισχύει turn ==
i, αυτή θα µπει απευθείας στο κρίσιµο τµήµα της αλλά η άλλη διεργασία δεν θα µπορέσει
να ξεφύγει από τους εξωτερικούς και τους εσωτερικούς ελέγχους των εντολών
ανακύκλωσης του κώδικα εισόδου της. Ας δοκιµάσουµε λοιπόν να ξεκινήσουµε πρώτη
τη διεργασία 1 για την οποία δεν ισχύει “turn == 1” και ας παρατηρήσουµε τι θα συµβεί
σε αυτή την περίπτωση. Η εξωτερική while αποτιµάται σε TRUE, αλλά η εσωτερική
while αποτιµάται σε FALSE. Από εδώ και στο εξής, η 1 είναι έτοιµη να αλλάξει το turn
σε 1, να αποτιµήσει την συνθήκη της εξωτερικής while σε FALSE και να εισέλθει στο
κρίσιµο τµήµα της. Αν της επιτρέψουµε να το κάνει αυτό και µετά ξεκινήσουµε την 0, η
0 θα έχει την ίδια τύχη που είχε η 1 στην αποτυχηµένη προσπάθεια της προηγούµενης
παραγράφου. Για αυτό ας υποθέσουµε ότι ο χρονοδροµολογητής διακόπτει την 1 πριν
προλάβει να αλλάξει την τιµή της turn σε 1. ∆ροµολογείται τώρα η 0. Η 0 αποτιµά την
συνθήκη της εξωτερικής while σε FALSE και εισέρχεται στο κρίσιµο τµήµα της. Αυτό
ήταν ακριβώς αυτό που επιζητούσαµε! Αν ο χρονοδροµολογητής διακόψει τώρα την 0
όσο εκτελεί το κρίσιµο τµήµα της και αφήσει να εκτελεστεί η 1, η 1 θα αλλάξει την τιµή
της turn σε 1, θα αποτιµήσει την συνθήκη της εξωτερικής while σε FALSE και θα µπει
και αυτή στο κρίσιµο τµήµα της! Ο σκοπός µας επιτεύχθηκε! Μόλις τελειώσαµε το
«µαγείρεµα» του σεναρίου που ψάχναµε, το οποίο παρουσιάζεται πιο αναλυτικά στο
Σχήµα 41.
∆ιεργασία 0 ∆ιεργασία 1
flag[1] = TRUE;
Έλεγχος συνθήκης εξωτερικής while;
/* είναι TRUE */
Έλεγχος συνθήκης εσωτερικής while;
/* είναι FALSE */
flag[0] = TRUE;
Έλεγχος συνθήκης εξωτερικής while;
/* είναι FALSE */
94
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 41: Σενάριο που καταστρατηγεί τη συνθήκη του αµοιβαίου αποκλεισµού για την 5η προτεινόµενη
λύση.
Άσκηση Αυτοαξιολόγησης 21
Εξετάστε αν η προτεινόµενη λύση του Σχήµατος 42 είναι σωστή λύση στο πρόβληµα του
αµοιβαίου αποκλεισµού για δύο διεργασίες.
Κοινές Μεταβλητές
shared in turn; /* αρχικά 0 ή 1 */
shared boolean flag[2]; /* αρχικά FALSE */
∆ιεργασία 0 ∆ιεργασία 1
repeat repeat
begin begin
1. flag[0] = TRUE; 1. flag[1] = TRUE;
2. turn = 1; 2. turn = 0;
3. while (turn == 0 && flag[1] == TRUE) do 3. while (turn == 1 && flag[0] == TRUE) do
noop; noop;
4. <κρίσιµο τµήµα>; 4. <κρίσιµο τµήµα>;
5. flag[0] = FALSE; 5. flag[1] = FALSE;
6. <µη-κρίσιµο τµήµα>; 6. <µη-κρίσιµο τµήµα>;
end end
forever; forever;
95
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 43: Ένα σενάριο που οδηγεί σε παρατεταµένη στέρηση την ελαφρώς τροποποιηµένη έκδοση της
λύσης του Peterson.
Στο Σχήµα 43, η διεργασία 0 ξεκινά πρώτη, εκτελεί τις δύο πρώτες εντολές της και
διακόπτεται. Η διεργασία 1 δροµολογείται στη συνέχεια και εισέρχεται στο κρίσιµο
τµήµα της. Όταν η διεργασία 1 εξέρχεται από το κρίσιµο τµήµα της, προλαβαίνει πριν
δροµολογηθεί η 0 να ξαναεκτελέσει τις δύο πρώτες εντολές του κώδικα εισόδου της (θα
µπορούσαµε εναλλακτικά να είχαµε αφήσει την 1 να εκτελέσει όλο τον κώδικα εισόδου
της και να την διακόπταµε όταν θα ήταν και πάλι στο κρίσιµο τµήµα της). Έτσι, η 0,
παρότι περιοδικά της δίνεται η δυνατότητα να εκτελεστεί, δεν παρουσιάζει πρόοδο αφού
96
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
(είναι κακότυχη και) η συνθήκη της while που εκτελεί είναι πάντα αληθής όταν ο
χρονοδροµολογητής αποφασίζει να τη δροµολογήσει.
Εξετάστε αν η λύση του Peterson είναι δίκαιη λύση. Στην περίπτωση που η λύση του
Peterson είναι δίκαιη λύση, τι είναι το διαφορετικό στις δύο λύσεις που κάνει τη µια
δίκαιη και την άλλη να οδηγεί σε παρατεταµένη στέρηση; □
Άσκηση Αυτοαξιολόγησης 22
Εξετάστε αν η προτεινόµενη λύση του Σχήµατος 44 είναι σωστή λύση στο πρόβληµα του
αµοιβαίου αποκλεισµού για δύο διεργασίες.
Κοινές µεταβλητές
shared boolean flag[2]; /* αρχικά, FALSE */
∆ιεργασία 0 ∆ιεργασία 1
flag[0] = 1; start: flag[1] = 0;
while (flag[1] != 0) do noop; while (flag[0] != 0) do noop;
<κρίσιµο τµήµα>; flag[1] = 1;
flag[0] = 0; if (flag[0] == 1) then goto start;
<µη-κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
flag[1] = 0;
<µη-κρίσιµο τµήµα>;
Σχήµα 44: Έβδοµη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού.
Σχήµα 45: Όγδοη προτεινόµενη λύση µε χρήση λογισµικού στο πρόβληµα του αµοιβαίου αποκλεισµού.
97
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Η ρουτίνα GetPid() επιστρέφει τον ίδιο πάντα ακέραιο όταν καλείται από τη διεργασία 0
(π.χ., ας υποθέσουµε ότι η GetPid() επιστρέφει το 0 στη διεργασία 0) και τον ίδιο πάντα
ακέραιο, διαφορετικό από αυτόν που επιστρέφει στη διεργασία 0, όταν καλείται από τη
διεργασία 1 (π.χ., ας υποθέσουµε ότι η GetPid() επιστρέφει το 1 στη διεργασία 1).
Εποµένως, ο κώδικας που εκτελεί η κάθε διεργασία είναι αυτός που φαίνεται στο Σχήµα
46.
∆ιεργασία 0 ∆ιεργασία 1
1. start: while (busy == 1) noop; 1. start: while (busy == 1) noop;
2. trying = 0; 2. trying = 1;
3. if (busy == 1) goto start; 3. if (busy == 1) goto start;
4. busy = 1; 4. busy = 1;
5. if (trying != 0) goto start; 5. if (trying != 1) goto start;
<κρίσιµο τµήµα>; <κρίσιµο τµήµα>;
busy = 0; busy = 0;
<µη-κρίσιµο τµήµα>; <µη-κρίσιµο τµήµα>;
Σχήµα 46: Πιο αναλυτική περιγραφή του κώδικα των διεργασιών της όγδης προτεινόµενη λύσης.
Υπόδειξη: Βεβαιωθείτε ότι δεν µπορεί να βρίσκονται δύο διεργασίες ταυτόχρονα στο κρίσιµο τµήµα τους
(δηλαδή η συνθήκη του αµοιβαίου αποκλεισµού ισχύει). Βρείτε σενάριο στο οποίο και οι δύο διεργασίες
επιθυµούν να εισέλθουν στο κρίσιµο τµήµα τους, αλλά βρίσκονται και οι δύο µπλοκαρισµένες στη while
του κώδικα εισόδου. (Στο κακό σενάριο, η µία διεργασία θα οδηγηθεί στη while µέσω του goto της if της
γραµµής 3 του κώδικα και η άλλη µέσω του goto της if της γραµµής 5 του κώδικα).
Σχήµα 47: Ένατη προτεινόµενη λύση στο πρόβληµα του αµοιβαίου αποκλεισµού για δύο διεργασίες.
98
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Υπόδειξη: Η συνθήκη του αµοιβαίου αποκλεισµού δεν ισχύει. Προσπαθήστε να βρείτε κακό σενάριο που
οδηγεί και τις δύο διεργασίες στο κρίσιµο τµήµα τους ταυτόχρονα.
Η συνθήκη προόδου επίσης δεν ισχύει. Υπάρχει σενάριο στο οποίο και οι δύο διεργασίες επιθυµούν να
εισέλθουν στο κρίσιµο τµήµα τους αλλά καµία δεν το επιτυγχάνει. Αν προσπαθήσατε και δεν µπορέσατε
να το βρείτε µην το βάζετε κάτω. Το σενάριο αυτό είναι πραγµατικά δύσκολο. Μελετήστε το από την
ενδεικτική επίλυση του θέµατος 3 της 4ης εργασίας του Ακ. Έτους 2001-2002. Παρουσιάζει ενδιαφέρον.
3.6 Σηµαφόροι
Οι σηµαφόροι προτάθηκαν από τον Dijkstra [] το 1965. Η χρήση τους συζητείται
εκτενώς σε όλα τα βιβλία λειτουργικών συστηµάτων [].
Ένας σηµαφόρος είναι µια διαµοιραζόµενη ακέραια µεταβλητή την οποία οι διεργασίες
µπορούν να προσβαίνουν µόνο µέσω της εκτέλεσης δύο λειτουργιών που ονοµάζονται
down() (ή P()) και up() (ή V()). (Επειδή οι ονοµασίες P() και V() είναι αρχικά
ολλανδικών λέξεων και άρα είναι ελάχιστα µνηµονικές για όσους δεν µιλούν την
Ολλανδική γλώσσα, θα χρησιµοποιηθούν από εδώ και στο εξής τα ονόµατα down() και
up() για τις δύο λειτουργίες.)
Η λειτουργία up() αυξάνει την τιµή του σηµαφόρου κατά 1. Έτσι, κάθε φορά που µια
διεργασία εκτελεί την up(), η τιµή του σηµαφόρου αυξάνεται κατά 1 (ωστόσο, η
πραγµατική τιµή του σηµαφόρου δεν είναι γνωστή ούτε στην διεργασία που εκτελεί την
up() ούτε και σε καµία άλλη διεργασία χρήστη του συστήµατος).
Η λειτουργία down() ελέγχει την τιµή του σηµαφόρου και αν είναι µεγαλύτερη του 0 την
µειώνει κατά 1 και τερµατίζει. Αν η τιµή του σηµαφόρου είναι µικρότερη ή ίση του
µηδενός, η διεργασία που εκτελεί την down() απενεργοποιείται. Όλες οι διεργασίες που
εκτελούν την down() πάνω σε ένα σηµαφόρο που έχει τιµή µικρότερη ή ίση του µηδενός
απενεργοποιούνται. Αν αργότερα ο σηµαφόρος αποκτήσει τιµή µεγαλύτερη του µηδενός
(λόγω της εκτέλεσης µιας η περισσότερων up() λειτουργιών στο σηµαφόρο), όλες οι
διεργασίες που έχουν απενεργοποιηθεί πάνω στο σηµαφόρο επανενεργοποιούνται και
προσπαθούν να εκτελέσουν την down() εξ αρχής. Ανάλογα µε την τιµή του σηµαφόρου
µία ή περισσότερες από αυτές θα τα καταφέρουν ενώ οι υπόλοιπες και πάλι θα
απενεργοποιηθούν, κ.ο.κ.
Σε έναν σηµαφόρο µπορεί επίσης να αποδοθεί αρχική τιµή. Είναι πολύ σηµαντικό να
θυµάται ο αναγνώστης, ότι αυτές είναι οι µοναδικές λειτουργίες που υποστηρίζονται από
σηµαφόρους. Για παράδειγµα, δεν είναι δυνατό να διαβαστεί η τιµή ενός σηµαφόρου.
Επίσης, οι µόνοι τρόποι να αλλαχθεί η τιµή ενός σηµαφόρου είναι µέσω της εκτέλεσης
µιας ή περισσότερων up() και down() λειτουργιών (δηλαδή, εκτός από την αρχική
ανάθεση τιµής, δεν επιτρέπεται να γίνει άλλη απευθείας ανάθεση τιµής σε έναν
σηµαφόρο). Οι λειτουργίες up() και down() εκτελούνται ατοµικά.
Μια απλοϊκή υλοποίηση των λειτουργιών up() και down() ενός σηµαφόρου µε χρήση
λογισµικού φαίνεται στο Σχήµα 48. Στην υλοποίηση αυτή δεν παρουσιάζεται ο τρόπος µε
τον οποίο επιτυγχάνεται η ατοµική εκτέλεση των λειτουργιών αυτών.
Λειτουργία up(semaphore S) Λειτουργία down(semaphore S)
S = S + 1; while (S <= 0) do noop;
S = S -1;
99
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Η λειτουργία up() αυξάνει την τιµή του σηµαφόρου κατά 1. Η λειτουργία down() ελέγχει
επαναληπτικά την τιµή του S µέχρι να ανακαλύψει ότι αυτή είναι µεγαλύτερη του
µηδενός. Σε αυτή την περίπτωση την µειώνει κατά 1 και τερµατίζει. Τροποποιήσεις του S
θα πρέπει να είναι εγγυηµένο πως εκτελούνται ατοµικά. Επίσης, ο έλεγχος της while
στην down() και η ενδεχόµενη µείωση της τιµής του S που ακολουθεί θα πρέπει να
εκτελούνται επίσης ατοµικά.
Στην πραγµατικότητα, τα ΛΣ δεν υλοποιούν τις λειτουργίες up() και down() µε τον τρόπο
που παρουσιάζεται στο Σχήµα 48. Ο λόγος είναι πως η επαναληπτική εκτέλεση της
συνθήκης της while της down() από µια ή περισσότερες διεργασίες επιβαρύνει το
σύστηµα, ενώ δεν εκτελείται κάποιος χρήσιµος υπολογισµός. Όταν µια διεργασία ελέγχει
επαναληπτικά µια συνθήκη χωρίς να εκτελεί οποιοδήποτε άλλο χρήσιµο υπολογισµό,
λέµε ότι βρίσκεται σε ενεργό αναµονή. Για λόγους καλής απόδοσης, τα ΛΣ φροντίζουν
να αποφεύγουν την ενεργό αναµονή.
Οι λειτουργίες up() και down() υλοποιούνται ως κλήσεις συστήµατος. Συνήθως, το ΛΣ
χρησιµοποιεί µια λίστα για κάθε σηµαφόρο στην οποία τοποθετεί όλες τις διεργασίες που
πρέπει να απενεργοποιηθούν πάνω στο σηµαφόρο. Αν αργότερα το ΛΣ αναλάβει την
εκτέλεση µιας ή περισσότερων up() λειτουργιών και µετά το πέρας τους διαπιστώσει ότι
ο σηµαφόρος έχει πλέον τιµή µεγαλύτερη του µηδενός, διαλέγει αυθαίρετα µια από τις
διεργασίες της λίστας, µειώνει την τιµή του σηµαφόρου κατά 1 και επιστρέφει
επιβεβαίωση περάτωσης της λειτουργίας down() στη διεργασία που επιλέχθηκε για να
συνεχίσει την εκτέλεσή της. Αυτό γίνεται επαναληπτικά µέχρι η τιµή του σηµαφόρου να
ξαναγίνει 0 ή η λίστα απενεργοποιηµένων διεργασιών να αδειάσει.
Για να γίνει κατανοητή η χρησιµότητα των σηµαφόρων ως εργαλεία συγχρονισµού, ας
δούµε πως θα µπορούσαµε να επιλύσουµε το πρόβληµα του αµοιβαίου αποκλεισµού µε
χρήση σηµαφόρων. Η λύση είναι πολύ απλή. Κάθε διεργασία εκτελεί τον κώδικα του
Σχήµατος 49.
Σηµαφόροι
semaphore mutex; /* αρχική τιµή 1 */
Κώδικας που εκτελεί κάθε διεργασία
repeat
begin
down(mutex);
<κρίσιµο τµήµα>;
up(mutex);
<µη-κρίσιµο τµήµα>;
end
forever;
Εξηγούµε στη συνέχεια γιατί η λύση του Σχήµατος 49 είναι σωστή. Η λύση χρησιµοποιεί
έναν σηµαφόρο mutex που έχει αρχική τιµή 1. Έστω ότι δύο ή περισσότερες διεργασίες
επιθυµούν να εισέλθουν στο κρίσιµο τµήµα τους. Ας θυµηθούµε ότι η down() εκτελείται
100
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
ατοµικά. Η πρώτη διεργασία, έστω Α, που θα την καλέσει, θα µειώσει την τιµή του
σηµαφόρου σε 0 και θα εισέλθει στο κρίσιµο τµήµα της. Όλες οι υπόλοιπες διεργασίες
που θα εκτελέσουν την down() όσο η Α βρίσκεται στο κρίσιµο τµήµα της, θα βρουν την
τιµή του mutex ίση µε 0 και θα απενεργοποιηθούν. Μόνο όταν αργότερα η Α εξέλθει από
το κρίσιµο τµήµα της και εκτελέσει την up(), η τιµή του σηµαφόρου θα αλλάξει σε 1 και
έτσι θα επιτραπεί σε µια ακόµη διεργασία να εισέλθει στο κρίσιµο τµήµα της (οι
υπόλοιπες θα βρουν και πάλι την τιµή του σηµαφόρου ίση µε 0 και θα απενεργοποιηθούν
για άλλη µια φορά). Έτσι, µόνο µια διεργασία µπορεί να βρίσκεται κάθε χρονική στιγµή
στο κρίσιµο τµήµα της. ∆εν είναι δύσκολο να συµπεράνουµε ότι η λύση του Σχήµατος
49 ικανοποιεί και τη συνθήκη προόδου. Εποµένως, η λύση του Σχήµατος 49 είναι σωστή
λύση στο πρόβληµα του αµοιβαίου αποκλεισµού. Η απλότητα της λύσης αυτής οφείλεται
στην υπόθεση ότι το σύστηµα µας παρέχει ένα ισχυρό εργαλείο συγχρονισµού, όπως οι
σηµαφόροι.
Σηµαφόροι όπως ο mutex στο Σχήµα 49, που χρησιµοποιούνται µε τον τρόπο που
περιγράφτηκε πιο πάνω για την επίτευξη αµοιβαίου αποκλεισµού, ονοµάζονται δυαδικοί
σηµαφόροι. Ένας δυαδικός σηµαφόρος έχει αρχική τιµή 1 (προκειµένου να επιτραπεί
στην 1η διεργασία που θα εκτελέσει την down() πάνω στο σηµαφόρο να µην
απενεργοποιηθεί και εποµένως να εισέλθει στο κρίσιµο τµήµα της), ενώ οι µοναδικές
τιµές που µπορεί να πάρει είναι 0 και 1. Επαφίεται στον αναγνώστη να επαληθεύσει ότι
οι ισχυρισµοί αυτοί είναι αληθείς για το σηµαφόρο mutex του Σχήµατος 49.
Λύση
Είναι σχετικά εύκολο να επαληθεύσετε ότι ο κώδικας του Σχήµατος 50 ικανοποιεί τη
συνθήκη του αµοιβαίου αποκλεισµού. Μια µόνο διεργασία, έστω η Α, θα καταφέρει να
101
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
µειώσει την τιµή του σηµαφόρου και να εισέλθει στο κρίσιµο τµήµα της. Οι υπόλοιπες
θα βρουν τη τιµή του σηµαφόρου ίση µε 0 και θα µπλοκάρουν στην down(). Άρα, δεν
είναι δυνατό δύο διεργασίες να εκτελούν ταυτόχρονα το κρίσιµο τµήµα τους. Ωστόσο, η
λύση του Σχήµατος 50 δεν ικανοποιεί τη συνθήκη προόδου. Όταν η Α τελειώνει την
εκτέλεση του κρίσιµου τµήµατός της, εκτελεί down() στο σηµαφόρο mutex. Όµως, η
τιµή του σηµαφόρου είναι 0 και άρα η Α απενεργοποιείται (όπως έχουν απενεργοποιηθεί
και όλες οι άλλες διεργασίες που επιθυµούν να εισέλθουν στο κρίσιµο τµήµα τους και
όπως θα απενεργοποιηθούν, λόγω του πρώτου down() στον κώδικα και όλες οι
διεργασίες που θα επιχειρήσουν να εισέλθουν στο κρίσιµο τµήµα τους στο µέλλον). Όλες
αυτές οι διεργασίες δεν θα επανενεργοποιηθούν ποτέ, αφού ποτέ καµία διεργασία δεν θα
εκτελέσει κάποια λειτουργία up() στον mutex για να αυξήσει την τιµή του. Με άλλα
λόγια το σύστηµα βρίσκεται σε αδιέξοδο. Παρότι καµία διεργασία δεν εκτελεί το κρίσιµο
τµήµα της, και παρότι υπάρχουν διεργασίες που επιθυµούν να εισέλθουν, καµία από
αυτές δεν τα καταφέρνει. Εποµένως, η λύση του Σχήµατος 50 δεν ικανοποιεί τη συνθήκη
προόδου και άρα δεν είναι σωστή λύση στο πρόβληµα του αµοιβαίου αποκλεισµού. □
Λύση
Η λύση του Σχήµατος 51 δεν ικανοποιεί τη συνθήκη αµοιβαίου αποκλεισµού. Ας
υποθέσουµε ότι δύο διεργασίες επιχειρούν να εισέλθουν στο κρίσιµο τµήµα τους
ταυτόχρονα. Η πρώτη που θα εκτελέσει την εντολή up(mutex) θα αλλάξει την τιµή του
mutex από 1 σε 2 και θα εισέλθει στο κρίσιµο τµήµα της. Αν τώρα µια δεύτερη διεργασία
δροµολογηθεί, θα αυξήσει και αυτή την τιµή του mutex κατά 1 και θα εισέλθει και αυτή
στο κρίσιµο τµήµα της. Όσες διεργασίες και να επιχειρήσουν να εισέλθουν στο κρίσιµο
τµήµα τους θα τα καταφέρουν ανεξάρτητα µε το πόσες άλλες διεργασίες βρίσκονται
ταυτόχρονα στο κρίσιµο τµήµα τους. Εποµένως, η λύση του Σχήµατος 51 δεν είναι
σωστή λύση του προβλήµατος του αµοιβαίου αποκλεισµού. □
Παρατήρηση
Είναι σηµαντικό να κατανοήσει ο αναγνώστης γιατί η λύση του Σχήµατος 49 είναι
σωστή λύση στο πρόβληµα του αµοιβαίου αποκλεισµού, ενώ οι λύσεις που προτείνονται
στα Σχήµατα 50 και 51 δεν είναι σωστές λύσεις. ∆εν θα πρέπει να συνεχίσει τη µελέτη
102
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
του αν δεν έχει βεβαιωθεί πως έχει κατανοήσει σε βάθος που οφείλεται η ορθότητα της
πρώτης λύσης και ποια είναι τα προβλήµατα των δύο τελευταίων.
Άσκηση Αυτοαξιολόγησης 29
Είναι η λύση που παρουσιάζεται στο Σχήµα 49 δίκαιη λύση του προβλήµατος του
αµοιβαίου αποκλεισµού; Περιγράψτε σενάριο στο οποίο µια διεργασία εισέρχεται µη-
πεπερασµένο αριθµό φορών στο κρίσιµο τµήµα της, ενώ άλλες διεργασίες παραµένουν
µπλοκαρισµένες επ’ άπειρον εκτελώντας τον κώδικα εισόδου.
103
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Όταν καλείται η λειτουργία up(), η τιµή του σηµαφόρου πρέπει να αυξάνει κατά 1. Η
λειτουργία αυτή θα πρέπει να εκτελείται ατοµικά.
Η λύση παρουσιάζεται στο Σχήµα 52.
Σηµαφόροι και Κοινές Μεταβλητές
typedef shared int semaphore;
/* ο τύπος semaphore προσοµοιώνεται από τον τύπο shared int */
semaphore sem;
shared int lock = 1;
/* χρησιµοποοιούµε επίσης µια διαµοιραζόµενη ακέραια µεταβλητή lock µε αρχική τιµή 1 */
Σχήµα 52: Υλοποίηση σηµαφόρων χρησιµοποιώντας ακέραιες διαµοιραζόµενες µεταβλητές και την
ατοµική λειτουργία Test&Set() που παρέχεται από το υλικό.
104
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
105
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
µερικές συµβουλές για τον τρόπο που πρέπει κάποιος να εργάζεται, οι οποίες ίσως
βοηθούν στην επίλυση τέτοιων προβληµάτων.
Το πρώτο ερώτηµα που θα πρέπει να απαντηθεί, όταν ζητείται λύση σε ένα πρόβληµα
συγχρονισµού, είναι τι είδους διεργασίες αναµιγνύονται; Η απάντηση στο ερώτηµα αυτό
είναι εύκολη και συνήθως προκύπτει άµεσα από την εκφώνηση. Αν ωστόσο δεν είναι
σαφής, ο αναγνώστης θα πρέπει να απαντήσει την εξής ερώτηση: «Πόσα διαφορετικά
προγράµµατα µπορούν να εκτελούνται ταυτόχρονα;» ή «Πόσες διαφορετικές οντότητες
µπορούν να είναι ενεργές ταυτόχρονα;» ή «Πόσες διαφορετικές ενέργειες µπορούν να
συµβαίνουν ταυτόχρονα;». Για κάθε ένα τέτοιο πρόγραµµα/οντότητα/ενέργεια, υπάρχει
και ένα είδος διεργασιών. Για παράδειγµα, στο πρόβληµα που προαναφέρθηκε, υπάρχουν
δύο είδη διεργασιών, η διεργασία εξυπηρέτη και οι διεργασίες πελατών. Άρα, θα πρέπει
να γραφεί κώδικας για δύο ρουτίνες, µία για τη διεργασία εξυπηρέτη και µία για τις
διεργασίες πελατών.
Το δεύτερο ερώτηµα που χρειάζεται να απαντηθεί είναι αν απαιτείται η χρήση κοινών
µεταβλητών (ή κοινών πόρων) που θα πρέπει να προσπελαύνονται ατοµικά από τις
διεργασίες. Για κάθε κοινό πόρο που απαιτείται, συνήθως χρειαζόµαστε έναν δυαδικό
σηµαφόρο για να επιτύχουµε αµοιβαίο αποκλεισµό. Στο παράδειγµα µας, έχουµε µόνο
µία κοινή µεταβλητή και άρα ενδεχόµενα χρειαζόµαστε έναν δυαδικό σηµαφόρο για να
επιτύχουµε αµοιβαίο αποκλεισµό. Ας ονοµάσουµε τον σηµαφόρο αυτό mutex.
Στη συνέχεια θα πρέπει να αναρωτηθούµε µήπως υπάρχουν καταστάσεις (ή
περιπτώσεις), επιπρόσθετα εκείνων που προκύπτουν λόγω του αµοιβαίου αποκλεισµού,
στις οποίες κάποιο είδος διεργασιών πρέπει να απενεργοποιείται. Προφανώς, κάθε
διεργασία πρέπει να απενεργοποιείται όταν κάποιο άλλη διεργασία προσβαίνει τον κοινό
πόρο, αλλά µήπως υπάρχουν και άλλες περιπτώσεις που απαιτείται απενεργοποίηση
διεργασιών στο πρόβληµά µας;
Παρατηρούµε ότι η διεργασία εξυπηρέτη θα πρέπει να εκτελείται αυστηρά εναλλάξ µε
µια κάθε φορά από τις διεργασίες πελατών. Παρατηρήστε ότι, ενώ η χρήση του δυαδικού
σηµαφόρου mutex εγγυάται ότι µια µόνο διεργασία εκτέλει κάθε φορά το κρίσιµο τµήµα,
δεν παρέχει καµία εγγύηση για τη σειρά µε την οποία θα εκτελεστούν οι διεργασίες.
Βάσει των παραπάνω, προκύπτει ότι υπάρχουν δύο ακόµη περιπτώσεις στο πρόβληµα
που µελετάµε, στις οποίες κάποιο είδος διεργασίας θα πρέπει να απενεργοποιείται:
1. Η διεργασία εξυπηρέτη πρέπει να απενεργοποιείται όσο δεν έχει εκτελεστεί κάποια
από τις διεργασίες πελατών από την τελευταία φορά εκτέλεσής της.
2. Οι διεργασίες πελατών θα πρέπει να απενεργοποιούνται όσο η διεργασία εξυπηρέτη
δεν έχει εκτελεστεί από την τελευταία φορά εκτέλεσης κάποιας διεργασίας πελάτη.
Ο µοναδικός τρόπος απενεργοποίησης των διεργασιών είναι µε την εκτέλεση µιας
λειτουργίας down() πάνω σε κάποιο σηµαφόρο του οποίου η τιµή είναι 0. Άρα, η λύση
χρειάζεται τουλάχιστον τόσους σηµαφόρους όσες και οι περιπτώσεις που προ-
αναφέρθηκαν. Ας ονοµάσουµε ServerSem το σηµαφόρο πάνω στον οποίο θα
απενεργοποιούνται οι διεργασίες-πελάτες και ας ονοµάσουµε ClientSem το σηµαφόρο
πάνω στον οποίο θα απενεργοποιείται η διεργασία εξυπηρέτης (η ανάθεση ονοµάτων
είναι προφανώς αυθαίρετη, αλλά καλό είναι να διαλέγουµε ονόµατα που να επιτρέπουν
στον κώδικα να είναι ευανάγνωστος).
106
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 53: Περιγραφή ενεργειών που εκτελούν οι διεργασίες πελατών και εξυπηρέτη.
Στο σηµείο αυτό είµαστε πολύ κοντά στη λύση. Όταν έχουµε καταφέρει να γράψουµε
ψευδοκώδικα στη µορφή που παρουσιάζεται στο Σχήµα 53, είναι εύκολο να
αποφασίσουµε ποια είναι τα κρίσιµα τµήµατα κάθε διεργασίας. Κρίσιµα τµήµατα είναι
τα µέρη του κώδικα που προσβαίνουν διαµοιραζόµενες µεταβλητές (και γενικότερα
διαµοιραζόµενους πόρους). Αυτά θα πρέπει να προστατευθούν από ταυτόχρονη
προσπέλαση, περικλείοντάς τα σε κώδικα εισόδου και κώδικα εξόδου, ώστε να
επιτευχθεί αµοιβαίος αποκλεισµός.
Το µόνο που αποµένει να γίνει είναι να καταλάβουµε ποιες down() και up() λειτουργίες
πρέπει να εκτελεστούν για κάθε µια από τις ενέργειες που περιγράφτηκαν πιο πάνω. Ας
δούµε πρώτα τις ενέργειες της διεργασίας εξυπηρέτη. Η 1η ενέργεια θα υλοποιηθεί µε µια
κλήση της down() στο σηµαφόρο ServerSem (ο οποίος αποφασίσαµε πως θα είναι ο
σηµαφόρος πάνω στον οποίο θα απενεργοποιείται η διεργασία εξυπηρέτη). Η 2η ενέργεια
είναι το κρίσιµο τµήµα της διεργασίας εξυπηρέτη και άρα θα πρέπει να γίνει κλήση της
down(mutex) πριν εκτελεστεί η ενέργεια αυτή, καθώς και κλήση της up(mutex) αµέσως
107
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
µετά. Η 3η ενέργεια αποσκοπεί στο να επιτρέψει σε κάποια από τις διεργασίες πελατών
(που µπορεί να είναι απενεργοποιηµένη περιµένοντας την διεργασία εξυπηρέτη να
διαβάσει την αίτηση που της στάλθηκε να επανενεργοποιηθεί και) να δηµιουργήσει νέα
αίτηση. Η ενέργεια αυτή υλοποιείται µε ένα up() στο σηµαφόρο ClientSem στον οποίο
αποφασίσαµε πως θα απενεργοποιούνται οι διεργασίες πελατών. Τέλος, η 4η ενέργεια
είναι το µη-κρίσιµο τµήµα της διεργασίας εξυπηρέτη και άρα καµία down() ή up()
λειτουργία δεν σχετίζεται µε την εκτέλεσή του.
Κάθε µια από τις διεργασίες πελατών ξεκινά µε το µη-κρίσιµο τµήµα της (1η ενέργεια)
και άρα δεν εµπλέκονται σηµαφόροι στην ενέργεια αυτή. Η 2η ενέργεια υλοποιείται µε
την κλήση µιας down() στο σηµαφόρο ClientSem πάνω στον οποίο αποφασίσαµε πως θα
απενεργοποιούνται οι διεργασίες πελατών. Η 3η ενέργεια αποτελεί το κρίσιµο τµήµα των
διεργασιών πελατών και άρα θα πρέπει να είναι εγγυηµένο (µε κατάλληλη χρήση του
σηµαφόρου mutex) ότι θα εκτελεστεί ατοµικά. Τέλος, κάθε διεργασία πελάτη πρέπει να
ενηµερώσει τη διεργασία εξυπηρέτη ότι µια νέα αίτηση δηµιουργήθηκε. Αυτό γίνεται
εκτελώντας µια λειτουργία up() πάνω στο σηµαφόρο ServerSem (ο οποίος αποφασίσαµε
πως θα είναι ο σηµαφόρος πάνω στον οποίο θα απενεργοποιείται η διεργασία
εξυπηρέτη).
Πρέπει να είναι σαφές πως στο σηµείο αυτό βρισκόµαστε πολύ κοντά σε µια πρώτη
λύση. Το µόνο που αποµένει να αποφασιστεί είναι ποιες θα είναι οι αρχικές τιµές των
σηµαφόρων. Προφανώς, κάποια διεργασία πελάτη θα πρέπει να είναι εκείνη που θα
ξεκινήσει µε την παραγωγή µιας αίτησης. Άρα, ο σηµαφόρος ServerSem πάνω στον
οποίο απενεργοποιείται η δεργασία εξυπηρέτη θα πρέπει να έχει αρχική τιµή 0 (έτσι ώστε
αν δροµολογηθεί πρώτη η διεργασία αυτή να απενεργοποιηθεί αµέσως). Αντίθετα, η
αρχική τιµή του σηµαφόρου ClientSem θα πρέπει να είναι 1 για να δοθεί η δυνατότητα
σε κάποιον από τους πελάτες να δηµιουργήσει µια πρώτη αίτηση. Η αρχική τιµή του
δυαδικού σηµαφόρου mutex είναι 1.
Μια πρώτη λύση του προβλήµατος που µελετάµε απεικονίζεται στο Σχήµα 54. Η ρουτίνα
process_request() αναλαµβάνει να επεξεργαστεί την αίτηση (µη-κρίσιµο τµήµα
διεργασίας εξυπηρέτη), ενώ η ρουτίνα produce_request() αναλαµβάνει την δηµιουργία
µιας νέας αίτησης σε κάποια τοπική µεταβλητή της εκάστοτε διεργασίας πελάτη που την
καλεί (µη-κρίσιµο τµήµα διεργασίας πελάτη).
Κοινές µεταβλητές & Σηµαφόροι:
semaphore ClientSem = 1;
semaphore ServerSem = 0;
semaphore mutex = 1;
request X;
∆ιεργασία Εξυπηρέτης ∆ιεργασία Πελάτη
repeat repeat
begin begin
down(ServerSem); produce_request();
down(mutex); down(ClientSem);
read X; down(mutex);
up(mutex); write X;
up(ClientSem); up(mutex);
108
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
process_request(); up(ServerSem);
end end
forever; forever;
Η λύση (αν και περισσότερο πολύπλοκη από ότι θα έπρεπε, όπως θα δούµε στη
συνέχεια) είναι σωστή. Αν η διεργασία εξυπηρέτη ξεκινήσει να εκτελείται πρώτη θα
απενεργοποιηθεί εκτελώντας την λειτουργία down() στο σηµαφόρο ServerSem που έχει
αρχική τιµή 0. Αν µία ή περισσότερες διεργασίες πελατών ξεκινήσουν την εκτέλεση
τους, όλες εκτός από µία θα µπλοκάρουν εκτελώντας την λειτουργία down() στο
σηµαφόρο ClientSem. Η µοναδική διεργασία, έστω Α, που δεν θα µπλοκάρει στην
down(ClientSem), θα βρει το σηµαφόρο mutex να έχει τιµή 1 και δεν θα µπλοκάρει o;yte
στην down(mutex). Εποµένως, η Α θα συνεχίσει µε την καταγραφή της αίτησής της στη
µεταβλητή Χ. Στη συνέχεια, η Α θα εκτελέσει µια λειτουργία up() στο mutex για να
υποδηλώσει ότι τελείωσε την εκτέλεση του κρίσιµου τµήµατός της και µια up() στον
ServerSem για να αλλάξει την τιµή του από 0 σε 1 και να επιτρέψει στη διεργασία-
εξυπηρέτη να επεξεργαστεί την αίτηση. Προσέξτε ότι ο σηµαφόρος ClientSem
εξακολουθεί να έχει τιµή 0. Εποµένως, όλες οι διεργασίες-πελάτη που θέλουν να
παράγουν αίτηση συνεχίζουν να βρίσκονται µπλοκαρισµένες στην down(ClientSem).
Παρατηρήστε επίσης ότι, αν η Α θελήσει εκ νέου να παράγει µια αίτηση, θα µπλοκάρει
αν εκτελέσει την λειτουργία down(ClientSem) πριν η διεργασία εξυπηρέτη προλάβει να
επεξεργαστεί την αίτηση που είναι ήδη γραµµένη στην Χ.
Η διεργασία εξυπηρέτη εκτελείται όταν κάποια διεργασία πελάτη εκτελέσει µια up() στο
σηµαφόρο ServerSem. Η πρώτη ενέργεια που εκτελεί είναι η down(mutex) προκειµένου
να επιτευχθεί αµοιβαίος αποκλεισµός για την πρόσβαση στη µεταβλητή Χ. Προσέξτε
πως η τιµή του mutex θα είναι 1. Η διεργασία εξυπηρέτη διαβάζει την αίτηση στην Χ,
επιτελεί µια λειτουργία up(mutex) για να υποδηλώσει το τέλος του κρίσιµου τµήµατος
και µια up() στον σηµαφόρο ClientSem για να επιτρέψει σε έναν ακόµη πελάτη να
δηµιουργήσει µια νέα αίτηση.
Είναι σηµαντικό να κατανοήσουµε ότι στο παράδειγµα που µελετάµε, η διεργασία
εξυπηρέτης και µία κάθε φορά από τις διεργασίες πελατών θα πρέπει να εκτελούνται
αυστηρά εναλλάξ. Αυτό ακριβώς επιτυγχάνει ο κώδικας του Σχήµατος 54.
Πριν τελειώσουµε τη µελέτη του προβλήµατος θα πρέπει να µας απασχολήσει ένα
τελευταίο ερώτηµα. Χρειάζονται όλοι οι σηµαφόροι που χρησιµοποιήσαµε ή µήπως
κάποιοι από αυτούς είναι περιττοί; Μήπως ο αµοιβαίος αποκλεισµός που απαιτείται για
την ατοµική προσπέλαση στην διαµοιραζόµενη µεταβλητή X παρέχεται ήδη (έµµεσα)
λόγω της χρήσης των υπολοίπων σηµαφόρων που έχουν στρατευτεί; Με άλλα λόγια,
είναι ποτέ δυνατό δύο ή περισσότερες διεργασίες να προσπαθήσουν ταυτόχρονα να
προσβούν την µεταβλητή Χ, ή µήπως η διαδιεργασιακή επικοινωνία που επιτυγχάνεται
µε τη χρήση των σηµαφόρων ServerSem και ClientSem καθιστά κάτι τέτοιο αδύνατο;
∆εν είναι δύσκολο να γίνει κατανοητό ότι η χρήση του σηµαφόρου mutex είναι περιττή.
Όπως έχει ήδη αναφερθεί, η χρήση των σηµαφόρων ServerSem και ClientSem
συνεπάγεται ότι η διεργασία εξυπηρέτης και µία κάθε φορά από τις διεργασίες πελατών
θα εκτελούνται αυστηρά εναλλάξ. Εποµένως, η χρήση του mutex είναι περιττή. Με άλλα
λόγια, ο αµοιβαίος αποκλεισµός κατά την προσπέλαση στην Χ είναι εγγυηµένος ακόµη
109
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
και αν παραληφθούν οι εντολές “down(mutex)” και “up(mutex)” από τον κώδικα του
Σχήµατος 54.
Η απλούστερη λύση του προβλήµατος πελατών-εξυπηρέτη παρουσιάζεται στο Σχήµα 55.
Κοινές µεταβλητές & Σηµαφόροι:
semaphore ClientSem = 1;
semaphore ServerSem = 0;
request X;
∆ιεργασία-Εξυπηρέτης ∆ιεργασία-Πελάτης
repeat repeat
begin begin
down(ServerSem); produce_request();
read X; down(ClientSem);
up(ClientSem); write X;
process_request(); up(ServerSem);
end end
forever; forever;
110
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
111
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
καπνιστή (κάτι που δεν ίσχυε για τις διεργασίες πελατών, όπου οποιαδήποτε από τις
διεργασίες πελατών θα µπορούσε να είναι αυτή που θα παράγει την επόµενη αίτηση).
Στη συνέχεια δουλεύουµε µε ίδιο ακριβώς τρόπο όπως στην Ενότητα 3.7.1, ώστε να
καταλήξουµε στη λύση που παρουσιάζεται στο Σχήµα 57. Στη λύση αυτή, θεωρούµε ότι
η ρουτίνα DecideWhichMaterialsToSell() αποφασίζει µε τυχαίο τρόπο ποια δύο από τα
υλικά θα τοποθετηθούν κάθε φορά στο τραπέζι (µη κρίσιµο τµήµα διεργασίας πωλητή),
ενώ µέσω της ρουτίνας TakeMaterialsFromTable() ένας καπνιστής παίρνει τα υλικά από
το τραπέζι και τα χρησιµοποιεί (κρίσιµο τµήµα διεργασιών καπνιστών).
Κοινές µεταβλητές & Σηµαφόροι:
semaphore SmokerSem[3] = {0, 0, 0}; /* αρχικές τιµές 0 και για τους 3 σηµαφόρους */
semaphore SellerSem = 1;
shared variable table;
∆ιεργασία-Πωλητής ∆ιεργασία-Καπνιστής i, 0 <= i <= 2
repeat repeat
begin begin
DecideWhichMaterialsToSell(); down(SmokerSem[i]);
down(SellerSem); TakeMaterialsFromTable();
update table; up(SellerSem);
if (υλικά που τοποθετήθηκαν στο end
τραπέζι είναι χαρτί και σπίρτα) forever;
then up(SmokerSem[0]);
else if (υλικά που τοποθετήθηκαν στο
τραπέζι είναι καπνός και
σπίρτα)
then up(SmokerSem[1]);
else up(SmokerSem[2]);
end
forever;
Παρατηρήστε ότι στη λύση του Σχήµατος 57 δεν χρησιµοποιείται κανένας δυαδικός
σηµαφόρος για επίτευξη αµοιβαίου αποκλεισµού, αφού αυτός επιτυγχάνεται λόγω του
τρόπου χρήσης των υπολοίπων σηµαφόρων. □
112
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
up(SB); up(SA);
end end
forever; forever;
113
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
∆ιεργασία Αυτοκίνητο
repeat
begin
1. Αν δεν υπάρχουν πελάτες απενεργοποιήσου;
2. Κάνε ταξίδι;
3. Ενηµέρωσε πελάτες ότι υπάρχει ένα ακόµη ελεύθερο αυτοκίνητο;
/* αυτό γίνεται αφού τελειώσει το ταξίδι */
end
forever;
∆ιεργασία Πελάτης
1. Αν δεν υπάρχει ελεύθερο αυτοκίνητο απενεργοποιήσου;
2. Ενηµέρωσε τα αυτοκίνητα ότι υπάρχει άλλος ένας διαθέσιµος πελάτης;
3. Κάνε ταξίδι;
Σχήµα 59: Περιγραφή απλών ενεργειών που εκτελούν οι διεργασίες επιβατών και αυτοκινήτων.
Βελτιώσεις: Στη λύση του Σχήµατος 60 δεν θεωρήσαµε ότι η επιβίβαση στα αυτοκίνητα
και η αποβίβαση από αυτά θα πρέπει να γίνεται ατοµικά. Μήπως όµως θα ήταν καλύτερο
αν επιτευχθεί κάτι τέτοιο; Θεωρείστε ότι πολλοί επιβάτες αποφασίζουν ταυτόχρονα ότι
θέλουν να χρησιµοποιήσουν ένα από τα αυτοκίνητα. Προφανώς, αν διεκδικήσουν όλοι
µαζί το ίδιο αυτοκίνητο θα υπάρξει πρόβληµα. Έτσι, θα πρέπει να επιτευχθεί αµοιβαίος
αποκλεισµός κατά την επιβίβαση στο αυτοκίνητο. Οµοίως, κατά την αποβίβαση θα
πρέπει να είναι εγγυηµένη η επίτευξη αµοιβαίου αποκλεισµού, ώστε να µην γίνει
προσπάθεια από κάποια άλλη διεργασία επιβάτη να καταλάβει το όχηµα πριν ο
προηγούµενος επιβάτης εξέλθει αυτού. Για να γίνει πιο κατανοητή η ανάγκη αµοιβαίου
αποκλεισµού στο σηµείο αυτό, θεωρείστε ότι ο αριθµός των ελεύθερων αυτοκινήτων
είναι αποθηκευµένος σε µια διαµοιραζόµενη µεταβλητή Χ. Κάθε φορά που γίνεται
επιβίβαση σε ένα αυτοκίνητο, η τιµή της Χ πρέπει να µειωθεί κατά 1, ενώ κάθε φορά που
γίνεται αποβίβαση από ένα αυτοκίνητο, η τιµή της Χ πρέπει να αυξηθεί κατά 1.
114
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Χρειαζόµαστε εποµένως έναν δυαδικό σηµαφόρο mutex, µε αρχική τιµή 1, για την
επίτευξη του αµοιβαίου αποκλεισµού.
Η βελτιωµένη λύση παρουσιάζεται στο Σχήµα 61, όπου οι ρουτίνες ReserveCar () και
ReleaseCar() εµπεριέχουν τις ενέργειες κατάληψης και ελευθέρωσης, αντίστοιχα, ενός
αυτοκινήτου.
Κοινές µεταβλητές & Σηµαφόροι:
semaphore PassengerSem = 0;
semaphore CarSem = µ;
semaphore mutex = 1;
∆ιεργασία-Αυτοκίνητο ∆ιεργασία-Επιβάτης
repeat down(CarSem);
begin down(mutex);
down(PassengerSem); up(PassengerSem);
take_trip(); car_embarkation();
up(CarSem); up(mutex);
end take_trip();
forever; down(mutex);
leave_car();
up(mutex);
Σχήµα 61: Πρώτη βελτιωµένη λύση στο πρόβληµα του Ζωολογικού Κήπου.
115
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
forever; leave_car();
up(EndOfTrip);
up(mutex);
Σχήµα 62: ∆εύτερη βελτιωµένη λύση στο πρόβληµα του ζωολογικού κήπου.
116
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
3.7.3 Το Πρόβληµα του Κουρέα (Θέµα 3Β, Α’ Τελική Εξέταση, Μάιος 2001)
Ένα κουρείο αποτελείται από την "αίθουσα αναµονής" µε µ καρέκλες και το δωµάτιο
στο οποίο εργάζεται ο κουρέας που περιέχει την καρέκλα του κουρέα. Θεωρήστε πως
ένας πελάτης που εισέρχεται στο κουρείο περιµένει να εξυπηρετηθεί µόνο αν βρει κενή
καρέκλα στην αίθουσα αναµονής, αλλιώς φεύγει από το κουρείο. Αν δεν υπάρχουν
πελάτες να εξυπηρετηθούν, ο κουρέας πηγαίνει για ύπνο. Αν ένας πελάτης µπει στο
κουρείο και βρει τον κουρέα να κοιµάται τον ξυπνάει. Χρησιµοποιώντας σηµαφόρους,
ζητείται αλγόριθµος περιγραφής της λειτουργίας του κουρείου, ο οποίος θα συγχρονίζει
τον κουρέα µε τους πελάτες.
Ας µελετήσουµε πρώτα το εξής απλούστερο πρόβληµα. Ένα κουρείο απασχολεί ένα
κουρέα και έχει άπλετο χώρο για να περιµένουν οι πελάτες του. Αν κάποιος πελάτης βρει
τον κουρέα ελεύθερο εξυπηρετείται διαφορετικά περιµένει για να εξυπηρετηθεί. Αν δεν
υπάρχει πελάτης για εξυπηρέτηση ο κουρέας απενεργοποιείται. Αν ένας πελάτης βρει
τον κουρέα να κοιµάται, τον ξυπνάει.
Το πρόβληµα µπορεί εύκολα να µελετηθεί µε τον γνωστό τρόπο που παρουσιάστηκε στις
προηγούµενες ενότητες. Συνίσταται ισχυρά στον αναγνώστη να δοκιµάσει να δώσει λύση
στο απλό αυτό πρόβληµα χρησιµοποιώντας σηµαφόρους (πριν διαβάσει τη λύση που
περιγράφεται στη συνέχεια).
Η λύση στο απλό πρόβληµα φαίνεται στο Σχήµα 63.
Σηµαφόροι
semaphore CustomersSem = 0;
semaphore BarbersSem = 1;
∆ιεργασία-Κουρέας ∆ιεργασία-Πελάτης
repeat
begin
down(CustomersSem); up(CustomersSem);
cut_hair(); down(BarbersSem);
up(BarbersSem); get_haircut();
end
forever;
Σχήµα 63: Λύση στην απλή έκδοση του προβλήµατος του κουρέα.
Πριν συνεχίσει, ο αναγνώστης θα πρέπει να βεβαιωθεί πως έχει πειστεί ότι µια λύση
παρόµοια αυτής του Σχήµατος 63 θα είχε προκύψει, αν είχε ακολουθηθεί η γνωστή
διαδικασία επίλυσης του προβλήµατος (που παρουσιάζεται αναλυτικά στην Ενότητα
3.7.1). Αξίζει να τονιστεί πως η λύση του Σχήµατος 63 δεν είναι η µόνη σωστή λύση.
Στο Σχήµα 64 παρουσιάζεται µία ακόµη σωστή λύση.
Σηµαφόροι
semaphore CustomersSem = 0;
semaphore BarbersSem = 0;
117
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
∆ιεργασία-Κουρέας ∆ιεργασία-Πελάτης
repeat
begin
down(CustomersSem); up(CustomersSem);
up(BarbersSem); down(BarbersSem);
cut_hair(); get_haircut();
end
forever;
Σχήµα 64: Μία ακόµη σωστή λύση στην απλή έκδοση του προβλήµατος του κουρέα.
Θα προσπαθήσουµε τώρα να λύσουµε την πιο πολύπλοκη έκδοση του προβλήµατος που
περιγράφτηκε αρχικά. Κάθε πελάτης θα πρέπει τώρα να ελέγχει πόσοι πελάτες
περιµένουν ήδη στο κουρείο και αν αυτοί είναι περισσότεροι από µ θα πρέπει να
αποχωρεί. Παρατηρήστε ότι ο σηµαφόρος CustomersSem έχει τιµή ίση µε τον αριθµό
των πελατών σε αναµονή, αλλά αφού δεν επιτρέπεται να διαβάσουµε την τιµή ενός
σηµαφόρου, η πληροφορία αυτή δεν είναι δυνατόν να ληφθεί µέσω του σηµαφόρου.
Χρειαζόµαστε εποµένως µια διαµοιραζόµενη µεταβλητή που θα µετρά τον αριθµό των
πελατών σε αναµονή. Ας ονοµάσουµε την µεταβλητή αυτή cc. Κάθε πελάτης θα ελέγχει
αρχικά την cc και αν η τιµή της είναι µ θα αποχωρεί από το κουρείο. Στην αντίθετη
περίπτωση, θα αυξάνει την cc κατά ένα και θα απενεργοποιείται περιµένοντας σε κάποια
από τις διαθέσιµες καρέκλες για να κουρευτεί.
Η διεργασία κουρέας µειώνει την τιµή της cc κατά ένα κάθε φορά που παραλαµβάνει
έναν πελάτη για κούρεµα.
Η προσπέλαση στην cc (είτε για ανάγνωση ή για εγγραφή) θα πρέπει να γίνεται ατοµικά.
Χρησιµοποιούµε εποµένως έναν δυαδικό σηµαφόρο mutex προκειµένου να επιτευχθεί
αµοιβαίος αποκλεισµός.
Ο κώδικας φαίνεται στο Σχήµα 65.
Σηµαφόροι και Κοινές µεταβλητές
semaphore CustomersSem = 0;
semaphore BarbersSem = 1;
semaphore mutex = 1;
shared int cc; /* µε αρχική τιµή 0 */
∆ιεργασία-Κουρέας ∆ιεργασία-Πελάτης
repeat down(mutex);
begin if (cc < µ) then
down(CustomersSem); begin
down(mutex); up(CustomersSem);
cc = cc – 1; cc = cc + 1;
up(mutex); up(mutex);
cut_hair(); down(BarbersSem);
up(BarbersSem); get_haircut();
end end
forever; else up(mutex);
118
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
119
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
∆ιεργασία-Αναγνώστης ∆ιεργασία-Εγγραφέας
repeat
begin repeat
down(rcSem); begin
rc = rc + 1; down(DataBase);
if (rc == 1) down(DataBase); write_data();
up(rcSem); up(DataBase);
read_data(); end
down(rcSem); forever;
rc = rc – 1;
if (rc == 0) up(DataBase);
up(rcSem);
end
forever;
Άσκηση Αυτοαξιολόγησης 39
Είναι η λύση του Σχήµατος 67 δίκαιη ή οδηγεί σε παρατεταµένη στέρηση; Τι θα συµβεί
αν καταυθάνουν διαρκώς αναγνώστες στο σύστηµα ενώ υπάρχουν εγγραφείς που
ενδιαφέρονται να ενηµερώσουν τη βάση;
Άσκηση Αυτοαξιολόγησης 40
Τροποποιήστε τον κώδικα του εγγραφέα, ώστε αν στο σύστηµα καταφθάσουν και άλλοι
εγγραφείς όσο κάποιος τροποποιεί τη βάση να είναι εγγυηµένο ότι θα τροποποιήσουν και
αυτοί τη βάση πριν κάποιος αναγνώστης ξεκινήσει ανάγνωση. Ο κώδικας του αναγνώστη
δεν θα πρέπει να αλλάξει µορφή, ενώ ο νέος κώδικας του εγγραφέα θα πρέπει να µοιάζει
περισσότερο στον κώδικα του αναγνώστη (µε την επιπρόσθετη µικρή δυσκολία ότι δεν
επιτρέπεται πολλοί εγγραφείς να προσπελαύνουν τη βάση δεδοµένων ταυτόχρονα).
120
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
121
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Το µοναδικό είδος διεργασιών στο σύστηµα αυτό είναι οι κολυµβητές, ενώ τα ερµάρια
και τα αποδυτήρια είναι οι πόροι του συστήµατος για τους οποίους ανταγωνίζονται οι
διεργασίες.
Η ανάλυση του προβλήµατος δείχνει πως κάθε διεργασία (δηλ. κάθε κολυµβητής) πρέπει
να εκτελέσει τρεις διαφορετικές ρουτίνες από τη στιγµή που θα εισέλθει στο
κολυµβητήριο έως τη στιγµή που θα εξέλθει. Αυτές οι ρουτίνες είναι :
ΆλλαξεΤαΡούχα();
Κολύµπησε();
ΆλλαξεΤοΜαγιό();
Επιλύστε το παραπάνω πρόβληµα συγχρονισµού χρησιµοποιώντας σηµαφόρους.
122
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Περίµενε τον τερµατισµό της εκτέλεσης της P2; Εκτέλεσε τον κώδικά σου;
Εκτέλεσε τον κώδικά σου;
Ενηµέρωσε την P4 ότι τελείωσε η εκτέλεσή σου;
Ενηµέρωσε την P5 ότι τελείωσε η εκτέλεσή σου;
∆ιεργασία P5
Περίµενε τον τερµατισµό εκτέλεσης της P3;
Εκτέλεσε τον κώδικά σου;
Σχήµα 69: Περιγραφή των ενεργειών των διεργασιών της Άσκησης Αυτοαξιολόγησης 44.
123
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
• Μια διεργασία αυτοκίνητο στο νησί απενεργοποιείται αν το πλοίο δεν είναι στο νησί
και υπάρχει χώρος στο parking.
• Μια διεργασία αυτοκίνητο στην ηπειρωτική χώρα απενεργοποιείται αν το πλοίο είναι
στο νησί.
• Η διεργασία πλοίο δεν απενεργοποιείται ποτέ.
Απαιτούνται εποµένως δύο ακόµη σηµαφόροι.
Υποθέτουµε ότι τα αυτοκίνητα που βρίσκουν το χώρο στάθµευσης γεµάτο στο νησί
αποχωρούν από το σύστηµα.
Οι τρεις ρουτίνες που πρέπει να δηµιουργηθούν θα έχουν τη µορφή που παρουσιάζεται
στο Σχήµα 70. Θεωρείστε ότι οι OnIsland, OnLand είναι σταθερές που έχουν τις τιµές 0
και 1, αντίστοιχα.
∆ιεργασία Πλοίο
repeat
begin
if (BoatLocation == OnIsland) then
begin
Όσο (υπάρχουν αυτοκίνητα για επιβίβαση στο νησί και υπάρχει χώρος στο πλοίο):
Ενηµέρωσε ένα αυτοκίνητο ότι επιβιβάζεται;
Μείωσε κατά ένα τον αριθµό των αυτοκινήτων προς επιβίβαση;
Άλλαξε την τιµή της µεταβλητής BoatLocation;
end
else
begin
Όσο (υπάρχουν αυτοκίνητα για επιβίβαση στην ηπειρωτική χώρα και
υπάρχει χώρος στο πλοίο):
Ενηµέρωσε ένα αυτοκίνητο ότι επιβιβάζεται;
Μείωσε κατά ένα τον αριθµό των αυτοκινήτων προς επιβίβαση;
Αν (CarsOnIsland == Ν) (και άρα ο χώρος στάθµευσης έχει γεµίσει)
τερµάτισε την εκτέλεση της while
Άλλαξε την τιµή της µεταβλητής BoatLocation;
end
end
forever;
124
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 70: Σκελετός κώδικα διεργασιών για το πρόβληµα που περιγράφεται στην Άσκηση
Αυτοαξιολογόγησης 45.
Βάσει της παραπάνω λύσης παρουσιάστε ψευδοκώδικα που να συµπεριλαµβάνει όλες τις
λειτουργίες up() και down() που πραγµατοποιούνται. □
125
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
διεργασίες και άρα απαιτείται συγχρονισµός των διεργασιών στην προσπέλασή του. Η
γλωσσική έκφραση που περιγράφεται στο Σχήµα 71 ορίζει ότι το µπλοκ εντολών S
αποτελεί την κρίσιµη περιοχή του u. Η έκφραση χρησιµοποιήθηκε για πρώτη φορά από
τον Hansen (το 1972).
shared T u; /* διαµοιραζόµενη µεταβλητή u τύπου T */
region u do
begin
<µπλοκ εντολών S>;
end
Όπως έχουµε ήδη δει, η εκτέλεση της κρίσιµης περιοχής µιας διεργασίας απαιτεί
αµοιβαίο αποκλεισµό. Το ερώτηµα πως θα υλοποιήσουµε κρίσιµες περιοχές (δηλαδή πως
θα επιλύσουµε το πρόβληµα του αµοιβαίου αποκλεισµού) έχει ήδη αναλυθεί αρκετά. Για
παράδειγµα, µια τέτοια υλοποίηση µπορεί να χρησιµοποιεί σηµαφόρους, ή την εντολή
Test&Set(), ή τη λύση του Peterson, κλπ. Στην ενότητα αυτή θα χρησιµοποιούµε την
γλωσσική έκφραση που περιγράφτηκε πιο πάνω, αφαιρετικά, για να καθορίσουµε ότι το
µπλοκ εντολών S πρέπει να εκτελεστεί ατοµικά. Το πως αυτό θα επιτευχθεί εξαρτάται
από το εκάστοτε σύστηµα και τα εργαλεία συγχρονισµού που παρέχει.
Παράδειγµα (Μέρος Θέµατος 2, Εργασία 4, Ακ. Έτος 2001-2002)
Περιγράψτε και πάλι τις ρουτίνες boolean deposit(int amount) και boolean withdraw(int
amount), που περιγράφονται στην Άσκηση Αυτοαξιολόγησης 8, χρησιµοποιώντας τη
γλωσσική έκφραση του Hansen για αµοιβαίο αποκλεισµό.
Οι µη-ατοµικές εκδόσεις των ρουτινών deposit() και withdraw() έχουν συζητηθεί στην
Ενότητα 3.2. ∆εδοµένου ότι τα κρίσιµα τµήµατα αυτών είναι γνωστά, ο ψευδοκώδικάς
παρουσιάζεται στο Σχήµα 72.
Κοινές µεταβλητές
shared int balance;
boolean deposit(int amount) boolean withdraw(int amount)
int tmp; /* τοπική µεταβλητή */ begin
region balance do
begin
begin
region balance do
tmp = balance;
begin
end
balance = balance + amount;
if (amount > tmp) then
print “Το νέο σας ποσό είναι”;
begin
print balance;
print “∆εν επιτρέπεται ανάληψη
end
τόσο µεγάλου ποσού!”;
return TRUE;
return FALSE;
end
end
else
begin
region balance do
begin
126
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 72: Ατοµική έκδοση των deposit() και withdraw() χρησιµοποιώντας τη γλωσσική έκφραση του
Hansen. □
Άσκηση Αυτοαξιολόγησης 46
Εξηγήστε πως υλοποιείται η γλωσσική έκφραση του Hansen χρησιµοποιώντας όλους
τους δυνατούς τρόπους συγχρονισµού που µελετήθηκαν σε προηγούµενες ενότητες. □
Θα δούµε στη συνέχεια µια ισχυρότερη γλωσσική έκφραση από εκείνη του Hansen, η
οποία επιτρέπει σε µια διεργασία να περιµένει έως µια αυθαίρετη συνθήκη πάνω στο
κοινό πόρο να ικανοποιηθεί. Η γλωσσική έκφραση περιγράφεται στη συνέχεια:
shared T u; /* η διαµοιραζόµενη µεταβλητή u είναι τύπου Τ */
region u do
begin
S 0;
await B(u);
S 1;
end
Η συνθήκη B(u) είναι µια οποιαδήποτε boolean έκφραση που αναφέρεται στον κοινό
πόρο u. Ο κώδικας µεταξύ των begin end εκτελείται ατοµικά. Αν η συνθήκη B(u) είναι
αληθής, η διεργασία εκτελεί το µπλοκ εντολών S1 και τερµατίζει την εκτέλεση του
κρίσιµου τµήµατός της. Ωστόσο, αν η συνθήκη B(u) δεν είναι αληθής, η διεργασία που
εκτελεί την await αναστέλλεται και µια άλλη διεργασία µπορεί να ξεκινήσει την
εκτέλεση της κρίσιµης περιοχής της (δηλαδή του κώδικα µεταξύ των begin end). Όταν
µια διεργασία τερµατίζει την εκτέλεση της κρίσιµης περιοχής, όλες οι διεργασίες που
είχαν ανασταλεί στην await επανενεργοποιούνται, έτσι ώστε η κάθε µια ατοµικά να
εξετάσει και πάλι τη συνθήκη B(u). Αξίζει να σηµειωθεί πως η συνθήκη µπορεί να είναι
τώρα αληθής αφού η διεργασία που µόλις τελείωσε το κρίσιµο τµήµα της µπορεί να
άλλαξε την τιµή του u. Θα πρέπει να τονιστεί πως ακόµη και αν όλες οι διεργασίες βρουν
τη συνθήκη αληθή, η εκτέλεση του S1 είναι εγγυηµένο πως θα γίνει ατοµικά. Το ίδιο
φυσικά ισχύει και για το µπλοκ εντολών S0.
Η γλωσσική έκφραση του Σχήµατος 73 είναι ισχυρότερη από εκείνη του Σχήµατος 71
αφού, επιπρόσθετα του αµοιβαίου αποκλεισµού, µπορεί να χρησιµοποιηθεί για την
υλοποίηση όλων εκείνων των καταστάσεων που µία ή περισσότερες διεργασίες πρέπει να
ανασταλούν περιµένοντας µέχρι κάποια συνθήκη να είναι αληθής. Η γλωσσική έκφραση
127
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
κρίσιµων περιοχών υπό συνθήκη µπορεί εποµένως να χρησιµοποιηθεί για την επίλυση
σύνθετων προβληµάτων συγχρονισµού, όπως αυτά που µελετήθηκαν στην Ενότητα 3.7.
128
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 74: Λύση στο πρόβληµα του ζωολογικού κήπου µε χρήση κρίσιµων περιοχών υπό συνθήκη
(αντίστοιχη εκείνης του Σχήµατος 60).
Σχήµα 75: Πρώτη βελτιωµένη λύση στο πρόβληµα του ζωολογικού κήπου µε χρήση κρίσιµων περιοχών
υπό συνθήκη (αντίστοιχη εκείνης του Σχήµατος 61).
129
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 76: ∆εύτερη βελτιωµένη λύση στο πρόβληµα του Ζωολογικού Κήπου µε χρήση κρίσιµων περιοχών
υπό συνθήκη (αντίστοιχη εκείνης του Σχήµατος 62).
Παρατήρηση: Στις λύσεις που παρουσιάζονται στα Σχήµατα 74, 75 και 76 δεν είναι
δυνατόν να προσπελαύνονται δύο διαφορετικές κοινές µεταβλητές ταυτόχρονα. Αυτό
οφείλεται στο γεγονός ότι επιλέχθηκε οι κοινές µεταβλητές που χρησιµοποιούνται σε
κάθε µια από τις υλοποιήσεις αυτές να αποτελούν πεδία µιας µεταβλητής b τύπου struct
zoo στην οποία αναφέρονται όλες οι εντολές region που χρησιµοποιούνται. Εποµένως
στις λύσεις αυτές ο κοινός πόρος είναι η µεταβλητή b και η region δεν επιτρέπει την
προσπέλαση κανένος πεδίου της b από πολλές διεργασίες ταυτόχρονα.
Η λύση που παρουσιάζεται στο Σχήµα 77 είναι αντίστοιχη της λύσης του Σχήµατος 76
αλλά επιτυγχάνει µεγαλύτερο παραλληλισµό (βάσει όσων συζητηθήκαν παραπάνω).
Κοινές Μεταβλητές
130
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Σχήµα 77: Λύση αντίστοιχη εκείνης του Σχήµατος 76 αλλά που επιτυγχάνει µεγαλύτερο παραλληλισµό. □
Σχήµα 78: Πρώτη προσπάθεια επίλυσης του προβλήµατος αναγνωστών-εγγραφέων µε χρήση κρίσιµων
περιοχών υπό συνθήκη.
Η παραπάνω λύση, όπως και η λύση του Σχήµατος 66, παρουσιάζει το πρόβληµα ότι
µόνο ένας αναγνώστης µπορεί να διαβάζει κάθε φορά δεδοµένα από τη βάση. Για να
επιλύσουµε το πρόβληµα αυτό, θα εισαγάγουµε µια µεταβλητή συνθήκης, την DataBase
131
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
(που θα παίζει το ρόλο του σηµαφόρου DataBase). Η DataBase θα έχει αρχική τιµή 1. Θα
χρησιµοποιηθεί επίσης η κοινή µεταβλητή rc µε αρχική τιµή 0. Ο κώδικας φαίνεται στο
Σχήµα 79.
Κοινές Μεταβλητές
shared record RW
begin
shared int rc;
shared boolean DataBase;
end
shared record RW b; /* µε αρχική τιµή 0 για το πεδίο b.rc και αρχική τιµή 1 για το πεδίο
b.DataBase */
∆ιεργασία-Αναγνώστης ∆ιεργασία-Εγγραφέας
region b do region b do
begin begin
if (b.rc == 0) then await b.DataBase > 0;
begin b.DataBase = b.DataBase – 1;
await b.DataBase > 0; write_data();
b.DataBase = b.DataBase - 1; b.DataBase = b.DataBase + 1;
end end
b.rc = b.rc + 1;
end
read_data();
region b do
begin
b.rc = b.rc – 1;
if (b.rc == 0) then
b.DataBase = b.DataBase + 1;
end
Σχήµα 79: Σωστή λύση του προβλήµατος αναγνωστών-εγγραφέων µε χρήση κρίσιµων περιοχών υπό
συνθήκη.
Ο πρώτος αναγνώστης που θα εισέλθει στο σύστηµα θα εξετάσει την τιµή της
b.DataBase. Αν είναι 1, θα την αλλάξει σε 0. Επόµενοι αναγνώστες δεν θα εκτελούν την
if και άρα θα µπορούν να εκτελεστούν ταυτόχρονα. Όσοι εγγραφείς έρχονται στο
σύστηµα ενόσω κάποιοι αναγνώστες διαβάζουν δεδοµένα της βάσης, βρίσκουν την
b.DataBase να έχει τιµή 0 και µπλοκάρουν στην await. Ο τελευταίος αναγνώστης θα
αλλάξει την τιµή της b.DataBase σε 1 προκειµένου να δοθεί η δυνατότητα σε εγγραφείς
(ή και σε αναγνώστες που µπορεί να εισέλθουν αργότερα στο σύστηµα) να προσβούν τη
βάση.
Αν ένας αναγνώστης εισέλθει στο σύστηµα ενόσω ένας εγγραφέας τροποποιεί τη βάση, ο
αναγνώστης θα βρει την b.DataBase να έχει τιµή 0 και θα µπλοκάρει στην await. Το ίδιο
θα συµβεί µε όλους τους αναγνώστες που θα εισέλθουν στο σύστηµα πριν ο εγγραφέας
τελειώσει τις τροποποιήσεις του στη βάση. Παρατηρήστε ότι αυτό δεν θα συνέβαινε αν η
αύξηση της rc προηγείτο της await και για το λόγο αυτό η αύξηση της rc συµβαίνει τώρα
µετά την await. Θυµηθείτε ότι η αύξηση της rc προηγείται της down(DataBase) στον
132
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
κώδικα του Σχήµατος 67. Η διαφορά µεταξύ των δύο κωδίκων είναι πως αν µια
διεργασία µπλοκάρει σε κάποια await, η region επιτρέπει σε άλλη διεργασία να εκτελέσει
το µπλοκ εντολών της. Αυτό δεν ισχύει στην περίπτωση που χρησιµοποιούνται
σηµαφόροι, όπου όλοι οι αναγνώστες, εκτός από τον πρώτο, θα µπλοκάρουν στην
down(rcSem) και κανένας άλλος δεν θα εκτελέσει την συνθήκη της if πριν να
επανενεργοποιηθεί ο πρώτος και εκτελέσει την up(rcSem).
Άσκηση Αυτοαξιολόγησης 47
Επιλύστε όλα τα προβλήµατα που συζητήθηκαν στην Ενότητα 3.7 χρησιµοποιώντας
κρίσιµες περιοχές υπό συνθήκη αντί για σηµαφόρους.
133
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
number[i] = 0;
<µη-κρίσιµο τµήµα>;
Κάθε διεργασία i, 0 ≤ i < n, επιλέγει αρχικά έναν αριθµό. Η διεργασία διαβάζει τους
αριθµούς όλων των άλλων διεργασιών και επιλέγει ο αριθµός της να είναι το µέγιστο των
αριθµών αυτών συν ένα. Προσέξτε ότι αυτό το κοµµάτι κώδικα µπορεί να εκτελεστεί
ταυτόχρονα από περισσότερες της µιας διεργασίες και άρα είναι πιθανό περισσότερες
από µια διεργασίες να πάρουν τον ίδιο αριθµό. Αφού τα εισιτήρια πρέπει να είναι
µοναδικά, οι διεργασίες χρησιµοποιούν ως εισιτήριο το ζεύγος που αποτελείται από τον
αριθµό που διάλεξαν (που δεν είναι µοναδικός) και το αναγνωριστικό τους (που είναι
µοναδικό). Έτσι, το εισιτήριο κάθε διεργασίας είναι τελικά µοναδικό. Όσο κάθε
διεργασία i επιλέγει τον αριθµό της, η µεταβλητή choosing[i] έχει την τιµή TRUE.
Όταν µια διεργασία i επιλέξει εισητήριο εκτελεί τα εξής. Για κάθε άλλη διεργασία j, αν η
j έχει δηλώσει την πρόθεση της να επιλέξει αριθµό (έχοντας θέσει τη µεταβλητή
choosing[j] στην τιµή TRUE), η i περιµένει την j να τελειώσει µε την επιλογή του
αριθµού της. Στη συνέχεια, ελέγχει αν το εισιτήριο της j είναι µεγαλύτερο από εκείνο της
i. Αν δεν ισχύει αυτό, η i θα πρέπει να περιµένει την j να εισέλθει πριν από αυτήν στο
κρίσιµο τµήµα. Αν το εισιτήριο της i βρεθεί να είναι το µικρότερο από τα εισιτήρια των
υπολοίπων διεργασιών που ενδιαφέρονται να εισέλθουν στο κρίσιµο τµήµα τους, η i
αποφασίζει πως είναι η σειρά της να εισέλθει στο κρίσιµο τµήµα.
Όταν η i εξέλθει από το κρίσιµο τµήµα µεταβάλλει την τιµή της number[i] σε 0 για να
υποδηλώσει πως δεν εκτελεί πλέον το κρίσιµο τµήµα της.
Για να γίνει κατανοητό ότι ο παραπάνω αλγόριθµος επιτυγχάνει αµοιβαίο αποκλεισµό, ο
αναγνώστης θα πρέπει να παρατηρήσει τα εξής:
• Αν µια διεργασία i βρίσκεται στο κρίσιµο τµήµα της, ισχύει ότι (number[i] != 0).
• Αν µια διεργασία i βρίσκεται στο κρίσιµο τµήµα της ισχύει ότι (number[i],i) <
(number[j],j), για κάθε 0 ≤ j < n, i != j, τέτοιο ώστε Number[j] != 0.
Ο αλγόριθµος ικανοποιεί επίσης τη συνθήκη της προόδου. Η διεργασία µε το µικρότερο
κάθε φορά εισιτήριο δεν µπορεί να αποτραπεί από το να εισέλθει στο κρίσιµο τµήµα της.
Επίσης, διεργασίες που εκτελούν το µη-κρίσιµο τµήµα τους δεν αποτρέπουν άλλες από
το να εισέλθουν στο κρίσιµο τµήµα τους (αφού ισχύει ότι η µεταβλητή number για αυτές
είναι 0).
Τέλος, ο αλγόριθµος ικανοποιεί τη συνθήκη της αποφυγής παρατεταµένης στέρησης.
Όταν µια διεργασία i επιλέξει αριθµό, οι άλλες διεργασίες δεν µπορούν να εισέλθουν στο
κρίσιµο τµήµα τους παραπάνω από µια φορά πριν η i εισέλθει στο κρίσιµο τµήµα της.
Συνίσταται ισχυρά στον αναγνώστη να προσπαθήσει να κατανοήσει γιατί συµβαίνει αυτό
(η σηµαντική παρατήρηση είναι ότι αν µια διεργασία j πάρει εισιτήριο ενόσω µια άλλη
διεργασία i έχει number[i] != 0 τότε ισχύει number[j] > number[i]).
134
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
priority = 1; priority = 0;
flag[0] = FALSE; flag[1] = FALSE;
135
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
3.9.3 ∆ικαιοσύνη
Πιο ∆ίκαιες Λύσεις µε τη Βοήθεια του Υλικού (Παραλλαγή Μέρους Άσκησης
Αυτοαξιολόγησης 3.19)
136
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Όπως είδαµε στην Άσκηση Αυτοαξιολόγησης 9 της Ενότητας 3.4, η λύση του
προβλήµατος του αµοιβαίου αποκλεισµού µε χρήση της Test&Set() που παρουσιάστηκε
στο Σχήµα 22 µπορεί να οδηγήσει σε παρατεταµένη στέρηση. Στην ενότητα αυτή θα
χρησιµοποιήσουµε την Test&Set() για να σχεδιάσουµε λύση στο πρόβληµα του
αµοιβαίου αποκλεισµού που θα ικανοποιεί επιπρόσθετα και τη συνθήκη αποφυγής
παρατεταµένης στέρησης. Η λύση παρουσιάζεται στο Σχήµα 82.
Κοινές µεταβλητές
boolean waiting[n]; /* µε αρχική τιµή FALSE για όλα τα στοιχεία */
boolean lock; /* µε αρχική τιµή FALSE */
Κώδικας ∆ιεργασίας pi
int j; /* τοπική µεταβλητή */
boolean tmp; /* τοπική µεταβλητή */
repeat
begin
waiting[i] = TRUE;
tmp = TRUE;
while (waiting[i] == TRUE AND tmp == TRUE) do
tmp = Test&Set(lock);
waiting[i] = FALSE;
<κρίσιµο τµήµα>;
j = (i+1) mod n;
while (j != i AND (not waiting[j])) do j = (j+1) mod n;
if (j == i) then lock = FALSE;
else waiting[j] = FALSE;
<µη-κρίσιµο τµήµα>;
end
forever;
Σχήµα 82: ∆ίκαιη λύση στο πρόβληµα του αµοιβαίου αποκλεισµού µε χρήση Test&Set().
137
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
διεργασίες που βρίσκονται σε αναµονή (δηλαδή έχουν θέσει την µεταβλητή τους waiting
σε TRUE) τότε διαλέγει εκείνη τη διεργασία της οποίας το αναγνωριστικό είναι το πρώτο
που έπεται του k (ας ονοµάσουµε τη διεργασία αυτή j) και αλλάζει την τιµή της
waiting[j] σε FALSE. Αυτό έχει σαν αποτέλεσµα, την επόµενη φορά που η j θα
δροµολογηθεί, να αποτιµήσει την συνθήκη της while του κώδικα εισόδου σε FALSE και
θα εισέλθει στο κρίσιµο τµήµα της.
Άσκηση Αυτοαξιολόγησης 49
Προσθέστε σχόλια σε κάθε γραµµή ψευδοκώδικα του Σχήµατος 82. Ποιες τιµές έχουν οι
µεταβλητές που χρησιµοποιούνται από τον αλγόριθµο όταν κάποια διεργασία i βρίσκεται
στο κρίσιµο τµήµα της; Περιγράψτε τον αλγόριθµο σε πρώτο πρόσωπο από την πλευρά
της διεργασίας.
Άσκηση Αυτοαξιολόγησης 50
Περιγράψτε σωστή λύση στο πρόβληµα του αµοιβαίου αποκλεισµού που να ικανοποιεί
επιπρόσθετα και τη συνθήκη αποφυγής παρατεταµένης στέρησης, χρησιµοποιώντας την
εντολή Fetch&Add() που περιγράφτηκε στο Σχήµα 28.
Άσκηση Αυτοαξιολόγησης 51
Περιγράψτε σωστή λύση στο πρόβληµα του αµοιβαίου αποκλεισµού που να ικανοποιεί
επιπρόσθετα και τη συνθήκη αποφυγής παρατεταµένης στέρησης, χρησιµοποιώντας την
εντολή Swap() που περιγράφτηκε στο Σχήµα 29.
Άσκηση Αυτοαξιολόγησης 52
Περιγράψτε σωστή λύση στο πρόβληµα του αµοιβαίου αποκλεισµού που να ικανοποιεί
επιπρόσθετα και τη συνθήκη αποφυγής παρατεταµένης στέρησης, χρησιµοποιώντας την
εντολή RMW() που περιγράφτηκε στο Σχήµα 30.
138
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
begin begin
down(cSem); down(cSem);
rc = rc + 1; wc = wc + 1;
up(cSem); up(cSem);
down(DataBase); down(DataBase);
if (priority == 0) then if (priority == 1) then
begin begin
read_data(); wite_data();
down (cSem); down (cSem);
rc = rc - 1; wc = wc - 1;
if (wc != 0 ) priority = 1; if (rc != 0 ) priority = 0;
up(cSem); up(cSem);
end end
else else
begin begin
down(cSem); down(cSem);
if (wc == 0) priority = 0; if (rc == 0) priority = 1;
rc = rc -1; wc = wc -1;
up(cSem); up(cSem);
end end
up(DataBase); up(DataBase);
end end
Άσκηση Αυτοαξιολόγησης 53
Παρουσιάστε λύση που να αποφεύγει το πρόβληµα της παρατεταµένης στέρησης για το
πρόβληµα της στενής γέφυρας.
Άσκηση Αυτοαξιολόγησης 54
Παρουσιάστε λύση που να αποφεύγει το πρόβληµα της παρατεταµένης στέρησης για το
πρόβληµα της διάσχισης της χαράδρας.
3.9.4 Προσοµοιώσεις
Σε µια προσοµοίωση δίνεται κάποιο εργαλείο συγχρονισµού και ζητείται να
χρησιµοποιηθεί για να υλοποιήθει κάποιο άλλο εργαλείο συγχρονισµού. Η πρώτη επαφή
µε προσοµοιώσεις έγινε στις Ασκήσεις Αυτοαξιολόγησης 30 -32, όπου ζητήθηκε να
139
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
140
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
Χρειαζόµαστε εποµένως και έναν ακόµη δυαδικό σηµαφόρο, ας τον ονοµάσουµε block,
πάνω στον οποίο θα µπλοκάρουν, όταν χρειάζεται, οι διεργασίες που εκτελούν την
gsem_down().
Ας προσπαθήσουµε τώρα να περιγράψουµε µε λόγια ποιες είναι οι ενέργειες που επιτελεί
κάθε µια από τις λειτουργίες αυτές. Η περιγραφή αυτή φαίνεται στο Σχήµα 84.
∆υαδικοί Σηµαφόροι και Κοινές Μεταβλητές
semaphore mutex;
semaphore block;
int cnt = s; /* όπου s είναι η αρχική τιµή που θα θέλαµε να έχει ο γενικός σηµαφόρος */
Λειτουργία gsem_down() Λειτουργία gsem_up()
Έλεγξε την τιµή της µεταβλητής cnt; Αύξησε την τιµή της cnt κατά 1;
Μείωσε την τιµή της cnt κατά 1; Αν η τιµή της cnt ήταν ≤ 0 ενηµέρωσε τις
Αν η τιµή της cnt είναι < 0 απενεργοποιήσου; διεργασίες που επιτελούν την gsem_down()
ώστε κάποια από αυτές να µπορέσει να
επανενεργοποιηθεί;
Είναι αξιοσηµείωτο ότι η cnt χρησιµοποιείται επιπρόσθετα για να µετρά τον αριθµό των
διεργασιών που έχουν απενεργοποιηθεί πάνω στο γενικό σηµαφόρο. Η πληροφορία αυτή
παρέχεται από την τιµή |cnt| όταν cnt < 0.
141
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
up(mutex);
down(block);
στον κώδικα του gsem_down();
Εξηγήστε πως εξασφαλίζεται ο αµοιβαίος αποκλεισµός όταν τα παρακάτω ζευγάρια
διεργασιών εκτελούνται παράλληλα:
α) ∆ύο διεργασίες που καλούν ταυτόχρονα την gsem_down();
β) ∆ύο διεργασίες που καλούν ταυτόχρονα την gsem_up();
γ) ∆ύο διεργασίες που η µία καλεί την gsem_down() και η άλλη καλεί την gsem_up();
142
3ο Κεφάλαιο ∆ιαδιεργασιακή Επικοινωνία & Συγχρονισµός
S1;
…
up(mutex);
end
Σχήµα 86: Σκελετός κώδικα για την υλοποίηση κρίσιµων περιοχών υπό συνθήκη χρησιµοποιώντας
σηµαφόρους.
143