Ergastirio_Simioseis

59
Λειτουργικά Συστήματα Εργαστηριακές Ασκήσεις Λειτουργικά Συστήματα Εργαστηριακές Ασκήσεις Δρ. Παπαδάκης Στυλιανός Τεχνολογικό Εκπαιδευτικό ΄Ιδρυμα Καβάλας Τμήμα Βιομηχανικής Πληροφορικής ΄Εκδοση 1.1 1 e-mail: [email protected]

Transcript of Ergastirio_Simioseis

Page 1: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Λειτουργικά ΣυστήµαταΕργαστηριακές Ασκήσεις

∆ρ. Παπαδάκης ΣτυλιανόςΤεχνολογικό Εκπαιδευτικό ΄Ιδρυµα Καβάλας

Τµήµα Βιοµηχανικής Πληροφορικής

΄Εκδοση 1.1

1 e-mail: [email protected]

Page 2: Ergastirio_Simioseis

Περιεχόµενα

1 Οπτικά Περιβάλλοντα Προγραµµατισµού Ι 4

1.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

1.2.1 Περιγραφή της C++ Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

1.2.2 Βασικά Βήµατα ∆ηµιουργίας Project . . . . . . . . . . . . . . . . . . . . . . . 8

1.2.3 Παράδειγµα χρήσης της εφαρµογής C++ Builder . . . . . . . . . . . . . . . . 8

1.2.4 Τοποθέτηση κώδικα στο σωστό σηµείο . . . . . . . . . . . . . . . . . . . . . . 9

2 Οπτικά Περιβάλλοντα Προγραµµατισµού ΙΙ 12

2.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3 Νήµατα (Threads) 13

3.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2.1 ∆ιεργασίες και νήµατα . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2.2 ∆ηµιουργία νηµάτων . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2.3 Πέρασµα παραµέτρων σε νήµατα . . . . . . . . . . . . . . . . . . . . . . . . . 16

4 Συγχρονισµός ∆ιεργασιών και Νηµάτων 18

4.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

4.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.2.1 Σηµαφόροι (Semaphores) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.2.2 Αντικείµενα αµοιβαίου αποκλεισµού (mutexes) . . . . . . . . . . . . . . . . . 23

4.2.3 Συµβάντα (Events) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.2.4 Κρίσιµα τµήµατα (critical sections) . . . . . . . . . . . . . . . . . . . . . . . . 29

4.2.5 Συναρτήσεις αποκλειστικής πρόσβασης Interlocked. . . . . . . . . . . . . 31

4.2.6 Συναρτήσεις αναµονής (wait functions) . . . . . . . . . . . . . . . . . . . . . 31

2

Page 3: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

5 ∆ιαδιεργασιακή Επικοινωνία (Interprocess Comunnication) 34

5.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

5.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

5.2.1 Απεικόνιση Αρχείων στη Μνήµη (Memory Mapped Files) . . . . . . . . . . . . 35

6 Μυνήµατα (messages) 39

6.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.2.1 Αποστολή µυνηµάτων . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.2.2 Λήψη µυνηµάτων . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

6.2.3 ∆ιαδιεργασιακή επικοινωνία µε µυνήµατα . . . . . . . . . . . . . . . . . . . . 44

7 Αγκίστρωση µηνυµάτων 47

7.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

7.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.2.1 Εγκατάσταση γάντζων . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.2.2 Η συνάρτηση γάντζος . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

8 Αντικείµενα Αναγγελίας (Notification Objects) 53

8.1 ΄Ασκηση: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

8.2 Θεωρητικά Στοιχεία . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

9 Παράρτηµα 58

9.1 Χρήσιµες Συναρτήσεις . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

3 e-mail: [email protected]

Page 4: Ergastirio_Simioseis

Κεφάλαιο 1

Οπτικά ΠεριβάλλονταΠρογραµµατισµού Ι

Το κεφάλαιο αυτό έχει γραφτεί από τον κ. Αθηνέλλη Αλέξανδρο.

Στόχος:

• Η εξοικείωση µε το ολοκληρωµένο περιβάλλον ανάπτυξης εφαρµογών C++ Builder.

• Είσοδος - ΄Εξοδος σε οπτικά (Visual) περιβάλλοντα ανάπτυξης εφαρµογών.

• Βιβλιοθήκη Οπτικών Στοιχείων ελέγχου (Visual Components Library - VCL -) και χρήση της.

• (ActiveX controls) και σχέση (VCL-ActiveX).

• Εξοικείωση µε συµβάντα (events).

1.1 ΄Ασκηση:

Να κατασκευαστεί συντάκτης κειµένου Text Editor µε τη χρήση ενός οπτικού στοιχείου εµ-πλουτισµένου κειµένου (Rich text control) της (VCL) µε τα ακόλουθα χαρακτηριστικά:

1. Επιλογή Αποθήκευσης κειµένου σε αρχείο (Save / Save As Button).

2. Επιλογή Ανάκτησης κειµένου από αρχείο (Open Button).

3. Επιλογή Αναίρεσης µιας ενέργειας (Undo Button).

4. Επιλογή Επανάληψης µιας ενέργειας (Redo Button).

5. Επιλογή ενός τµήµατος κειµένου και αλλαγή της µορφοποίησης του π.χ. (Bold,Red).

6. TaskBar για την εµφάνιση ενηµερωτικών µυνηµάτων.

4

Page 5: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

1.2 Θεωρητικά Στοιχεία

1.2.1 Περιγραφή της C++ Builder

Για να ανοίξει η εφαρµογή C++ Builder είτε ακολουθείται το path start-> programs-> BorlandC++ Builder 6-> C++ Builder 6 είτε επιλέγεται µε διπλό αριστερό κλικ το εικονίδιο µε το όνοµαC++ Builder 6 που ϐρίσκεται στην επιφάνεια εργασίας του H/Y σας.

Μετά την επιλογή της C++ Builder εµφανίζεται το περιβάλλον εργασίας το οποίο χωρίζεται σταπαρακάτω πέντε µέρη:

1. Γραµµή εργαλείων , όπως ϕαίνεται και στο σχήµα 1.1 , η οποία περιλαµβάνει τα τµήµατα :

• Speedbar: Στην οποία παρατίθενται σειρά ενεργών-γρήγορων (one time access) κουµ-πιών που παραπέµπουν σε ενέργειες όπως αποθήκευση, άνοιγµα και compiling.

• Component Palette: Στην οποία παρατίθενται σειρά από ενεργές καρτέλες (tab)από τις οποίες γίνεται η επιλογή component όπως label, edit box, button.

Σχήµα 1.1: Γραµµή Εργαλείων

2. Στο αριστερό τµήµα της οθόνης ϐρίσκεται το παράθυρο µε το όνοµα Object Inspector, όπωςϕαίνεται και στο σχήµα 1.2, το οποίο περιέχει δύο καρτέλες (tabs). Από την καρτέλα Proper-ties, γίνεται η διαχείριση των component όπως για παράδειγµα η αλλαγή του ονόµατος του.Από την καρτέλα Events, καθορίζεται το event ( η συµπεριφορά ) του αντίστοιχου component.

3. Πάνω από το παράθυρο µε το όνοµα Object Inspector υπάρχει το παράθυρο µε το όνοµαObject TreeView, στο οποίο εµφανίζονται όλα τα component που χρησιµοποιούνται στηνεφαρµογή, όπως ϕαίνεται στο σχήµα 1.3

4. ∆εξιά από το παράθυρο Object Inspector ϐρίσκεται η επιφάνεια εργασίας της C++

Builder όπως ϕαίνεται στο σχήµα 1.4. Συνήθως η επιφάνεια εργασίας είναι µία ϕόρµα(Form Editor). Αυτή η ϕόρµα χρησιµοποιείται για την τοποθέτηση, µετακίνηση και επεξεργα-σία των components από τον χρήστη.

5. Πίσω από την ϕόρµα (Form Editor) ϐρίσκεται ο (Code Editor), όπως ϕαίνεται στο σχήµα 1.5.Στο Code Editor γράφουµε τον κώδικα του προγράµµατος.

5 e-mail: [email protected]

Page 6: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Σχήµα 1.2: Το παράθυρο Object Inspector

Σχήµα 1.3: Το παράθυρο Object TreeView

6 e-mail: [email protected]

Page 7: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Σχήµα 1.4: Το παράθυρο Form Editor

Σχήµα 1.5: Το παράθυρο Code Editor

7 e-mail: [email protected]

Page 8: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

1.2.2 Βασικά Βήµατα ∆ηµιουργίας Project

1. Πριν την εκτέλεση της εφαρµογής C++ Builder δηµιουργήστε ένα directory στον προσωπικόσας ϕάκελο.

2. Αφού ανοίξετε την εφαρµογή C++ Builder επιλέξτε από την γραµµή εργαλείων File | New

Application. Αυτό το ϐήµα γίνεται είτε όταν δεν έχει εµφανιστεί ο Form Editor κατά τοάνοιγµα της εφαρµογής, είτε αφού κλείσετε το υπάρχον Project και ϑέλετε να δηµιουργήσετεένα νέο.

3. Επιλέξτε από την γραµµή εργαλείων File | Save Project as.

4. Επιλέξτε πρώτα το directory που δηµιουργήσατε στην αρχή.

5. Αρχικά, Ϲητείται το ονοµάσετε το αρχείο µε κατάληψη .cpp,

6. Στην συνέχεια Ϲητείται να ονοµάσετε το Project

7. Μετά την ολοκλήρωση της εκάστοτε άσκησης (και αφού κάνετε συχνά save!) επιλέξτε απότην γραµµή εργαλείων Project | Build All Projects.

8. Στην συνέχεια για να τρέξει το πρόγραµµα είτε επιλέγετε από την γραµµή εργαλείων Run |

Run είτε πατήστε το πλήκτρο F9.

9. Για να κλείσετε το Project επιλέξτε από την γραµµή εργαλείων File | Close All

10. Για να κλείσετε την εφαρµογή C++ Builder επιλέξτε από την γραµµή εργαλείων File |

Exit.

11. Για να ανοίξετε ένα Project που υπάρχει, αφού ανοίξετε πρώτα την εφαρµογή C++ Builder

και κλείσετε το αρχικό Project, επιλέξτε από την γραµµή εργαλείων File | Open Project.Στην συνέχεια επιλέξτε το διρεςτορψ στο οποίο υπάρχει το Project και το αρχείο µε κατάληξη.bpr για παράδειγµα το αρχείο alex.bpr.

1.2.3 Παράδειγµα χρήσης της εφαρµογής C++ Builder

Στην συνέχεια ακολουθεί ένα απλό παράδειγµα χρήσης της εφαρµογή C++ Builder µε σκοπό τηνδηµιουργία ενός Project που ϑα εµφανίζει το µήνυµα "Operating systems 2006" στην οθόνηµετά το πάτηµα του button show_message.

∆ηµιουργία button στην επιφάνεια εργασίας

Για να τοποθετήσετε ένα button στην επιφάνεια εργασίας επιλέξτε από την γραµµή εργαλείων καιαπό το tab Standard το εικονίδιο που αντιστοιχεί στο button, όπως ϕαίνεται στο σχήµα 1.6.

8 e-mail: [email protected]

Page 9: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Σχήµα 1.6: Επιλογή Button από το Tab Standard

Στην συνέχεια επιλέξτε, µε αριστερό κλικ , το σηµείο µέσα στην επιφάνεια εργασίας (Form Editor),στο οποίο ϑέλετε να ϐάλετε το ϐυττον όπως ϕαίνεται στο σχήµα 1.7.

Σχήµα 1.7: Τοποθέτηση του Button στον Form Editor

Για να µετονοµάσετε το button1 κάνετε τα εξής :

1. Επιλέξτε το button

2. Γράψτε το κείµενο show_message στο πεδίο Caption του παραθύρου Object Inspector.

1.2.4 Τοποθέτηση κώδικα στο σωστό σηµείο

Για να εκτελεστεί ένα κοµµάτι κώδικα µετά από µία ενέργεια, για παράδειγµα να εµφανιστεί έναµήνυµα στην οθόνη µετά την επιλογή του button από τον χρήστη, πρέπει να γραφεί το αντίστοιχοκοµµάτι κώδικα στο σωστό σηµείο του προγράµµατος. Για παράδειγµα, µε διπλό αριστερό κλικ στοbutton που µόλις δηµιουργήθηκε εµφανίζεται η καρτέλα Unit1.cpp και ποιο συγκεκριµένα ησυνάρτηση, στην οποία ϑα γράψετε τον κώδικα που ϑα εκτελεστεί όταν επιλεγεί το button από τονχρήστη όπως ϕαίνεται στο σχήµα 1.8.

9 e-mail: [email protected]

Page 10: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Σχήµα 1.8: Η συνάρτηση που αντιστοιχεί στο πάτηµα του button από τον χρήστη

Αρχείο Unit1.cpp

Πριν την τοποθέτηση κάποιου Object στην επιφάνεια εργασίας (Form Editor), υπάρχει στην καρτέλαUnit1.cpp (που αντιστοιχεί στο αρχείο Unit1.cpp) η συνάρτηση ¨δηµιουργός¨ :

Listing 1.1: Η συνάρτηση δηµιουργός της ϕόρµας1 __fastcall TForm1::TForm1(TComponent∗ Owner)2 : TForm(Owner)3 4 //place here any initialization code.5

Για το παραπάνω παράδειγµα η συνάρτηση στην οποία ϑα γράψετε τον κώδικα που ϑα εµφανίζειστην οθόνη το µήνυµα "Operating Systems 2006" είναι η εξής :

Listing 1.2: Τοποθέτηση κώδικα στο Event ButtonClick1 void __fastcall TForm1::Button1Click(TObject ∗Sender)2 3 MessageBox (NULL,"Operating systems 2006","Warning", MB_OK);4

• void: Ο Κώδικας ενός Event αρχίζει πάντα µε τον τύπο της επιστρεφόµενης τιµής. Σχεδόνσε όλα τα Event στην C++ Builder η επιστρεφόµενη τιµή είναι τύπου void.

• __fastcall: ΄Ολες οι συναρτήσεις στην C++ Builder χρησιµοποιούν το χαρακτηριστικό__fastcall.

• TForm1: Το όνοµα της parent class από όπου ϑα τρέξει το Event.

10 e-mail: [email protected]

Page 11: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

• ::Button1Click: Μετά το όνοµα της parent class ο compiler χρησιµοποιεί τον ( :: )class member access operator για να καλέσει το Event, που στην περίπτωση µας είναι τοButton1Click.

• TObject *Sender: Κάθε Event δέχεται τουλάχιστον ένα όρισµα το TObject *Sender.

Αρχείο Unit1.h

Στην καρτέλα Unit1.h ( που αντιστοιχεί στο αρχείο Unit1.h ) όπως ϕαίνεται στο σχήµα 1.9δηλώνουµε τις global µεταβλητές και γράφουµε τις δικές µας συναρτήσεις, όχι µέσα στην κλάσηclass TForm1 εκτός και αν χρειαστεί.

Σχήµα 1.9: Η καρτέλα που εµφανίζει το αρχείο Unit1.h

11 e-mail: [email protected]

Page 12: Ergastirio_Simioseis

Κεφάλαιο 2

Οπτικά ΠεριβάλλονταΠρογραµµατισµού ΙΙ

Στόχος:

• Ολοκλήρωση του επεξεργαστή κειµένου και κατασκευή ϐοηθητικών συναρτήσεων για (inplace

spell-checking).

2.1 ΄Ασκηση:

1. Να κατασκευαστεί ο κώδικας αποµόνωσης µιας νέας λέξης που δηµιουργείται κατά την πλη-κτρολόγηση του κειµένου από τον χρήστη.

2. Να κατασκευαστεί συνάρτηση void LoadDictionary(std::string filename), τοοποίο να διαβάζει από αρχείο κειµένου λεξικό µε αγγλικές και ελληνικές λέξεις και το απο-ϑηκεύει στη µνήµη.

3. Να κατασκευαστεί συνάρτηση void spell(NEW_WORD w) που ϑα παίρνει ως όρισµα µιαδοµή τύπου NEW_WORD και ϑα ελέγχει την ορθότητα της λέξης που δηµιουργήθηκε.

4. Να κατασκευαστεί συνάρτηση void mark(NEW_WORD w) η οποία να αλλάζει το χρώµαµιας λέξης που είναι λανθασµένη στο κείµενο.

5. Εισαγωγή στον πολυπρογραµµατισµό.

Listing 2.1: Η ∆οµή NEW_WORD1 struct NEW_WORD2 3 std::string word;4 int start;5 int length;6

12

Page 13: Ergastirio_Simioseis

Κεφάλαιο 3

Νήµατα (Threads)

Στόχος:

• Εισαγωγή στον πολυπρογραµµατισµό.

• ∆ηµιουργία νηµάτων (Threads).

• Πέρασµα παραµέτρων σε νήµατα.

3.1 ΄Ασκηση:

1. Οι συναρτήσεις LoadDictionary και spell να µετατραπούν σε νηµατοσυναρτήσεις.

2. Να δηµιουργείται ένα νήµα για κάθε λέξη που να ασχολείται µε την διόρθωση µιας νέας λέξηςκαι την αλλαγή του χρώµατος της στην περίπτωση που η λέξη είναι λανθασµένη.

3. Να συγχρονιστούν όλα τα νήµατα των λέξεων ώστε να περιµένουν την ολοκλήρωση της ϕόρ-τωσης του λεξικού.

4. Να συγχρονιστούν τα νήµατα των λέξεων µεταξύ τους ώστε το ένα να περιµένει το άλλο µεστόχο να µαρκάρει ένα νήµα κάθε ϕορά τη λέξη του στον επεξεργαστή κειµένου.

13

Page 14: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

3.2 Θεωρητικά Στοιχεία

3.2.1 ∆ιεργασίες και νήµατα

Το λειτουργικό σύστηµα windows είναι νηµατοκεντρικό (thread oriented). Αυτό σηµαίνει ότι η ϐα-σική µονάδα εκτέλεσης είναι τα νήµατα. ΄Ενα νήµα, ϑεωρείται ως µια ελαφρά διεργασία (lightweight

process). Κάθε ϕορά που δηµιουργείται µια διεργασία, δηµιουργείται αυτόµατα ένα νήµα, το οποίοονοµάζεται πρωτεύον νήµα (primary thread). Συνεπώς, µια διεργασία περιλαµβάνει τουλάχιστονένα νήµα εκτέλεσης (το πρωτεύον νήµα) και ϕυσικά µπορεί να περιλαµβάνει περισσότερα νήµατα,τα οποία λαµβάνουν χρόνο από το χρονοπρογραµµατιστή ανεξάρτητα µεταξύ τους.

Τα νήµατα µιας διεργασίας µοιράζονται τον ίδιο εικονικό χώρο διευθύνσεων (address space) µετην διεργασία. Αυτό πρακτικά σηµαίνει ότι έχουν πρόσβαση σε όλες τις παγκόσµιες µεταβλητές καιπόρους της διεργασίας. ΄Ενα νήµα µπορεί να δηµιουργηθεί από ένα άλλο νήµα και να δηµιουργήσειαλλά νήµατα, όπως επίσης και µια διεργασία µπορεί να δηµιουργηθεί είτε από µια άλλη διεργασίαείτε από ένα άλλο νήµα. Στο λειτουργικό σύστηµα windows κάθε διεργασία είναι ανεξάρτητηκαι δεν υπάρχει η σχέση γονέα παιδιού όπως σε άλλα λειτουργικά συστήµατα π.χ Linux.Αν έναοποιοδήποτε νήµα µιας διεργασίας δεν έχει τερµατιστεί τότε η διεργασία παραµένει ενεργή. Μιαδιεργασία τερµατίζεται όταν τερµατιστούν όλα τα νήµατα της.

Η ϐασική διαφορά διεργασίας και νήµατος είναι ότι τα νήµατα δεν διαθέτουν δικό τους αυτοδύναµοεικονικό χώρο διευθύνσεων αλλά µοιράζονται αυτό της διεργασίας στην οποία ανήκουν. αυτότους δίνει το πλεονέκτηµα της ταχύτητας όσον αφορά τη δηµιουργία και τον τερµατισµό τους. Σεαντίθεση µε τα νήµατα, κάθε διεργασία έχει το δικό της ατοµικό εικονικό χώρο διευθύνσεων, οοποίος δηµιουργείται µε τη δηµιουργία της διεργασίας και καταστρέφεται µε τον τερµατισµό της.

Ο αριθµός αυτός αναφέρεται συχνά και ως χειριστήριο η λαβή (handle).

Κάθε ϕορά που δηµιουργείται µια διεργασία η ένα νήµα το λειτουργικό σύστηµα δίνει σε αυτόµια ταυτότητα, που είναι ένας ακέραιος αριθµός. Η ταυτότητα του νήµατος µπορεί να τεθεί ωςόρισµα σε διάφορες συναρτήσεις µε τις οποίες, µπορούµε να επιτελέσουµε διάφορες λειτουργίεςστα νήµατα η τις διεργασίες. Επίσης, τα νήµατα είναι αδιαφανή αντικείµενα (opaque objects). Αυτόσηµαίνει ότι δεν µπορούν εκτελεστούν λειτουργίες απευθείας στα νήµατα αλλά µόνο µέσω κλήσεωνσυστήµατος, οι οποίες παρέχονται από το λειτουργικό σύστηµα υπό τη µορφή συναρτήσεων API. Οισυναρτήσεις αυτές αναφέρονται στο νήµα µέσω µιας µεταβλητής τύπου HANDLE που ϑα την ονοµά-Ϲουµε λαβή. Μια λαβή σε µια διαδικασία ή ένα νήµα µπορεί να ληφθεί µε τη συνάρτηση: HANDLEGetCurrentProcess(VOID) και HANDLE GetCurrentThread(VOID), αντίστοιχα.

Μια λειτουργία που µπορούν να εκτελέσουν είναι ο τερµατισµός της διεργασίας η του νήµατος, µετις συναρτήσεις : BOOL TerminateProcess(HANDLE hProcess,UINT uExitCode); καιBOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); αντίστοιχα.

3.2.2 ∆ηµιουργία νηµάτων

Στο λειτουργικό σύστηµα windows τα νήµατα χωρίζονται γενικά σε δύο κατηγορίες : Στα νήµαταεργάτες (worker threads) και στα νήµατα διεπαφής µε το χρήστη (user interface threads). ΄Ενανήµα διεπαφής µε το χρήστη διαφέρει από ένα νήµα εργάτη στο ότι διαθέτει αντλία µηνυµάτων(message pump) και έχει τη δυνατότητα να αποκρίνεται σε µηνύµατα. ΄Ολα τα πρωτεύοντα νήµατα

14 e-mail: [email protected]

Page 15: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

των διεργασιών σε περιβάλλον windows που διαθέτουν περιβάλλον διεπαφής (user interface) είναινήµατα διεπαφής. ΄Ενα νήµα δηµιουργείται1 µε κλήση της συνάρτησης :

Listing 3.1: Η συνάρτηση δηµιουργίας ενός νήµατος1 HANDLE CreateThread(2 LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes3 DWORD dwStackSize, // initial thread stack size, in bytes4 LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function5 LPVOID lpParameter, // argument for new thread6 DWORD dwCreationFlags, // creation flags7 LPDWORD lpThreadId // pointer to returned thread identifier8 ) ;

Παράµετροι

• lpThreadAttributes: Η παράµετρος αυτή είναι δείκτης σε µια δοµή τύπου LPSE

CURITY_ATTRIBUTES και καθορίζει το αν η λαβή που επιστρέφεται από τη συνάρτηση ϑαείναι κληρονοµήσιµη από διεργασίες που δηµιουργούνται από το νήµα αυτό. Αν η τιµή τηςπαραµέτρου αυτής είναι NULL η λαβή δεν µπορεί να κληρονοµηθεί.

• dwStackSize: Καθορίζει το αρχικό µέγεθος της στοίβας του νήµατος που δηµιουργείται.Αν η παράµετρος αυτή έχει την τιµή 0 τότε το µέγεθος της στοίβας του είναι ίδιο µε τοµέγεθος της στοίβας της διεργασίες που το δηµιούργησε. Να σηµειωθεί στο σηµείο αυτό ότιη δέσµευση µνήµης για την στοίβα γίνεται αυτόµατα και το µέγεθος της αυξάνεται επίσηςαυτόµατα αν αυτό είναι απαραίτητο. Η αποδέσµευση της µνήµης που καταλαµβάνει η στοίβαγίνεται αυτόµατα µε την καταστροφή του νήµατος.

• lpStartAddress: Η παράµετρος αυτή είναι ένας δείκτης σε συνάρτηση, ο κώδικας τηςοποίας ϑα εκτελεσθεί ως νήµα. Η συνάρτηση αυτή ϑα ονοµάζεται νηµατοσυνάρτηση καιπρέπει να έχει τη µορφή

1 DWORD WINAPI ThreadFunc( void∗ );

Παρατηρούµε ότι µια νηµατοσυνάρτηση είναι δυνατόν να πάρει µε µόνο ένα όρισµα τύπουvoid*. ∆ηµιουργείται λοιπόν το εύλογο ερώτηµα πώς είναι δυνατόν να περάσουµε περισ-σότερες της µια παράµερους σε νηµατοσυνάρτηση. Η απάντηση στο ερώτηµα αυτό ϑα µαςαπασχολήσει στην επόµενη ενότητα.

• lpParameter: ∆είκτης τύπου void*, ο οποίος περνάει στη νηµατοσυνάρτηση. Η χρησι-µότητα αυτού του δείκτη είναι στο πέρασµα παραµέτρων.

• dwCreationFlags: Καθορίζει τον τρόπο µε τον οποίο ϑα δηµιουργηθεί το νήµα. Ηπαράµετρος αυτή µπορεί να λάβει δύο τιµές α) CREATE_SUSPENDED που σηµαίνει ότι τονήµα ϑα δηµιουργηθεί αλλά η εκτέλεση του ϑα αναβληθεί. ϐ) µηδέν σηµαίνει ότι το νήµαϑα δηµιουργηθεί και η εκτέλεσή του ϑα αρχίσει άµεσα. Στην περίπτωση που ένα νήµαδηµιουργείται µε την παράµετρο CREATE_SUSPENDED, για να αρχίσει την εκτέλεση του ϑαπρέπει να κληθεί η συνάρτηση ResumeThread µε όρισµα τη λαβή του νήµατος.

1εκτός από το πρωτεύον νήµα που δηµιουργείται αυτόµατα

15 e-mail: [email protected]

Page 16: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

• lpThreadId: Στην παράµετρο αυτή αποθηκεύεται η ταυτότητα του νήµατος που δηµιουρ-γείται.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτηση είναι επιτυχής η επιστρεφόµενη τιµή είναι µια λαβή στο νήµα το οποίοδηµιουργήθηκε. Αν η κλήση της ανάρτησης αποτύχει η επιστρεφόµενη τιµή είναι NULL.

3.2.3 Πέρασµα παραµέτρων σε νήµατα

΄Οπως έχει προαναφερθεί µια νηµατοσυνάρτηση λαµβάνει µόνο ένα όρισµα που είναι δείκτης τύ-που void. Στην περίπτωση που ϑέλουµε να περάσουν µόνο µία παράµετρο στη νηµατοσυνάρτησηαυτό είναι εφικτό µέσω του µοναδικού διαθέσιµου ορίσµατος. Επίσης, αν ϑέλουµε να περάσουµεδεδοµένα ίδιου τύπου π.χ. ακέραιους αριθµούς αυτό είναι εφικτό µε τον ίδιο τρόπο. Μπορούµεδηλαδή να αποθηκεύσουµε τους ακέραιος σε ένα πίνακα και να περάσουµε την αρχική διεύθυνσητου πίνακα. Τι γίνεται όµως στην περίπτωση που ϑέλουµε να περάσουµε πολλά και ετερόκληταδεδοµένα π.χ. µια παράµετρο τύπου ακεραίου, µια παράµετρο τύπου χαρακτήρα µια παράµετροτύπου αριθµό κινητής υποδιαστολής κ.τ.λ. ΄Εχουµε δύο επιλογές : Μπορούµε να εκµεταλλευτούµετο γεγονός ότι ένα νήµα µοιράζεται τον ίδιο χώρο εικονικών διευθύνσεων µε τη διεργασία, πουσηµαίνει ότι έχει πρόσβαση στις παγκόσµιες µεταβλητές της διεργασίας. Αν χρησιµοποιήσουµεεποµένως παγκόσµιες µεταβλητές για τις παραµέτρους που ϑέλουµε να χρησιµοποιήσει η νηµα-τοσυνάρτηση, τότε υπάρχει απευθείας πρόσβαση σε αυτές. Αυτός ο τρόπος ϐέβαια δεν είναι τόσοκοµψός και λειτουργικός, ιδιαίτερα στις περιπτώσεις όπου υπάρχουν πολλά νήµατα και πολλέςµεταβλητές. Μπορούµε λοιπόν να χρησιµοποιήσουµε µια άλλη απλούστατη τεχνική. Να δηµιουρ-γήσουµε µια δοµή, της οποίας µέλη να είναι οι µεταβλητές που ϑέλουµε να χρησιµοποιηθούν απότην νηµατοσυνάρτηση. Ας υποθέσουµε, για παράδειγµα ότι ϑέλουµε να περάσουµε δύο ακέραιους,ένα αριθµό τύπου float και ένα αντικείµενο τύπου string. ∆ηµιουργείται η ακόλουθη δοµή:

Listing 3.2: ∆οµή που περιλαµβάνει τους τύπους δεδοµένων που ϑέλουµε να περάσουµε στηνηµατοσυνάρτηση

1 struct MYDATA2 int x1;3 int x2;4 float f ;5 std:string strword;6

έστω ότι ϑέλουµε να περάσουµε τις συγκεκριµένες τιµές

1 x1=5; x2=7; f=3.2; strword="test";

και να τις εκτυπώσουµε από τη νηµατοσυνάρτηση. Ο σχετικός κώδικας δίνεται στο Listing 3.3 .

Listing 3.3: ∆οµή που περιλαµβάνει τους τύπους δεδοµένων που ϑέλουµε να περάσουµε στηνηµατοσυνάρτηση

1 #include <iostream.h>2 #include <string>

16 e-mail: [email protected]

Page 17: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

3 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−4 //The data structure5 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−6 struct MYDATA7 int x1;8 int x2;9 float f ;

10 std:string strword;11 12 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−13 //The thread function14 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−15 DWORD WINAPI mythread(void∗ p)16 17 MYDATA ∗pd=(MYDATA∗)p;18 cout<<pd−>x1<<endl<<pd−>x2<<endl<<pd−>f<<endl<<pd−>strword.c_str()<<endl;19 delete(pd);20 21 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−22 int main()23 24 MYDATA ∗pdata=new MYDATA;25 pdata−>x1=5;26 pdata−>x2=7;27 pdata−>f=3.2;28 pdata−>strword="test";29 DWORD dwThreadId=0;30 HANDLE h=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)mythread,pdata,0,&dwThreadId);31 WaitForSingleObject(h,INFINITE);32 return 0;33

Προσέξτε την εντολή delete(pd) στη γραµµή 19 του Listing 3.3 και εξηγήστε γιατί η αποδέσµευ-ση µνήµης πρέπει να γίνει εκεί και όχι για παράδειγµα µετα τη γραµµή 30 του κώδικα ; Επίσηςγιατί δεν γίνεται δέσµευση µνήµης µέσα στη νηµατοσυνάρτηση π.χ πρίν τη γραµµή 17 αλλά γίνεταιστην main;

΄Ενα νήµα µπορεί να αυτοκτονήσει κάνοντας χρήση συνάρτησης1 VOID ExitThread(DWORD dwExitCode);

η να δολοφονηθεί µε τη χρήση της συνάρτησης1 BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

Η συνάρτηση TerminateThread τερµατίζει ϐίαια και άµεσα το νήµα και πρέπει να χρησιµο-ποιείται µε ϕειδώ. Αν π.χ το νήµα ϐρίσκεται σε κρίσιµο τµήµα τότε αυτό δεν ϑα ελευθερωθεί ποτέ.Επίσης, αν το νήµα εκτελεί κλήσεις πυρήνα και τερµατίσει ϐίαια, ολόκληρη η διεργασία µπορεί ναοδηγηθεί σε αστάθεια. Τέλος, όταν το νήµα τερµατίζει ϕυσιολογικά τη λειτουργία του η αυτοκτονείη µνήµη της στοίβας του αποδεσµεύεται. ΄Οταν όµως δολοφονείται η µνήµη παραµένει δεσµευµένηµέχρι τον τερµατισµό της διεργασίας οπότε και ελευθερώνεται όλος ο χώρος εικονικών διευθύνσεωντης διεργασίας.

17 e-mail: [email protected]

Page 18: Ergastirio_Simioseis

Κεφάλαιο 4

Συγχρονισµός ∆ιεργασιών και Νηµάτων

Στόχος:

• Εισαγωγή στον συγχρονισµό διεργασιών και νηµάτων.

• Σηµαφόροι (semaphores), δυαδικοί σηµαφόροι (mutexes), και τρόπος υλοποίησης τους σεπεριβάλλον win32.

• Υλοποίηση του µοντέλου παραγωγού - καταναλωτή.

• Κρίσιµα τµήµατα (critical sections).

• Φράγµατα (barriers).

4.1 ΄Ασκηση:

Να υλοποιηθεί το µοντέλο παραγωγού - καταναλωτή (για την ακρίβεια το µοντέλο παραγωγών κατα-ναλωτή) µέσω του οποίου να επιτελείται η διόρθωση των λέξεων των προηγούµενων εργαστηριακώνασκήσεων. Συγκεκριµένα:

1. Να δηµιουργηθεί µια νηµατοσυνάρτηση void consumer(void *p), η οποία να λαµβάνειµια λέξη από ένα κοινόχρηστο πίνακα Ν-Θέσεων, στον οποίο αποθηκεύονται λέξεις προςδιόρθωση να την διορθώνει και στην περίπτωση που είναι λανθασµένη να τη µαρκάρει µεκόκκινο χρώµα στο πλαίσιο της επεξεργασίας κειµένου. Αν δεν έχουν αποθηκευτεί λέξειςπρος διόρθωση στον πίνακα τότε να περιµένει µέχρι να εµφανιστούν.

2. Να δηµιουργηθεί µια νηµατοσυνάρτηση void producer(void *p), η οποία να εισάγειµια λέξη στον κοινόχρηστο πίνακα λέξεων εφ΄ όσον ο πίνακας δεν είναι γεµάτος. Αν ο πίνα-κας είναι γεµάτος το νήµα ϑα πρέπει να περιµένει µέχρι να αδειάσει κάποια ϑέση από τονκαταναλωτή.

3. Για κάθε λέξη που παράγεται κατά την πληκτρολόγηση του κειµένου να δηµιουργείται ένανήµα το οποίο να ασχολείται µε την εισαγωγή της στον κοινόχρηστο πίνακα των λέξεων.

18

Page 19: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

4.2 Θεωρητικά Στοιχεία

Στην περίπτωση που περισσότερες της µια διεργασίες χρειάζονται τη χρήση του ίδιου πόρου απαι-τείται ο συγχρονισµός τους ώστε να αποφεύγεται η ταυτόχρονη χρήση του. Για το σκοπό αυτόχρησιµοποιούνται τα αντικείµενα συγχρονισµού, τα ϐασικότερα από τα οποία είναι :

• Semaphores: Χρησιµοποιούνται για να περιοριστεί ο µέγιστος αριθµός νηµάτων πουµπορεί να έχουν ταυτόχρονη πρόσβαση σε ένα κοινόχρηστο πόρο.

• Mutexes: Χρησιµοποιούνται όταν ϑέλουµε να εξασφαλίσουµε ότι µόνο µια διεργασία ϑαέχει πρόσβαση κάθε ϕορά σε ένα κοινόχρηστο πόρο.

• Events: Περιέρχονται σε σηµατοδοτηµένη και µη σηµατοδοτηµένη κατάσταση και χειρω-νακτικά.

• Critical Sections: Χρησιµοποιούνται για το συχρονισµό νηµάτων της ίδιας διεργα-σίας. Το πλεονέκτηµα τους είναι η ταχύτητα στη δηµιουργία και την καταστροφή τους. Τοµειονέκτηµα τους ότι δεν µπορούν να χρησιµοποιηθούν για να συχρονιστούν νήµατα διαφο-ϱετικών διεργασιών.

4.2.1 Σηµαφόροι (Semaphores)

΄Ενα αντικείµενο σηµαφόρων είναι ένα αντικείµενο συγχρονισµού που λειτουργεί σαν µια ακέραιατιµή µεταξύ του µηδενός και µιας προκαθορισµένης µέγιστης τιµής. Η τιµή του µειώνεται κατάµία µονάδα κάθε ϕορά που επιδρά πάνω του µια λειτουργία αναµονής (down), και αυξάνεται κατάµία µονάδα κάθε ϕορά που επιδρά πάνω του µια λειτουργία αφύπνισης (up). ΄Οταν η αρίθµησητου σηµαφόρου ϕτάσει στο µηδέν, οπότε η κατάσταση του σηµαφόρου µεταβαίνει σε µη σηµατοδο-τούµενη (non-signaled) η εφαρµογή µιας λειτουργίας αναµονής έχει ως αποτέλεσµα την αναστολήτης εκτέλεσης του νήµατος που καλεί τη συνάρτηση αναµονής µέχρι κάποιο άλλο νήµα να καλέσειµια συνάρτηση αφύπνισης του σηµαφόρου και να τον µεταφέρει σε κατάσταση σηµατοδοτούµενη(signaled).

Τα αντικείµενα σηµαφόρων χρησιµεύουν στην υλοποίηση του συγχρονισµού µεταξύ διεργασιών.΄Ενα παράδειγµα όπου απαιτείται συγχρονισµός µεταξύ διεργασιών είναι η περίπτωση εµφάνισηςσυνθηκών ανταγωνισµού για τη χρήση ενός κοινόχρηστου πόρου. ΄Ενα αντικείµενο σηµαφόρουενεργεί ως πύλη που περιορίζει τον αριθµό νηµάτων που χρησιµοποιούν ταυτόχρονα ένα πόρο σεέναν προκαθορισµένο µέγιστο αριθµό.

Πριν από τη χρήση του πόρου από διάφορα νήµατα η λαβή του σηµαφόρου χρησιµοποιείται ωςόρισµα σε µια συνάρτηση αναµονής. ΄Οταν η συνάρτηση αναµονής επιστρέφει, η αρίθµηση τουσηµατοφόρου µειώνεται κατά µια µονάδα. ΄Οταν το νήµα ολοκληρώσει µε τη χρήση του πόρου,καλεί µια λειτουργία αφύπνισης και αυξάνει την αρίθµηση του σηµατοφόρου κατά µια µονάδα. Ανη αρίθµηση ϕτάσει στο µηδέν από τις κλήσεις στις συναρτήσεις αναµονής, τα υπόλοιπα νήµατα πουδεν έχουν προλάβει να χρησιµοποιήσουν τον πόρο αναστέλλουν τη λειτουργία τους µέχρι κάποιονήµα να ολοκληρώσει τη χρήση του πόρου και να καλέσει µια συνάρτηση αφύπνισης.

Π.χ. µια εφαρµογή µπορεί να ϑέσει ένα όριο στον αριθµό των αρχείων που µπορεί να είναι ανοικτάταυτόχρονα. Η εφαρµογή µπορεί να χρησιµοποιήσει ένα σηµαφόρο µε µια µέγιστη αρίθµηση

19 e-mail: [email protected]

Page 20: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

ίση µε το µέγιστο αριθµό των αρχείων. Κάθε ϕορά που δηµιουργείται ένα αρχείο η τιµή τουσηµαφόρου µειώνεται ενώ κάθε ϕορά που κλείνει ένα αρχείο η τιµή του σηµαφόρου αυξάνεται. Ηεφαρµογή εφαρµόζει µια κλήση αναµονής στο σηµαφόρο πριν από το άνοιγµα ενός αρχείου. ΄Οταν ηαρίθµηση ϕτάσει στο µηδέν δείχνοντας ότι το όριο του µέγιστου αριθµού των αρχείων έχει επιτευχθεί,η λειτουργία αναµονής αναστέλλει την εκτέλεση του κώδικα ανοίγµατος νέου αρχείου µέχρι νακληθεί ο κώδικας που κλείνει κάποιο αρχείο µετά από τον οποίο καλείται η συνάρτηση αφύπνισηςτου σηµαφόρου που ϑα τον µεταφέρει σε σηµατοδοτούµενη κατάσταση. Ιδιαίτερη έµφαση πρέπεινα δοθεί στα ακόλουθα:

• Προσοχή χρειάζεται στην περίπτωση που το νήµα που καλεί τη συνάρτηση αναµονής είναι τοπρωτεύον νήµα (primary thread) διότι η ενδεχόµενη αναστολή του έχει ως αποτέλεσµα καιτην αναστολή όλων των νηµάτων που δηµιουργήθηκαν από αυτό. Συνεπώς αν το πρωτεύοννήµα περιµένει αφύπνιση από κάποιο δευτερεύων νήµα που δηµιούργησε, αυτή δε ϑα συµβείποτέ.

• ΄Οταν δηµιουργείται ο σηµαφόρος, η τιµή του µπορεί να προκαθοριστεί σε οποιοδήποτε ακέ-ϱαιο αριθµό. Μετά από αυτό οι µόνες διαδικασίες που είναι δυνατό να εκτελεστούν είναι είτεη αύξηση του έως µια προκαθορισµένη µέγιστη τιµή που καθορίζεται κατά τη δηµιουργία τουείτε η ελάττωση του έως το µηδέν.

• ∆εν µπορείτε να διαβάσετε την τιµή του σηµαφόρου.

• ΄Οταν ένα νήµα προσπαθεί να µειώσει την τιµή ενός σηµαφόρου, η οποία είναι ήδη µηδενική,το νήµα αναστέλλει την εκτέλεση του µέχρι η τιµή του σηµαφόρου να αυξηθεί και να γίνειµεγαλύτερη του µηδενός.

Υλοποίηση του σηµαφόρου σε περιβάλλον win32

Η λειτουργία down(s), που έχουµε δει στη ϑεωρία, για ένα σηµαφόρο s υλοποιείται µε τηνσυνάρτηση WaitForSingleObject(h_s,.), όπου h_s µια λαβή (handle) στον σηµαφόρο s,όπως ϑα δούµε αναλυτικά παρακάτω. (για τις συναρτήσεις αναµονής δες παράγραφο 4.2.6 σελ.31)

Η λειτουργία up(s), υλοποιείται µε την συνάρτηση ReleaseSemaphore(h_s).

΄Ενας σηµαφόρος δηµιουργείται µε τη συνάρτηση CreateSemaphore,

Listing 4.1: Η συνάρτηση δηµιουργίας ενός σηµαφόρου1 HANDLE CreateSemaphore(2 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,// pointer to security attributes3 LONG lInitialCount, // initial count4 LONG lMaximumCount, // maximum count5 LPCTSTR lpName // pointer to semaphore−object name6 ) ;

Παράµετροι

• lpSemaphoreAttributes: ∆είκτης σε µια δοµή SECURITY_ATTRIBUTES που καθο-ϱίζει εάν η επιστρεφόµενη λαβή µπορεί να κληρονοµηθεί. Εάν lpSemaphoreAttributes

20 e-mail: [email protected]

Page 21: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

είναι NULL, η λαβή δεν µπορεί να κληρονοµηθεί.

• lInitialCount: Καθορίζει τη µέγιστη αρχική τιµή για το αντικείµενο σηµαφόρων. Αυτήη τιµή πρέπει να είναι µεγαλύτερη ή ίση του µηδενός και µικρότερη ή ίση της lMaximumCount.Η αρίθµηση µειώνεται από µια οποιαδήποτε λειτουργία αναµονής απελευθερώνει ένα νήµαπου περίµενε το σηµαφόρο. Η αρίθµηση αυξάνεται κατά ένα διευκρινισµένο ποσό µε τηνκλήση της λειτουργίας ReleaseSemaphore.

• lMaximumCount: Καθορίζει τη µέγιστη τιµή που µπορεί να λάβει ένα αντικείµενο σηµα-ϕόρου. Αυτή η τιµή πρέπει να είναι µεγαλύτερη από µηδέν.

• lpName: ∆είκτης σε µια συµβολοσειρά (string) που καθορίζει το όνοµα του αντικειµένουσηµαφόρων. Το µεγιστο µήκος του ονόµατος πρέπει να είναι MAX_PATH, και µπορεί ναπεριέχει οποιοδήποτε χαρακτήρα εκτός από τους χαρακτήρες σύγκρισης και αντικάθετου(\).

Εάν lpName είναι ίδιο µε το όνοµα ενός ήδη υπάρχοντος αντικειµένου σηµαφόρου, ϑα πρέπεινα υπάρχει SEMAPHORE_ALL_ACCESS στο υπάρχον αντικείµενο. Σε αυτήν την περίπτωση,οι παράµετροι lInitialCount και lMaximumCount αγνοούνται επειδή έχουν ήδη απο-κτήσει τιµή µε τη προηγούµενη κλήση που δηµιούργησε το αντικείµενο.

Εάν lpName==NULL, το αντικείµενο σηµαφόρων δηµιουργείται χωρίς όνοµα. Εάν lpNameείναι το όνοµα ενός υπάρχοντος αντικειµένου αµοιβαίου αποκλεισµού, (mutex,) ή δηµιουργίατου αντικειµένου αποτυγχάνει και η συνάρτηση GetLastError επιστρέφει ERROR_INVALID_HANDLE. Αυτό συµβαίνει διότι το mutex, και ο σηµαφόρος, µοιράζονται τον ίδιο ονο-µατοχώρο.

Επιστρεφόµενη τιµή

Εάν η δηµιουργία του σηµαφόρου είναι επιτυχής, η επιστρεφόµενη τιµή είναι µια λαβή στο αντικεί-µενο σηµαφόρου που δηµιουργήθηκε. Εάν το επώνυµο (lpName) αντικείµενο σηµαφόρου υπάρχειπριν από την κλήση της συνάρτησης, η GetLastError επιστρέφει ERROR_ALREADY_EXISTS.∆ιαφορετικά η GetLastError επιστρέφει NULL. Εάν η δηµιουργία του σηµαφόρου αποτύχει,η επιστρεφόµενη τιµή είναι NULL. Για τη λήψη λεπτοµερέστερων πληροφοριών λάθους, πρέπει νακληθεί η GetLastError.

Η συνάρτηση GetLastError επιστρέφει ένα ακέραιο που είναι ένας κωδικός σφάλµατος1.

Οποιοδήποτε νήµα της διεργασίας που δηµιούργησε το σηµαφόρο µπορεί να χρησιµοποιήσει τηλαβή του σηµαφόρου ως όρισµα σε συναρτήσεις αναµονής / αφύπνισης. Νήµατα άλλων διεργα-σιών, τα οποία δε µοιράζονται τον ίδιο χώρο διευθύνσεων µε τη διεργασία που δηµιούργησε τοσηµαφόρο, µπορούν να αποκτήσουν λαβή στο σηµαφόρο χρησιµοποιώντας το όνοµα του ως όρισµαστη συνάρτηση OpenSemaphore. Η σύνταξη της συνάρτησης αυτής παρουσιάζεται παρακάτω:

1Η µετάφραση αυτού του κωδικού σε κείµενο περιγραφής του σφάλµατος παρουσιάζεται στον κώδικα Listing 9.1 (σελ.58)

21 e-mail: [email protected]

Page 22: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 4.2: Η συνάρτηση OpenSemaphore

1 HANDLE OpenSemaphore(2 DWORD dwDesiredAccess, // access flag3 BOOL bInheritHandle, // inherit flag4 LPCTSTR lpName // pointer to semaphore−object name5 ) ;

Παράµετροι

• dwDesiredAccess: Καθορίζει τα δικαιώµατα πρόσβασης στο αντικείµενο του σηµαφόρουπου πρόκειται να ανοικτεί. Η παράµετρος αυτή µπορεί να λάβει µια ή συνδυασµό από τιςακόλουθες τιµές :

1. SEMAPHORE_ALL_ACCESS: Καθορίζει όλα τα δυνατά δικαιώµατα πρόσβασης για το αν-τικείµενο σηµατοφόρων.

2. SEMAPHORE_MODIFY_STATE: ∆ικαίωµα τροποποίησης της αρίθµησης του αντικειµένουτου σηµαφόρου από συναρτήσεις αφύπνυσης ReleaseSemaphore.

3. SYNCHRONIZE: ∆ικαίωµα τροποποίησης της αρίθµησης του αντικειµένου του σηµαφό-ϱου από συναρτήσεις αναµονής WaitForSingleObject.

• bInheritHandle: Καθορίζει εάν η επιστρεφόµενη λαβή µπορεί να κληρονοµηθεί απόδιαδικασίες που δηµιουργούνται µέσω της συνάρτησης CreateProcess από την διαδικασίαπου ανοίγει το αντικείµενο του σηµαφόρου. Αν η τιµή αυτή είναι TRUE τότε η λαβή µπορεί νακληρονοµηθεί στην αντίθετη περίπτωση, αν bInheritHandle==TRUE η λαβή δεν µπορείνα κληρονοµηθεί.

• lpName: ∆είκτης σε µια συµβολοσειρά (string) που καθορίζει το όνοµα του σηµαφόρου πουπρόκειται να ανοικτεί. Προσοχή ! Γίνεται διάκριση µεταξύ πεζών και κεφαλαίων.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής η επιστρεφόµενη τιµή είναι µια λαβή (handle) στοαντικείµενο του σηµαφόρου µε όνοµα lpName, και µπορεί να χρησιµοποιηθεί σε συναρτήσειςαφύπνισης / αναµονής.

΄Οταν η λαβή δεν είναι πλέον χρήσιµη τότε πρέπει να κληθεί η συνάρτηση CloseHandle µε όρισµατη λαβή για να απελευθερωθούν οι πόροι που δεσµεύονται µε την κλήση της OpenSemaphore.

Η συνάρτηση ReleaseSemaphore, η οποία αυξάνει την τιµή του σηµαφόρου (είναι το ανάλογοτης up(s) όπως έχει ήδη αναφερθεί), παρουσιάζεται παρακάτω:

Listing 4.3: Η συνάρτηση ReleaseSemaphore

1 BOOL ReleaseSemaphore(2 HANDLE hSemaphore, // handle of the semaphore object3 LONG lReleaseCount, // amount to add to current count4 LPLONG lpPreviousCount // address of previous count5 ) ;

22 e-mail: [email protected]

Page 23: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Παράµετροι

• hSemaphore: Η παράµετρος αυτή είναι µια λαβή στο αντικείµενο του σηµατοφόρου,του οποίου η τιµή πρόκειται να αυξηθεί. Η λαβή αυτή επιστρέφεται από τη συνάρτησηCreateSemaphore ή OpenSemaphore. Για να είναι επιτυχής η κλήση στη συνάρτησηαυτή η λαβή ϑα πρέπει να έχει δικαίωµα πρόσβασης SEMAPHORE_MODIFY_STATE.

• lReleaseCount: Καθορίζει το ποσό κατά το οποίο πρόκειται να αυξηθεί η τρέχουσααρίθµηση του αντικειµένου σηµατοφόρων. Η αρίθµηση δηλαδή ενός σηµαφόρου δεν είναιαπαραίτητο να αυξάνεται µόνο κατά µια µονάδα αλλά και κατά περισσότερες µονάδες. Τοκατά πόσες µονάδες ϑα αυξηθεί καθορίζεται µε την παράµετρο αυτή. προφανώς η τιµήαυτή πρέπει να είναι µεγαλύτερη του µηδενός. Επίσης, αν η αρίθµηση του σηµαφόρουξεπεράσει τη µέγιστη αρίθµηση που καθορίζεται στην παράµετρο lMaximumCount κατά τηνκλήση της CreateSemaphore, η αρίθµηση του σηµαφόρου δε ϑα αλλάξει και η συνάρτησηReleaseSemaphore ϑα επιστρέψει false.

• lpPreviousCount: ∆είκτης (pointer) σε µια µεταβλητή τύπου long int, ή οποία λαµ-ϐάνει την προηγούµενη τιµή της αρίθµησης του σηµαφόρου πριν την αύξηση. Η παράµετροςαυτή µπορεί να έχει την τιµή NULL όταν δεν ϑέλουµε να διαβάσουµε την προηγούµενη τιµήτου σηµαφόρου.

Επιστρεφόµενη τιµή

Εάν η κλήση στη συνάρτηση είναι επιτυχής, η επιστρεφόµενη τιµή είναι true. Σε περίπτωση απο-τυχίας η επιστρεφόµενη τιµή είναι false. Σε περίπτωση αποτυχίας η συνάρτηση GetLastError,µπορεί να δώσει αναλυτικές πληροφορίες για το σφάλµα που έχει συµβεί δες Listing 9.1 (σελ. 58).

Παράδειγµα χρήσης σηµαφόρου

Ως παράδειγµα χρήσης των σηµαφόρων ας υποθέσουµε ότι έχουµε δύο νήµατα, όπου το ένα δη-µιουργεί τους άρτιους αριθµούς από 0 έως Ν και το άλλο νήµα τους περιττούς από 1 έως Ν. Στησυνέχεια ϑέλουµε να συγχρονίσουµε τα δύο νήµατα ώστε οι αριθµοί να γράφονται στην οθόνηδιαδοχικά ένας από κάθε νήµα ταξινοµηµένοι και µε αύξουσα σειρά, δηλαδή: 0,1,2,3,4,...,Ν. Οσχετικός κώδικας παρουσιάζεται στο Listing 4.4 (σελ. 24).

4.2.2 Αντικείµενα αµοιβαίου αποκλεισµού (mutexes)

Το mutex είναι ένα άλλο αντικείµενο αµοιβαίου αποκλεισµού, το οποίο έχει µια ϐασικότατη διαφοράσε σχέση µε τους σηµαφόρους : ΄Οπως έχουµε δει στην προηγούµενη παράγραφο, ένας σηµαφόροςµπορεί να χρησιµοποιηθεί για να περιορίσει τον αριθµό διεργασιών που µπορούν να χρησιµοποιή-σουν ένα πόρο σε ένα άνω όριο. Με τα αντικείµενα αµοιβαίου αποκλεισµού mutexes µπορούµε ναεξασφαλίσουµε ότι µια το πολύ διεργασία από ένα σύνολο διεργασιών ϑα χρησιµοποιεί ένα συγκε-κριµένο πόρο. Από την άποψη αυτή η δουλειά που κάνει ένα mutex µπορεί να γίνει και από ένασηµαφόρο µε µέγιστη αρίθµηση 1.

Σε περιβάλλον windows υπάρχει µια σηµαντική διαφορά στην ϕιλοσοφία χρήσης του mutex σεσχέση µε αυτή των σηµαφόρων. ΄Οπως έχουµε δει προηγούµενα ένας σηµαιοφόρος µπορεί να

23 e-mail: [email protected]

Page 24: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 4.4: Συγχρονισµός νηµάτων µε χρήση σηµαφόρων1 #include "stdafx.h"2 #include "windows.h"3 #include "iostream.h"4 #define N 55 HANDLE H[2];6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−7 DWORD WINAPI f_even(void∗ p)8 9 int buf[N];

10 // fill the buffer with data.11 for(int i=0;i<N;i++)buf[i]=2∗i+1;12 HANDLE hodd =OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,"odd");13 HANDLE heven=OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,"even");14 for( i=0;i<N;i++)15 16 WaitForSingleObject(hodd,INFINITE);17 cout<<buf[i]<<",";18 ReleaseSemaphore(heven,1,0);19 20 return 0;21 22 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−23 DWORD WINAPI f_odd(void∗ p)24 25 int buf[N];26 // fill the buffer with data.27 for(int i=0;i<N;i++)buf[i]=2∗i;28 HANDLE hodd=OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,"odd");29 HANDLE heven=OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,"even");30 for( i=0;i<N;i++)31 32 WaitForSingleObject(heven,INFINITE);33 cout<<buf[i]<<",";34 ReleaseSemaphore(hodd,1,0);35 36 return 0;37 ;38 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−39 int main(int argc, char∗ argv[])40 41 DWORD d1,d2;42 CreateSemaphore(NULL,1,1,"even");43 CreateSemaphore(NULL,0,1,"odd");44 H[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f_even,NULL,0,&d1);45 H[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f_odd,NULL,0,&d2);46 WaitForMultipleObjects(2,H,true,INFINITE);47 cout<<endl;48 return 0;49

24 e-mail: [email protected]

Page 25: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

επιτελέσει τις λειτουργίες up και down. Το σηµαντικό είναι, ότι οι λειτουργίες αυτές µπορούν ναεκτελεστούν από οποιοδήποτε νήµα. στη χρήση των mutexes τα πράγµατα είναι διαφορετικά. Ηϐασική έννοια για να κατανοήσουµε το µηχανισµό στην εφαρµογή τους είναι η έννοια της ιδιο-κτησίας. ΄Ενα mutex µπορεί να ανήκει σε ένα µόνο νήµα κάθε χρονική στιγµή. ΄Οταν ένα νήµαδηµιουργεί ένα mutex έχει τη δυνατότητα να γίνει κάτοχος του mutex. Από τη στιγµή που ένασυγκεκριµένο νήµα κατέχει το mutex κανένα άλλο νήµα δεν έχει τη δυνατότητα να αποκτήσει τηνιδιοκτησία του, αν πρώτα ο κάτοχος δεν το απελευθερώσει.

Η απελευθέρωση ενός mutex γίνεται µε τη συνάρτηση ReleaseMutex. ΄Οµως, η συνάρτησηReleaseMutex δεν είναι δυνατόν να είναι επιτυχής παρά µόνο αν κληθεί από το νήµα που κα-τέχει το mutex. Στο σηµείο αυτό εµφανίζεται η ϐασική διαφορά στη χρήση ενός mutex και ενόςσηµαφόρου. ∆ηλαδή, η λειτουργία up για ένα mutex, δεν µπορεί να γίνει από οποιοδήποτε νήµαπαρά µόνο από το νήµα που κατέχει το συγκεκριµένο mutex. ΄Οσον αφορά τη λειτουργία down, αυ-τή επιτελείται µε τις συναρτήσεις αναµονής Waitfor...(). Αν µια συνάρτηση αναµονής καλείταιαπό ένα νήµα για ένα συγκεκριµένο mutex τότε αν το mutex ανήκει σε άλλο νήµα, το νήµα το οποίοκαλεί τη συνάρτηση αναµονής αναστέλλει τη λειτουργία του µέχρι το mutex να απελευθερωθεί απότο νήµα που το κατέχει. Μόνο αν το mutex είναι αδέσποτο η κλήση της συνάρτησης αναµονής ϑέτειτο mutex υπό την ιδιοκτησία του νήµατος που την κάλεσε και η εκτέλεση του συνεχίζεται κανονικά.

Αν ένα νήµα, το οποίο κατέχει ένα mutex τερµατίσει χωρίς να το απελευθερώσει, το mutex ϑεωρείταιεγκαταλειµµένο (abandoned). Στην περίπτωση αυτή, εάν ένα άλλο νήµα Ϲητήσει την ιδιοκτησία τουmutex ϑα τη λάβει και ϑα ενηµερωθεί για το γεγονός της εγκατάλειψης του από την επιστρεφόµενητιµή της συνάρτησης αναµονής.

΄Ενα mutex δηµιουργείται µε τη συνάρτηση CreateMutex:

Listing 4.5: Η συνάρτηση δηµιουργίας ενός mutex1 HANDLE CreateMutex(2 LPSECURITY_ATTRIBUTES lpMutexAttributes, // pointer to security attributes3 BOOL bInitialOwner, // flag for initial ownership4 LPCTSTR lpName // pointer to mutex−object name5 ) ;

Παράµετροι

• lpMutexAttributes: Η παράµετρος αυτή είναι δείκτης σε µια δοµή τύπου LPSE

CURITY_ATTRIBUTES και καθορίζει το αν η λαβή που επιστρέφεται από τη συνάρτηση ϑαείναι κληρονοµήσιµη από διεργασίες που δηµιουργούνται από το νήµα αυτό. Αν η τιµή τηςπαραµέτρου αυτής είναι NULL η λαβή δεν µπορεί να κληρονοµηθεί.

• bInitialOwner: Καθορίζει τον αρχικό κάτοχο του mutex. Αν TRUE, το νήµα πουδηµιουργεί το mutex καταλαµβάνει αµέσως την ιδιοκτησία, µε την προϋπόθεση ϐέβαια ότι τοmutex δεν έχει δηµιουργηθεί ήδη και ανήκει σε άλλη διεργασία.

• lpName: ∆είκτης σε µια συµβολοσειρά (string) που καθορίζει το όνοµα του αντικειµένουmutex. Το µέγιστο µήκος του ονόµατος πρέπει να είναι MAX_PATH, και µπορεί να περιέχειοποιοδήποτε χαρακτήρα εκτός από τους χαρακτήρες σύγκρισης και αντικάθετου.

Αν lpName είναι NULL, το mutex που δηµιουργείται είναι ανώνυµο.

25 e-mail: [email protected]

Page 26: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Αν lpName είναι το όνοµα ενός υπάρχοντος αντικειµένου συχγχονισµού η συνάρτηση επιστρέ-ϕει ERROR_INVALID_HANDLE και η GetLastError µας δίνει τον κωδικό του σφάλµατοςπου συνέβη. Ο κωδικός αυτός µεταφράζεται σε κείµενο µε την συνάρτηση του στον κώδικαListing 9.1 (σελ. 58).

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής, η επιστρεφόµενη τιµή είναι µια λαβή (handle) στοαντικείµενο mutex που δηµιουργείται. ∆ιαφορετικά επιστρέφει ERROR_ALREADY_EXISTS και ησυνάρτηση GetLastError µας δίνει τον κωδικό του σφάλµατος που δηµιουργήθηκε.

Οποιοδήποτε νήµα οποιασδήποτε διεργασίας µπορεί να αποκτήσει µια λαβή σε ένα mutex µε όνοµαlpName µε τη συνάρτηση OpenMutex:

Listing 4.6: Η συνάρτηση απόκτησης λαβής ενός mutex1 HANDLE OpenMutex(2 DWORD dwDesiredAccess, // access flag3 BOOL bInheritHandle, // inherit flag4 LPCTSTR lpName // pointer to mutex−object name5 ) ;

Παράµετροι

• dwDesiredAccess: Πρέπει να έχει την τιµή MUTEX_ALL_ACCESS, ώστε το νήµα πουϑα αποκτήσει τη λαβή να έχει το δικαίωµα να αποκτήσει την ιδιοκτησία του mutex, η να τοαπελευθερώσει.

• bInheritHandle: Καθορίζει αν η λαβή που επιστρέφει η συνάρτηση είναι κληρονοµήσιµηαπό διεργασίες που δηµιουργούνται από το νήµα που καλεί τη συνάρτηση.

• lpName: Το όνοµα του mutex. Ισχύουν οι γνωστοί περιορισµοί (δες CreateMutex)

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής επιστρέφει µια λαβή στο αντικείµενο του mutex µεόνοµα lpName. ∆ιαφορετικά επιστρέφει NULL και η συνάρτηση GetLastError µας δίνει τονκωδικό του σφάλµατος που έχει συµβεί.

΄Οταν η λαβή σε ένα αντικείµενο mutex δεν είναι πλέον απαραίτητη η κλήση της συνάρτησηςCloseHandle απελευθερώνει τους πόρους που σχετίζονται µε τη συγκεκριµένη λαβή. Το αντικεί-µενο καταστρέφεται εντελώς όταν απελευθερωθεί και η τελευταία λαβή που σχετίζεται µαζί του.

Η συνάρτηση απελευθέρωσης της ιδιοκτησίας ενός mutex είναι η ReleaseMutex:

Listing 4.7: Η συνάρτηση απελευθέρωσης ενός mutex1 BOOL ReleaseMutex(2 HANDLE hMutex // handle of mutex object3 ) ;

26 e-mail: [email protected]

Page 27: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

∆εν χρειάζεται ιδιαίτερες συστάσεις. hMutex είναι η λαβή στο mutex που απελευθερώνεται, αν ηκλήση είναι µη επιτυχής η επιστρεφόµενη τιµή είναι FALSE και η συνάρτηση GetLastError µαςπληροφορεί για το τι συνέβη. Αυτό που πρέπει να τονιστεί ξανά είναι οτι η ReleaseMutex µπορείνα κληθεί επιτυχώς µόνο από το νήµα που κατέχει το mutex σε αντίθεση µε τη ReleaseSemaphoreπου µπορεί να κληθεί επιτυχώς από οποιοδήποτε νήµα.

4.2.3 Συµβάντα (Events)

Το πρώτο ϑέµα που πρέπει να διευκρινιστεί είναι ότι τα Events ως αντικείµενα συγχρονισµού δενέχουν καµία σχέση µε τα Events που έχουν αναφερθεί στο κεφάλαιο 1.2, σελ. 5. Απλή συνωνυµία !.

Η διαφορά που έχει ένα Event από τα άλλα αντικείµενα συγχρονισµού είναι ότι υπάρχει ηδυνατότητα καθορισµού του τρόπου αποσηµατοδότησης του. ∆ηλαδή, ενώ όλα τα αντικείµενασυγχρονισµού αποσηµατοδοτούνται αυτόµατα µε την εφαρµογή µια συνάρτησης αναµονής π.χ.WaitForSingleObject(.,.) ΄Ενα αντικείµενο Event µπορεί να ϱυθµιστεί να αποσηµατοδο-τείται χειρωνακτικά ώστε η εφαρµογή συναρτήσεων αναµονής να µην τα αποσηµατοδοτεί αυτόµατα.Στην περίπτωση αυτή αποσηµατοδοτούνται µε την κλήση της συνάρτησης ResetEvent.

΄Ενα αντικείµενο (Event) δηµιουργείται µε τη συνάρτηση:

Listing 4.8: Η συνάρτηση δηµιουργίας ενός αντικειµένου συχγρονισµού event1 HANDLE CreateEvent(2 LPSECURITY_ATTRIBUTES lpEventAttributes, // pointer to security attributes3 BOOL bManualReset, // flag for manual−reset event4 BOOL bInitialState, // flag for initial state5 LPCTSTR lpName // pointer to event−object name6 ) ;

Παράµετροι

• lpEventAttributes: Η παράµετρος αυτή είναι δείκτης σε µια δοµή τύπου LPSE

CURITY_ATTRIBUTES και καθορίζει το αν η λαβή που επιστρέφεται από τη συνάρτηση ϑαείναι κληρονοµήσιµη από διεργασίες που δηµιουργούνται από το νήµα αυτό. Αν η τιµή τηςπαραµέτρου αυτής είναι NULL η λαβή δεν µπορεί να κληρονοµηθεί.

• bManualReset: Καθορίζει το αν το Event που δηµιουργείται ϑα αποσηµατοδοτείται(non-signaled) χειρωνακτικά η αυτόµατα. Αν η παράµετρος αυτή έχει τιµή TRUE τότε ηαποσηµατοδότηση του γίνεται µόνο χειρωνακτικά. Αυτό σηµαίνει ότι η εφαρµογή των συναρ-τήσεων αναµονής π.χ. WaitForSingleObject(.,.) στο Event δεν το ϑέτει σε µησηµατοδοτηµένη κατάσταση όπως π.χ. ϑα έκανε σε ένα σηµαφόρο. Ο µόνος τρόπος για ναέλθει σε µη σηµατοδοτηµένη κατάσταση είναι να κληθεί η συνάρτηση ResetEvent. Αν ητιµή της παραµέτρου είναι FALSE και το Event σηµατοδοτηµένο (signaled), η επίδρασησυναρτήσεων αναµονής το αποσηµατοδοτεί αυτόµατα.

• bInitialState: Καθορίζει την αρχική κατάσταση του Event που δηµιουργείται. ΑνTRUE το Event είναι σηµατοδοτηµένο. Αν είναι FALSE το Event δηµιουργείται µησηµατοδοτηµένο.

27 e-mail: [email protected]

Page 28: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

• lpName: Κατά τα γνωστά, δείκτης σε µια συµβολοσειρά (string) που καθορίζει το όνοµα τουαντικειµένου Event. Το µέγιστο µήκος του ονόµατος πρέπει να είναι MAX_PATH, και µπορείνα περιέχει οποιοδήποτε χαρακτήρα εκτός από τους χαρακτήρες σύγκρισης και αντικάθετου.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής επιστρέφει µια λαβή στο αντικείµενο του Event µεόνοµα lpName. ∆ιαφορετικά επιστρέφει ERROR_ALREADY_EXISTS και η συνάρτηση GetLast

Error µας δίνει τον κωδικό του σφάλµατος που έχει συµβεί.

Η σηµατοδότηση του Event γίνεται µε τη συνάρτηση SetEvent:

Listing 4.9: Η συνάρτηση σηµατοδότησης ενός αντικειµένου συχγρονισµού event1 BOOL SetEvent(2 HANDLE hEvent // handle of event object3 ) ;

Η χειρωνακτική αποσηµατοδότηση ενός Event γίνεται µε την ResetEvent:

Listing 4.10: Η συνάρτηση χειρωνακτικής αποσηµατοδότησης ενός αντικειµένου συχγρονισµούevent

1 BOOL ResetEvent(2 HANDLE hEvent // handle of event object3 ) ;

Η αυτόµατη αποσηµατοδότηση του γίνεται από τις συναρτήσεις αναµονής.

Ειδικά για τα αντικείµενα τύπου Event υπάρχει η συνάρτηση PulseEvent, η οποία σηµατοδοτείτο Event, µέχρι να απελευθερωθούν όλα τα νήµατα που περιµένουν τη σηµατοδότηση του και στησυνέχεια το αποσηµατοδοτεί.

Listing 4.11: Η συνάρτηση παλµού ενός αντικειµένου συχγρονισµού event1 BOOL PulseEvent(2 HANDLE hEvent // handle of event object3 ) ;

Οποιοδήποτε νήµα οποιασδήποτε διεργασίας µπορεί να αποκτήσει µια λαβή σε ένα αντικείµενοτύπου Event µε κλήση της συνάρτησης OpenEvent:

Listing 4.12: Η απόκτησης της λαβής ενός αντικειµένου συγχρονισµού event1 HANDLE OpenEvent(2 DWORD dwDesiredAccess, // access flag3 BOOL bInheritHandle, // inherit flag4 LPCTSTR lpName // pointer to event−object name5 ) ;

Η παράµετρος dwDesiredAccess πρέπει να έχει τιµή EVENT_ALL_ACCESS ώστε το νήµα πουϑα αποκτήσει τη λαβή να έχει τη δυνατότητα σηµατοδότησης και αποσηµατοδότησης του Event.

Αν η κλήση της συνάρτησης είναι επιτυχής επιστρέφει µια λαβή στο αντικείµενο του Event µεόνοµα lpName. ∆ιαφορετικά επιστρέφει ERROR_INVALID_HANDLE και η συνάρτηση GetLast

Error µας δίνει, κατά τα γνωστά, τον κωδικό του σφάλµατος που έχει συµβεί.

28 e-mail: [email protected]

Page 29: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Name Relative speed Cross process Resource counting Supported platformsCritical Section Fast No No (exclusive access) 9x/NT/CE

Mutex Slow Yes No (exclusive access) 9x/NT/CESemaphore Slow Yes Automatic 9x/NT

Event Slow Yes Yes 9x/NT/CE

Πίνακας 4.1: Ποιοτική σύγκριση των διαφόρων αντικειµένων συγχρονισµού

4.2.4 Κρίσιµα τµήµατα (critical sections)

΄Ενας άλλος τρόπος συγχρονισµού νηµάτων είναι τα κρίσιµα τµήµατα (critical sections). Ορίζονταςως κρίσιµο τµήµα την περιοχή νηµάτων στην οποία χρησιµοποιείται ένας κοινόχρηστος πόρος εξα-σφαλίζουµε ότι µόνο ένα νήµα κάθε ϕορά ϑα χρησιµοποιεί τον πόρο αυτό. Αυτό ϕυσικά µπορούµενα το επιτύχουµε και µε τα προαναφερθέντα αντικείµενα συγχρονισµού, mutexes η σηµαφόρουςµε µέγιστη αρίθµηση το 1, ή events. Το πλεονέκτηµα των κρίσιµων τµηµάτων είναι η ταχύτηταστην εφαρµογή τους. Η δηµιουργία ενός αντικειµένου π.χ. mutex είναι αρκετά ποιο χρονοβόρααπό την εισαγωγή ενός νήµατος στην κρίσιµη περιοχή του. Το µειονέκτηµα τους είναι ότι µπορούννα χρησιµοποιηθούν µόνο για νήµατα της ίδιας διεργασίας, ενώ τα άλλα αντικείµενα συγχρονισµούµπορούν να χρησιµοποιηθούν για να συγχρονίσουν νήµατα που ανήκουν και σε διαφορετικές διερ-γασίες.

΄Ενα κρίσιµο τµήµα δηµιουργείται µε τη δήλωση µιας µεταβλητής τύπου CRITICAL_SECTION.

Πριν µπορέσουν να την χρησιµοποιήσουν τα νήµατα µιας διεργασίας η µεταβλητή αυτή ϑα πρέπεινα µονογραφηθεί. Αυτό γίνεται µε την συνάρτηση InitializeCriticalSection, η οποίαλαβµάνει ως όρισµα ένα δείκτη σε µια µεταβλητή τύπου CRITICAL_SECTION.

΄Ενα νήµα χρησιµοποιεί τη συνάρτηση EnterCriticalSection για να Ϲητήσει την ιδιοκτησίαενός κρίσιµου τµήµατος. Αν το αντικείµενο κρίσιµου τµήµατος ανήκει σε άλλο νήµα, το νήµα πουκαλεί την EnterCriticalSection αναστέλλει την εκτέλεση του µέχρι την απελευθέρωση τουαντικειµένου από τον κάτοχο του. Η απελευθέρωση ενός αντικειµένου κρίσιµου τµήµατος από ένανήµα γίνεται µε τη συνάρτηση LeaveCriticalSection.

Η συνάρτηση TryEnterCriticalSection προσπαθεί να πάρει την ιδιοκτησία ενός κρίσιµουτµήµατος όπως η EnterCriticalSection. Η πολύ χρήσιµη διαφορά είναι ότι αν το αντικείµενοκρίσιµου τµήµατος ανήκει σε άλλο νήµα τότε το νήµα που καλεί την TryEnterCriticalSectionδεν αναστέλλει την εκτέλεση του αλλά συνεχίζει κανονικά και επιστρέφει FALSE ενηµερώνοντας τονήµα που την καλεί ότι αυτή τη στιγµή το αντικείµενο κρίσιµου τµήµατος ανήκει αλλού.

΄Οταν ένα αντικείµενο κρίσιµου τµήµατος δεν είναι πλέον απαραίτητο µπορεί να διαγραφεί µεκλήση της συνάρτησης DeleteCriticalSection.

παράδειγµα

΄Εστω ότι έχουµε ένα κοινόχρηστο πόρο π.χ. την οθόνη και τρία νήµατα που ϑέλουν να γράψουνταυτόχρονα το όνοµα τους. Θέλουµε να τα συγχρονίσουµε µε χρήση κρίσιµων τµηµάτων. Ο κώδικαςαπεικονίζεται στο Listing 4.13 (σελ. 30).

Μια ποιοτική σύνοψη των αντικειµένων συγχρονισµού παρουσιάζεται στον πίνακα 4.1.

29 e-mail: [email protected]

Page 30: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 4.13: Παράδειγµα συχρονισµού νηµάτων µε κρίσιµα τµήµατα1 #include "stdafx.h"2 #include "windows.h"3 #include "iostream.h"4 HANDLE H[3];5 //define a critical section object6 CRITICAL_SECTION cs;7 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−8 //The thread functions9 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

10 DWORD WINAPI thread1(void∗ p)11 12 EnterCriticalSection(&cs);13 cout<<"Hello, i am thread one"<<endl;14 LeaveCriticalSection(&cs);15 16 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−17 DWORD WINAPI thread2(void∗ p)18 19 EnterCriticalSection(&cs);20 cout<<"Hello, i am thread two"<<endl;21 LeaveCriticalSection(&cs);22 23 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−24 DWORD WINAPI thread3(void∗ p)25 26 EnterCriticalSection(&cs);27 cout<<"Hello, i am thread three"<<endl;28 LeaveCriticalSection(&cs);29 30 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−31 int main()32 33 //initialize critical section cs.34 InitializeCriticalSection(&cs);35 //create threads36 DWORD d1,d2,d2;37 H[0]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread1,NULL,0,&d1);38 H[1]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread2,NULL,0,&d2);39 H[2]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread3,NULL,0,&d3);40 //ensure that all threads are comleted.41 WaitForMultipleObjects(3,H,INFINITE);42 //delete the critical section object43 DeleteCriticalSection(&cs);44 return 0;45

30 e-mail: [email protected]

Page 31: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

4.2.5 Συναρτήσεις αποκλειστικής πρόσβασης Interlocked.

Στην περίπτωση που έχουµε να εκτελέσουµε απλές πράξεις (π.χ. πρόσθεση, αφαίρεση, αύξηση/-µείωση δεικτών κ.τ.λ) σε µεταβλητές που χρησιµοποιούνται από περισσότερα του ενός νήµατα ϑαπρέπει να χρησιµοποιήσουµε ένα κρίσιµο τµήµα για κάθε µεταβλητή προκειµένου να επιτύχουµετον επιθυµητό αµοιβαίο αποκλεισµό. Προφανώς αυτή η αντιµετώπιση δεν είναι αποτελεσµατικήαφού για κάθε µεταβλητή χρειαζόµαστε άλλη µία τύπου CRITICAL_SECTION. Επίσης, ο κώδι-κας γίνεται δυσανάγνωστος και αυξάνεται ο κίνδυνος της εµφάνισης αδιεξόδου (deadlock). Για τηναντιµετώπιση του Ϲητήµατος αυτού το API των windows προσφέρει µια οµάδα από συναρτήσειςπου εκτελούνται ενιαία και αδιαίρετα επιτελώντας ϐασικές πράξεις σε µεταβλητές. Οι συναρτήσειςαυτές είναι :

• InterlockedIncrement: Η συνάρτηση αυτή αυτή αυξάνει κατά ένα την τιµή µιαςακέραιας µεταβλητής. Η σύνταξη της συνάρτησης έχει ως εξής :

1 LONG InterlockedIncrement(LPLONG lpAddend);

όπου:

lpAddend: είναι η διεύθυνση της µεταβλητής που πρόκειται να αυξηθεί. Η συνάρτησηαυτή εξασφαλίζει την αποκλειστική χρήση της µεταβλητής lpAddend από ένα νήµα.

Η επιστρεφόµενη τιµή µας δείχνει αν το αποτέλεσµα της αύξησης ήταν ϑετικό (επιστρεφόµενητιµή ϑετική) η αρνητικό (επιστρεφόµενη τιµή αρνητική) η µηδέν (επιστρεφόµενη τιµή µηδέν).Προσοχή ! η επιστρεφόµενη τιµή µας δίνει πληροφορία µόνο για το πρόσηµο και όχι για τηντιµή της µεταβλητής.

• InterlockedDecrement: ΄Οπως η προηγούµενη συνάρτηση µε τη διαφορά ότι αντί γιααύξηση κατά ένα επιτελεί µείωση κατά µια µονάδα κάθε ϕορά που εφαρµόζεται.

• InterlockedExchange: Μεταφέρει την τιµή µιας µεταβλητής σε µια αλλη µεταβλητή.Επιτελεί δηλαδή τη λειτουργία της ισότητας µεταξύ δύο µεταβλητών εξασφαλίζοντας την απο-κλειστική τους χρήση από ένα µόνο νήµα. Η σύνταξη της έχει ως εξής :

1 LONG InterlockedExchange(LPLONG Target,LONG Value);

΄Οπου:

Target: Η διεύθυνση της µεταβλητής που ϑα λάβει την τιµή Value.Value: Η τιµή (όχι διεύθυνση) της µεταβλητής που ϑα µεταφερθεί στην Target.

Η συνάρτηση επιστρέφει την τιµή της Target πριν την ανταλλαγή.

Υπάρχουν επίσης οι InterlockedCompareExchange και InterlockedExchangeAdd οιοποίες όµως υπάρχουν µόνο στο winNT.

4.2.6 Συναρτήσεις αναµονής (wait functions)

Για τα αντικείµενα συγχρονισµού που έχουν αναφερθεί, υπάρχουν τρεις ϐασικές συναρτήσεις ανα-µονής :

31 e-mail: [email protected]

Page 32: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

1. WaitForSingleObject

2. WaitForMultipleObjects

3. SignalObjectAndWait

Οι συναρτήσεις αυτές αναστέλλουν την εκτέλεση του νήµατος που τις καλεί όταν το αντικείµενογια το οποίο περιµένουν είναι µη σηµατοδοτηµένο και δεν έχει εκπνεύσει ο χρόνος αναµονής πουκαθορίζεται ως όρισµα.

Η συνάρτηση WaitForSingleObject έχει την ακόλουθη σύνταξη:

Listing 4.14: Η συνάρτηση WaitForSingleObject

1 DWORD WaitForSingleObject(2 HANDLE hHandle, // handle of object to wait for3 DWORD dwMilliseconds // time−out interval in milliseconds4 ) ;

Παράµετροι

• hHandle: Λαβή στο αντικείµενο συγχρονισµού για το οποίο πρέπει να περιµένει το νήµαπου καλεί τη συνάρτηση αναµονής. Η λαβή αυτή λαµβάνεται κατά τη δηµιουργία του αντι-κειµένου (δες Listing 3.1 (σελ. 15),Listing 4.1 (σελ. 20), Listing 4.5 (σελ. 25), Listing 4.8(σελ. 27)) ή µε τις συναρτήσεις OpenXXXX δες Listing 4.2 (σελ. 22), Listing 4.6 (σελ. 26).

• dwMilliseconds: Καθορίζει το χρόνο εκπνοής σε miliseconds Αν ο χρόνος αυτός πα-ϱέλθει χωρίς τη σηµατοδότηση του αντικειµένου, η συνάρτηση αναµονής επιστρέφει. Αν ητιµή αυτή είναι INFINITE η συνάρτηση αναµονής επιστρέφει µόνο αν σηµατοδοτηθεί τοαντικείµενο.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης αποτύχει η επιστρεφόµενη τιµή είναι WAIT_FAILED. Στην περίπτωσηαυτή η συνάρτηση GetLastError µας δίνει πληροφορίες για το λόγο της αποτυχίας.

Αν η κλήση είναι επιτυχής η επιστρεφόµενη τιµή (όταν η συνάρτηση επιστρέψει) µας δίνει πληρο-ϕορίες για το λόγο επιστροφής. Οι επιστρεφόµενη τιµή µπορεί να είναι :

WAIT_OBJECT_0: Το αντικείµενο αναµονής σηµατοδοτήθηκε.

WAIT_TIMEOUT: Ο χρόνος αναµονής εξέπνευσε χωρίς τη σηµατοδότηση του αντικειµένου ανα-µονής.

WAIT_ABANDONED: Κάποιο νήµα που είχε στην κατοχή του ένα mutex επέστρεψε χωρίς νααπελευθερώσει το mutex. (δες παράγραφο 4.2.2 σελ. 23)

Η συνάρτηση WaitForMultipleObjects συντάσσεται ως εξής :

32 e-mail: [email protected]

Page 33: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 4.15: Η συνάρτηση WaitForMultipleObjects

1 DWORD WaitForMultipleObjects(2 DWORD nCount, // number of handles in the object handle array3 CONST HANDLE ∗lpHandles, // pointer to the object−handle array4 BOOL bWaitAll, // wait flag5 DWORD dwMilliseconds // time−out interval in milliseconds6 ) ;

Παράµετροι

• nCount: Οι λαβές των αντικειµένων που πρέπει να περιµένει η συνάρτηση αναµονήςκαθορίζεται σε ένα πίνακα τύπου HANDLE. Η παράµετρος nCount καθορίζει τον αριθµό τωναντικειµένων (στοιχεία του πίνακα) για τα οποία πρέπει να υπάρχει αναµονή. Η µέγιστη τιµήπου µπορεί να πάρει είναι MAXIMUM_WAIT_OBJECTS.

• lpHandles: Ο πίνακας των λαβών που αναφέρθηκε προηγουµένως.

• bWaitAll: Αν η τιµή της παραµέτρου αυτής είναι TRUE, η συνάρτηση αναµονής περιµένειόλλα τα nCount αντικείµενα να σηµατοδοτηθούν. Αν η τιµή είναι FALSE η συνάρτησηεπιστρέφει αν τουλάχιστον ένα από τα nCount αντικείµενα σηµατοδοτηθεί.

• dwMilliseconds: Καθορίζει το χρόνο εκπνοής της αναµονής σε miliseconds.

Επιστρεφόµενη τιµή

΄Οπως και προηγουµένως, αν η κλήση της συνάρτησης αποτύχει η επιστρεφόµενη τιµή είναιWAIT_FAILED. Στην περίπτωση αυτή η συνάρτηση GetLastError µας δίνει πληροφορίες για τολόγο της αποτυχίας.

Αν η κλήση είναι επιτυχής η επιστρεφόµενη τιµή (όταν η συνάρτηση επιστρέψει) µας δίνει πληρο-ϕορίες για το λόγο επιστροφής. Οι επιστρεφόµενη τιµή µπορεί να είναι :

WAIT_TIMEOUT: Ο χρόνος αναµονής εξέπνευσε χωρίς τη σηµατοδότηση του αντικειµένου ανα-µονής.

[WAIT_OBJECT_0,(WAIT_OBJECT_0+nCount-1)]: Η επιστρεφόµενη τιµή µπορεί να ανή-κει στο διάστηµα αυτό. Η ερµηνεία είναι η εξής : Αν bWaitAll==FALSE τότε η επιστρεφόµε-νη τιµή ανήκει στο διάστηµα [WAIT_OBJECT_0,WAIT_OBJECT_0+nCount-1] ανάλογαµε το αντικείµενο που σηµατοδοτήθηκε. Αν bWaitAll==TRUE όλλα τα nCount αντικείµενααναµονής σηµατοδοτήθηκαν.

[WAIT_ABANDONED_0,(WAIT_ABANDONED_0+nCount-1): Οµοίως µε την προηγούµενηπερίπτωση µε τη διαφορά ότι αντί σηµατοδότηση η τιµή ερµηνεύεται ως εγκατάλειψη τουαντίστοιχου σηµατοδοτηµένου mutex.

Παραδείγµατα χρήσης των συναρτήσεων αναµονής παρουσιάζονται στο Listing 3.3 (σελ. 16) καιListing 4.4 (σελ. 24).

33 e-mail: [email protected]

Page 34: Ergastirio_Simioseis

Κεφάλαιο 5

∆ιαδιεργασιακή Επικοινωνία(Interprocess Comunnication)

Στόχος:

• Εισαγωγή στην ∆ιαδιεργασιακή Επικοινωνία.

• ∆ηµιουργία κοινόχρηστης µνήµης µεταξύ διεργασιών.

5.1 ΄Ασκηση:

Να υλοποιηθεί ο µηχανισµός clipboard για την ανταλλαγή δεδοµένων µέσω διαφορετικών διερ-γασιών.

Ως πρόγραµµα δοκιµής να δηµιουργηθούν δύο διαφορετικές διεργασίες Α, Β. Κάθε διεργασία ϑαέχει τη δυνατότητα να γράφει και να διαβάζει δεδοµένα από µια κοινόχρηστη µνήµη. Η κοινόχρηστηµνήµη να δηµιουργείται από τη διεργασία Α. Επίσης, να υλοποιηθεί ο αµοιβαίος αποκλεισµός.

34

Page 35: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

5.2 Θεωρητικά Στοιχεία

Με τον όρο διαδιεργασική επικοινωνία εννοούµε την ανταλλαγή δεδοµένων µεταξύ διαφορετικώνδιεργασιών. Ενώ τα νήµατα µιας διεργασίας µοιράζονται τον ίδιο χώρο διευθύνσεων που σηµαίνειότι έχουν πρόσβαση στις ίδιες κοινόχρηστες µεταβλητές, µέσω των οποίων µπορούν να ανταλάσουνµεταξύ τους δεδοµένα, δε συµβαίνει το ίδιο µεταξύ διαφορετικών διεργασιών που έχουν διαφορετικόχώρο διευθύνσεων. Οι διεργασίες µπορούν να ανταλάσουν µεταξύ τους δεδοµένα µε συγκεκριµέ-νους τρόπους :

1. Με τη δηµιουργία κοινής µνήµης (shared memory) αυτό υλοποιείται µε απεικόνιση αρχείωνστη µνήµη (Memory mapped files).

2. Με αρχεία (files) στο δίσκο.

3. Με µυνήµατα (messages).

4. Με γραµµατοκιβώτια (mailboxes / mailslots).

5. Με σωληνώσεις (pipes).

6. Με (sockets) µέσω του προτοκόλλου TCP/IP.

7. Μέσω του clipboard.

8. Με αποµακρυσµένες κλήσεις συναρτήσεων (Remote Procedure Call). Προϋπόθεση είναι οικλήσεις αυτές να είναι συµβατές µε το πρότυπο DCE (Distributed Computing Environment).

5.2.1 Απεικόνιση Αρχείων στη Μνήµη (Memory Mapped Files)

Μέσω της τεχνικής αυτής µια διεργασία µπορεί να δηµιουργήσει ένα πιστό αντίγραφο (µια εικόνα)ενός αρχείου (ή µέρους του αρχείου) στην κύρια µνήµη (RAM). Στη συνέχεια, άλλες διεργασίεςέχουν τη δυνατότητα να αποκτήσουν πρόσβαση στην περιοχή αυτή της µνήµης µε τη ϐοήθεια ενόςδείκτη (pointer) και να χειριστούν την περιοχή αυτή γράφοντας ή διαβάζοντας δεδοµένα. Με τοντρόπο αυτό δηµιουργείται µια κοινόχρηστη µνήµη µεταξύ διαφόρων διεργασιών. Μια διεργασίαµπορεί να τοποθετεί δεδοµένα στην περιοχή αυτή, τα οποία µπορεί να διαβάσει µια άλλη. ΄Ετσιµπορούν να µεταφέρονται δεδοµένα από µια διεργασία σε µια άλλη.

Η απεικόνιση ενός αρχείου στη µνήµη υλοποιείται σε δύο ϕάσεις :

1. Μια διεργασία απεικονίζει το αρχείο στη ϕυσική µνήµη RAM του Η/Υ. Αυτό υλοποιείταιµε τη συνάρτηση CreateFileMapping. Η συνάρτηση αυτή επιστρέφει ένα χειριστήριο(HANDLE) στο τµήµα της ϕυσικής µνήµης που δεσµεύεται.

2. Οποιαδήποτε διεργασία µπορεί να αποκτήσει µια λαβή στην περιοχή αυτή της µνήµης µετη συνάρτηση OpenFileMapping, µέσω της οποίας µπορεί στη συνέχεια να αποκτήσει έναδείκτη (pointer) στην περιοχή αυτή της µνήµης, δηλαδή να απεικονίσει την περιοχή της µνή-µης στην εικονική της µνήµη (Virtual Memory). Σχηµατικά η διαδικασία αυτή απεικονίζεταιστο Σχήµα 5.1 (σελ. 36).

35 e-mail: [email protected]

Page 36: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Virtual Memory

process 1

void* p1

Virtual Memory

process 2

void* p2

Virtual Memory

process N

void* pn

test.txtCreateFileMapping

MapV

iew

OfF

ile

MapV

iew

OfF

ileMapViewOfFile

RAM

hMap

Σχήµα 5.1: Σχηµατική απεικόνιση αρχείου στη µνήµη

Μετά την ολοκλήρωση της ανάγνωσης ή της εγγραφής από µια διεργασία πρέπει να κληθεί ηUnMapViewOfFile για να απελευθερώσει την εικονική µνήµη της διεργασίας. Τέλος, όταν πλέονδεν χρειάζεται η κοινή µνήµη που έχει δεσµευτεί για την απεικόνιση του αρχείου από καµιάδιεργασία, ϑα πρέπει να δοθεί εντολή στο λειτουργικό σύστηµα να την απελευθερώσει. Αυτό γίνεταιµέσω της συνάρτησης CloseHandle του win32API.

Ιδιαίτερη ΄Εµφαση πρέπει να δοθεί στα ακόλουθα:

• Μετά τη δηµιουργία του αντικείµενου απεικόνισης ενός αρχείου, το µέγεθος του αρχείουδεν πρέπει να υπερβαίνει το µέγεθος του αντικειµένου απεικόνισης. Αν συµβεί κάτι τέτοιοτότε υπάρχει κίνδυνος ορισµένα από τα περιεχόµενα του αρχείου να µη είναι διαθέσιµα στηνκοινή µνήµη.

• Αντίθετα, αν µια εφαρµογή καθορίσει το µέγεθος του αντικειµένου απεικόνισης µεγαλύτεροαπό το µέγεθος του αρχείου το οποίο απεικονίζει, το αρχείο στο δίσκο αυξάνει το µέγεθος του,µέχρι αυτό να γίνει ίσο µε το µέγεθος του αντικειµένου απεικόνισης

• Η λαβή που επιστρέφει συνάρτηση CreateFileMapping, είναι διαθέσιµη στην εφαρµογήπου την κάλεσε και µέσω αυτής υπάρχει πλήρη πρόσβαση στην κοινόχρηστη µνήµη. Αν γιακάποιο λόγο δεν υπάρχει πρόσβαση στη λαβή αυτή, π.χ. από µια άλλη διαδικασία που εκ των

36 e-mail: [email protected]

Page 37: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

πραγµάτων έχει διαφορετικό χώρο διευθύνσεων (Address Space,) µπορούµε να αποκτήσουµεµια λαβή στο αντικείµενο απεικόνισης µέσω της συνάρτησης OpenFileMapping, καθορί-Ϲοντας το όνοµα του αντικειµένου που δώσαµε όταν καλέσαµε την συνάρτηση CreateFile

Mapping.

• Ιδιαίτερη προσοχή χρειάζεται στον συγχρονισµό διεργασιών που χειρίζονται την ίδια κοινό-χρηστη περιοχή µνήµης, για την αποφυγή συνθηκών ανταγωνισµού. Αυτό είναι ευθύνη τουπρογραµµατιστή και όχι του λειτουργικού συστήµατος. Ως γνωστό ο αµοιβαίος αποκλεισµόςυλοποιείται µε τα αντικείµενα συγχρονισµού.

΄Ενα παράδειγµα κώδικα για τη δηµιουργία ενός αντικειµένου απεικόνισης του αρχείου myclp.txtστη µνήµη απεικονίζεται στο Listing 5.1 . Στο Listing 5.2 παρουσιάζεται η διαδικασία απεικόνισηςαπό τη RAM στο χώρο διευθύνσεων µιας διεργασίας.

Listing 5.1: παράδειγµα απεικόνισης Αρχείου στη RAM για τη δηµιουργία ενός αντικειµένουαπεικόνισης µε όνοµα myclp

1 #define MAX_CLIP_SIZE 10000002 hFile = CreateFile(3 "c:\\temp\\myclp.txt", // pointer to name of the file4 GENERIC_READ | GENERIC_WRITE, // access (read−write) mode5 FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode6 NULL, // pointer to security attributes7 OPEN_ALWAYS , // how to create8 FILE_ATTRIBUTE_NORMAL , // file attributes9 NULL // handle to file with attributes to copy

10 ) ;11 //set the size of myclipboard12 char tmp[MAX_CLIP_SIZE];13 DWORD dwBytes;14 WriteFile(hFile,tmp,MAX_CLIP_SIZE∗sizeof(char),&dwBytes,NULL);15 //map the file to memory.16 //create file mapping17 hFilemap=CreateFileMapping(18 hFile, // handle to file to map19 NULL, // optional security attributes20 PAGE_READWRITE, // protection for mapping object21 0, // high−order 32 bits of object size22 0, // low−order 32 bits of object size23 "myclp" // name of file−mapping object24 ) ;

Listing 5.2: παράδειγµα απεικόνισης ενός αντικειµένου απεικόνισης στο χώρο διευθύνσεων (Ad-

dress space) µιας διεργασίας1 //Get Data From custom clipboard2 //map the file to memory.3 //create file mapping4 HANDLE hfilemap=OpenFileMapping(5 FILE_MAP_ALL_ACCESS, // handle to file to map6 NULL, // optional security attributes7 "myclp" // name of file−mapping object

37 e-mail: [email protected]

Page 38: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

8 ) ;9 //The MapViewOfFile function maps a view of a file into the address space of the calling process.

10 char∗ pClipData = (char∗)MapViewOfFile(11 hfilemap, // file−mapping object to map into address space12 FILE_MAP_ALL_ACCESS , // access mode13 0, // high−order 32 bits of file offset14 0, // low−order 32 bits of file offset15 0 // number of bytes to map16 ) ;17 //−−−−−−−−−−−−−−−−−−− Use the memory

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−18 if (pClipData==NULL)19 20 MessageBox(NULL,"Unable to Open The ClipBoard","Error",MB_OK);21 return;22 23 m_clip−>Text=pClipData;24 UnmapViewOfFile(pClipData);25 CloseHandle(hfilemap);26 //Notify all processes that the shared memory has been altered.27 SendMessage(HWND_BROADCAST,RegisterWindowMessage("MSG_NEW_DATA_TO_MYCLIP"),0,0);

38 e-mail: [email protected]

Page 39: Ergastirio_Simioseis

Κεφάλαιο 6

Μυνήµατα (messages)

Στόχος:

• Εισαγωγή στο µηχανισµό µυνηµάτων µυνηµάτων των windows.

• ∆ιαδιεργασιακή επικοινωνία µε µυνήµατα

6.1 ΄Ασκηση:

• Να κατασκευαστή µια εφαρµογή η οποία να λειτουργεί ως Spell Server. Θα λαµβάνειµυνήµατα (λέξεις προς διόρθωση) από τον επεξεργαστή κειµένου που κατασκευάσαµε σεπροηγούµενα εργαστήρια ϑα διορθώνει τις λέξεις και να επιστρέφει τις διορθωµένες λέξειςπάλι µε µύνηµα στον αποστολέα τους.

• Να τροποποιηθεί η εφαρµογή του επεξεργαστή κειµένου που είδαµε σε προηγούµενα ερ-γαστήρια ώστε να στέλνει λέξεις προς διόρθωση στον Spell Server και να λαµβάνει υπόµορφή µυνήµατος τα αποτελέσµατα της διόρθωσης.

39

Page 40: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

6.2 Θεωρητικά Στοιχεία

Στο λειτουργικό σύστηµα windows ένα µήνυµα µπορεί να σταλεί από οποιαδήποτε συνάρτηση µεχρήση της συνάρτησης SendMessage προς οποιοδήποτε ανοικτό παράθυρο. Ο παραλήπτης είναιπάντοτε ένα ανοικτό παράθυρο η ένα νήµα, το οποίο είναι εφοδιασµένο µε αντλία (Message Pump)

µηνυµάτων. Ο παραλήπτης του µηνύµατος καθορίζεται ως όρισµα µέσω µιας λαβής στο παράθυροή στο νήµα. Εδώ ϑα ασχοληθούµε κυρίως µε µηνύµατα τα οποία απευθύνονται σε παράθυρα.

Κάθε ϕορά που ένα παράθυρο λαµβάνει ένα µήνυµα, καλείται αυτόµατα µια συνάρτηση επανάκλη-σης (CallBack function), η οποία είναι µέλος της ϐασικής τάξης παραθύρου από το οποίο παράγονταιµε κληρονοµικότητα όλα τα παράθυρα και χειρίζεται το µήνυµα. Η συνάρτηση αυτή ονοµάζεταιWndProc. Ο προγραµµατιστής έχει τη δυνατότητα να υποσκελίσει (overload) τη συνάρτηση αυτήστο κώδικα του παραθύρου, το οποίο έξ΄ ορισµού κληρονοµεί από τη ϐασική τάξη των παραθύρωνκαι να γράψει το δικό του κώδικα στην παράγωγή τάξη του παραθύρου του που ϑα χειρίζεται τοµήνυµα µε τον τρόπο που αυτός επιθυµεί.

Το λειτουργικό σύστηµα το windows είναι εφοδιασµένο µε ένα µεγάλο αριθµό προκαθορισµένωνµηνυµάτων περίπου δύο µε τρεις χιλιάδες, καθένα από τα οποία έχει ένα κωδικό και επιτελεί µιασυγκεκριµένη δουλειά. Π.χ. το µήνυµα µε κωδικό WM_CLOSE όταν αποσταλεί σε ένα παράθυροέχει ως αποτέλεσµα το κλείσιµο του παραθύρου. Φυσικά, το λειτουργικό σύστηµα δίνει τη δυνατό-τητα της κατασκευής κωδικών νέων µηνυµάτων κατά τη διάρκεια της εκτέλεσης ενός προγράµµατος(run time), µέσω της συνάρτησης RegiterWindowsMessage.

Μια λαβή σε κάποιο ανοιχτό παράθυρο µπορεί να ληφθεί από τον τίτλο του παραθύρου µε τησυνάρτηση FindWindow, η οποία λαµβάνει ως όρισµα µια σύµβολο σειρά που είναι το όνοµα τουανοικτού παράθυρου και επιστρέφει µια λαβή στο παράθυρο αυτό. Αν υπάρχουν περισσότερα τουενός παράθυρα µε τον ίδιο τίτλο η συνάρτηση επιστρέφει µια λαβή στο παράθυρο που είναι ποιοψηλά (top-most) στην λίστα εµφάνισης.

Πρέπει να αναφερθεί ότι ένα µήνυµα των windows είναι µια δοµή (struct), η οποία αποτελείταιαπό ένα σύνολο παραµέτρων που χαρακτηρίζουν το µήνυµα π.χ. κωδικός µυνήµατος, αποστολέας,χρονική στιγµή της αποστολής κ.λπ. Η δοµή ενός µυνήµατος παρουσιάζεται στο Listing 6.1

Listing 6.1: Η δοµή ενός µυνήµατος1 typedef struct tagMSG // msg2 HWND hwnd;3 UINT message;4 WPARAM wParam;5 LPARAM lParam;6 DWORD time;7 POINT pt;8 MSG;

Μέλη της δοµής

• hwnd: Λαβή στο παράθυρο παραλήπτη του οποίου η WndProc ϑα λάβει το µύνηµα.

• message: Η ταυτότητα του µυνήµατος που είναι ένας ακέραιος αριθµός.

40 e-mail: [email protected]

Page 41: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

• wParam: ακέραιος τύπου DWORD που χρησιµεύει για συµπληρωµατικές πληροφορίες πουαφορούν το µύνηµα. Για κάθε µύνηµα έχει και διαφορετική έννοια.

• lParam: οµοίως µε wParam.

• time: Καθορίζει τη χρονική στιγµή της αποστολής του µυνήµατος.

• pt: Καθορίζει τη ϑέση του MOUSE τη στιγµή της αποστολής. Το σύστηµα συντεταγµένωνείναι το σύστηµα συντεταγµένων της οθόνης.

6.2.1 Αποστολή µυνηµάτων

Η αποστολή ενός µυνήµατος πραγµατοποιείται µε τη συνάρτηση SendMessage, η ακριβής σύνταξητης οποίας ϕαίνεται παρακάτω:

Listing 6.2: Η συνάρτηση αποστολής ενός µυνήµατος1 LRESULT SendMessage(2 HWND hWnd, // handle of destination window3 UINT Msg, // message to send4 WPARAM wParam, // first message parameter5 LPARAM lParam // second message parameter6 ) ;

Παράµετροι

• hWnd: καθορίζει το παράθυρο το οποίο πρόκειται να δεχτεί το µήνυµα. Αν αυτή η πα-ϱάµετρος έχει την τιµή HWND_BROADCAST τότε το Μύνηµα αποστέλλεται σε όλα τα ανοιχτάπαράθυρα. Στα παράθυρα αυτά συµπεριλαµβάνονται και τα κρυφά (HIDEN) παράθυρα, αρ-κεί να είναι ϐέβαια ανοικτά, δε συµπεριλαµβάνονται όµως τα παράθυρα που είναι απόγονοιάλλων παραθύρων (child windows)

• Msg: Καθορίζει την ταυτότητα του µηνύµατος που πρόκειται να αποσταλεί.

• wParam: Περιέχει συµπληρωµατικές πληροφορίες για το µήνυµα που πρόκειται να απο-σταλεί.

• lParam: Περιέχει συµπληρωµατικές πληροφορίες για το µήνυµα που πρόκειται να απο-σταλεί. ΄Οπως έχει αναφερθεί η τιµή αυτής της παραµέτρου εξαρτάται και ερµηνεύεται κάθεϕορά διαφορετικά, ανάλογα µε το µήνυµα που αποστέλλεται.

Επιστρεφόµενη τιµή

Η επιστρεφόµενη τιµή της συνάρτησης εξαρτάται από την επεξεργασία του µηνύµατος και είναι ενγένει διαφορετική κάθε µήνυµα.

Αν η λαβή στο παράθυρο δεν είναι γνωστή τότε αυτή µπορεί να ληφθεί από τον τίτλο του παραθύρουµε τη χρήση της συνάρτησης FindWindow:

41 e-mail: [email protected]

Page 42: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 6.3: Η συνάρτηση εύρεσης της λαβής ενός παραθύρου από τον τίτλο του.1 HWND FindWindow(2 LPCTSTR lpClassName, // pointer to class name3 LPCTSTR lpWindowName // pointer to window name4 ) ;

Παράµετροι

• lpClassName: Το όνοµα της κλάσης του παραθύρου. Αν το όνοµα της κλάσης τουπαραθύρου είναι άγνωστο τότε αυτή η παράµετρος λαµβάνει ένα κενό κείµενο ¨¨.

• lpWindowName: Το όνοµα του τίτλου του παραθύρου. Γίνεται διάκριση πεζών και κεφα-λαίων.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής, τότε αυτή επιστρέφει µια λαβή στο πιο ψηλά στη λίσταπαράθυρο, του οποίου το όνοµα της κλάσης και το όνοµα του τίτλου ταιριάζει µε τα ονόµατα πουδόθηκαν στις παραµέτρους της συνάρτησης. Να σηµειωθεί ότι πρέπει να δοθεί ολόκληρος και όχιµέρος του ονόµατος τίτλου και κλάσης. Επίσης, προσοχή χρειάζεται διότι γίνεται διάκριση µεταξύπεζών και κεφαλαίων χαρακτήρων. Τέλος, η συνάρτηση δεν κάνει αναζήτηση σε παράθυρα πουείναι απόγονοι άλλων παραθύρων.

Αν η κλήση της συνάρτησης αποτύχει η επιστρέφόµενη τιµή είναι NULL και συνάρτηση GetLast

Error µας πληροφορεί για το σφάλµα το οποίο συνέβη.

Παρακάτω παρουσιάζεται ένα παράδειγµα αποστολής του µηνύµατος WM_CLOSE σε ένα παράθυροµε τίτλο test window

Listing 6.4: Παράδειγµα αποστολής µυνήµατος σε παράθυρο.1 HWND hwnd=FindWindow("","test window");2 if (hwnd==NULL)MessageBox(NULL,"test window not found","",MB_OK);return;3 SendMessage(hwnd,WM_CLOSE, NULL, NULL);

Αν ϑέλουµε να στείλουµε το µύνηµα σε όλα τα ανοικτά παράθυρα µπορούµε να πούµε:

1 SendMessage(WM_BROADCAST,WM_CLOSE, NULL, NULL);

∆οκιµάστε το !

6.2.2 Λήψη µυνηµάτων

΄Οπως έχει αναφερθεί τα µυνήµατα που αποστέλλονται σε ένα παράθυρο λαµβάνονται από τηνσυνάρτηση επανάκλησης (CALLBACK function) WndProc του παραθύρου. Ας υποθέσουµε λοιπόνότι έχουµε µια ϕόρµα1 -δες σχήµα 6.1 (σελ. 43)-.

1Η κλάση της ϕόρµας κληρονοµεί από τη ϐασική κλάση TForm η οποία κληρονοµεί από τη ϐασική κλάσηTWinControl

42 e-mail: [email protected]

Page 43: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Σχήµα 6.1: Ιεραρχία της κλάσης TForm στο περιβάλλον CBuilder

Listing 6.5: Unit1.h ∆ήλωση (definition) µιας ϕόρµας.1 class TForm1 : public TForm2 3 __published: // IDE−managed Components4 TButton ∗Button1;5 TButton ∗Button2;6 TButton ∗Button3;7 TRichEdit ∗m_Text;8 void __fastcall Button1Click(TObject ∗Sender);9 void __fastcall Button2Click(TObject ∗Sender);

10 void __fastcall Button3Click(TObject ∗Sender);11 private: // User declarations12 void __fastcall WndProc(Messages::TMessage &Message);13 public: // User declarations14 void send_the_word(NEW_WORD ∗w);15 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−16 __fastcall TForm1(TComponent∗ Owner);17 ;

Στο Listing 6.5 Απεικονίζεται η δήλωση (definition) της κλάσης TForm1. Προσέξτε τη γραµµή 12όπου δηλώνεται η συνάρτηση WndProc. Με τη δήλωση αυτή υπερφορτώνουµε την WndProc τηςϐασικής τάξης TWinControl από την οποία κληρονοµεί η TForm1. Η ανάπτυξη της WndProcγίνεται στο αρχείο ανάπτυξης (implementation file) Unit1.cpp. Ο κώδικας αυτός ϕαίνεται στοListing 6.6

Listing 6.6: Unit1.h ∆ήλωση (definition) µιας ϕόρµας.1 void __fastcall TForm1::WndProc(Messages::TMessage &Message)2 3 if (Message.Msg==WM_CLOSE)MessageBox(NULL,"I received the message WN_QUIT","",MB_OK);

43 e-mail: [email protected]

Page 44: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

4 TForm::WndProc(Message);5

Το ερώτηµα είναι : Πότε καλείται αυτή η συνάρτηση ; Η συνάρτηση αυτή καλείται κάθε ϕοράπου η ϕόρµα λαµβάνει ένα µύνηµα. Και που υπάρχει η πληροφορία του µυνήµατος π.χ. τιµύνηµα είναι πότε στάλθηκε κ.τ.λ.; Η πληροφορία αυτή υπάρχει στην παράµετρο της συνάρτησηςMessages::TMessage & Message. Επειδή ϐέβαια είµαστε στο οπτικό περιβάλλον Builderτο περιβάλλον αυτό έχει δικιά του τάξη µυνήµατος την τάξη TMessage που είναι εξέλιξη της δοµήςMSG του winAPI που είδαµε. Συνεπώς πρέπει να χρησιµοποιήσουµε τα µέλη της TMessage καιόχι της MSG για να δούµε τις πληροφορίες του µυνήµατος. Αυτά τα ϐλέπουµε από το HELP µη ταϑέλουµε και όλα έτοιµα !

6.2.3 ∆ιαδιεργασιακή επικοινωνία µε µυνήµατα

΄Οπως έχουµε αναφέρει στο κεφάλαιο 5, τα µυνήµατα είναι ένας τρόπος διαδιεργασιακής επικοι-νωνίας. Μέσω µυνηµάτων µπορούµε να µεταφέρουµε δεδοµένα από µια εφαρµογή σε µια άλλη.Αυτό γίνεται µε ένα ειδικό τύπο µυνήµατος το µύνηµα WM_COPYDATA. Το λειτουργικό σύστηµααναλαµβάνει να µεταφέρει τα δεδοµένα από το χώρο διευθύνσεων της διεργασίας που στέλνει τοµύνηµα στο χώρο διευθύνσεων της διεργασίας που το λαµβάνει. Το µύνηµα αυτό έχει ορισµένεςιδιαιτερότητες, τις οποίες ϑα δούµε στη συνέχεια.

Listing 6.7: Η δοµή COPYDATASTRUCT

1 typedef struct tagCOPYDATASTRUCT // cds2 DWORD dwData;3 DWORD cbData;4 PVOID lpData;5 COPYDATASTRUCT;

Μέλη

• dwData: Αριθµός που χρησιµοποιείται στην περίπτωση που ϑέλουµε να µεταφέρουµεαριθµητικά δεδοµένα.

• lpData: Η αρχική διεύθυνση µνήµης στην οποία είναι αποθηκευµένα τα δεδοµένα.

• cbData: Ο αριθµός των bytes που ϑα µεταφερθούν ξεκινώντας από τη διεύθυνσηlpData.

Αν για παράδειγµα έχουµε ένα πίνακα από N αριθµούς τύπου double και ϑέλουµε να τον στείλου-µε µε µύνηµα σε ένα παράθυρο µε τίτλο mytest µπορούµε να χρησιµοποιήσουµε τον ακόλουθοκώδικα:

Listing 6.8: Παράδειγµα αποστολής δεδοµένων σε παράθυρο διεργασίας.1 int N=100;2 double ∗p=new double[N];3 HWND hwnd = FindWindow(0,"mytest");4 if (! IsWindow(hwnd))MessageBox(NULL,"mytest window can not be found!","Warning",MB_OK);

44 e-mail: [email protected]

Page 45: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

5 COPYDATASTRUCT cds;6 cds.dwData = 0;7 cds.cbData = N∗sizeof(double);//DataSize;8 cds.lpData = p;9 SendMessage(

10 hwnd,11 WM_COPYDATA,12 0,13 reinterpret_cast <LPARAM> (&cds)14 ) ;15 delete [] p;

Αν έχουµε να µεταφέρουµε ετεροειδή δεδοµένα π.χ. ακέραιους, κείµενα, δεκαδικούς κ.τ.λ. τότετα ενθυλακώνουµε σε µια δοµή (struct) και στη συνέχεια δηµιουργούµε µια µεταβλητή τύπουCOPYDATASTRUCT και ενηµερώνουµε τα µέλη lpData, cbData µε την αρχική διεύθυνση τηςδοµής και το µέγεθος της, αντίστοιχα.

΄Οσον αφορά τη λήψη του µηνύµατος υπάρχει και εδώ µια ιδιαιτερότητα. Το µύνηµα ϕυσικά ϑα λη-ϕθεί από την WndProc του παράθυρου στόχος, όµως η WndProc δεν µπορεί να αποκωδικοποιήσειτα δεδοµένα ! Για την σωστή αποκωδικοποίηση των δεδοµένων πρέπει να δηλώσουµε εµείς ποιασυνάρτηση ϑα ενεργοποιείται κάθε ϕορά που το παράθυρο ϑα λαµβάνει το µύνηµα WM_COPYDATA.Για να γίνει αυτό πρέπει να απεικονίσουµε το µύνηµα WM_COPYDATA σε µια συνάρτηση (µέλοςτης κλάσης του παραθύρου) η οποία ϑα καλείται κάθε ϕορά που το παράθυρο µας λαµβάνει τοσυγκεκριµένο µύνηµα. Εδώ δηµιουργούµε µια συνάρτηση µέλος την WMCopyData(TMessage&Message); ∆ες γραµµή 8 του Listing 6.9 . Στη συνέχεια αναπτύσουµε την συνάρτηση αυτή στοimplementation file της ϕόρµας δες Listing 6.10 και στη συνέχεια δηλώνουµε ότι κάθε ϕο-ϱά που η ϕόρµα ϑα λαµβάνει το µύνηµα WM_COPYDATA ϑα καλείται η συνάρτηση WMCopyData.Αυτό γίνεται στις γραµµές 11,12,13 του Listing 6.9 .

Listing 6.9: Απεικόνιση του µυνήµατος WM_COPYDATA στη συνάρτηση WMCopyData

1 class TTForm1 : public TForm2 3 __published: // IDE−managed Components4 TEdit ∗Edit1;5 TLabel ∗Label1;6 private: // User declarations7 void __fastcall WndProc(Messages::TMessage &Message);8 void __fastcall TTForm1::WMCopyData(TMessage& Message);9 public: // User declarations

10 __fastcall TTForm1(TComponent∗ Owner);11 BEGIN_MESSAGE_MAP12 MESSAGE_HANDLER(WM_COPYDATA, TMessage, WMCopyData)13 END_MESSAGE_MAP(TForm)14 ;

Listing 6.10: Ανάπτυξη της WMCopyData.1 void __fastcall TTForm1::WMCopyData(TMessage& Message)2 3 COPYDATASTRUCT∗ cs = (COPYDATASTRUCT∗)(Message.LParam);4 int nSize=1 + cs−>cbData/sizeof(double);

45 e-mail: [email protected]

Page 46: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

5 double ∗A=new double[nSize];//allocate memory for the data array6 A=cs−>lpData;7 //begin processing A8 .9 .

10 .11 //end processing A12 delete[]A;13

46 e-mail: [email protected]

Page 47: Ergastirio_Simioseis

Κεφάλαιο 7

Αγκίστρωση µηνυµάτων

Στόχος:

• ∆ηµιουργία (DLL’s).

• Αγκίστρωση µηνυµάτων (Message Hooking).

7.1 ΄Ασκηση:

1. Να κατασκευαστεί δυναµική ϐιβλιοθήκη (DLL), ή οποία να ϕιλοξενεί γάντζους µυνηµάτων(Hooks) ποντικιού (mouse) και πληκτρολογίου (keyboard).

2. Ο γάντζος του ποντικιού να απενεργοποιεί το δεξί πλήκτρο του ποντικιού.

3. Ο γάντζος του πληκτρολογίου να καταγράφει σε αρχείο (Log File) τα συµβάντα πληκτρολογίου(Keyboard Events) που συµβαίνουν στο σύστηµα όπως επίσης και τον τίτλο του παράθυρουστο οποίο συνέβη το συµβάν.

47

Page 48: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

7.2 Θεωρητικά Στοιχεία

΄Ενας γάντζος µυνηµάτων (message hook) είναι ένα εργαλείο του µηχανισµού διαχείρισης των µη-νυµάτων Microsoft Windows, µέσω του οποίου µια εφαρµογή µπορεί να εγκαταστήσει µιασυνάρτηση για να ελέγξει την κυκλοφορία µηνυµάτων στο σύστηµα και να επεξεργαστεί ορισµένουςτύπους µηνυµάτων πριν την άφιξη τους στον προορισµό τους. Μέσω του µηχανισµού αγκίστρωσηςµυνηµάτων είναι εφικτή:

• Η απόρριψη ενός µυνήµατος πριν ϐρει το στόχο του.

• Η τροποποίηση ενός µυνήµατος πριν ϐρει το στόχο του.

• Η Ενεργοποίηση χαρακτηριστικών του συστήµατος.

• Η Απενεργοποίηση χαρακτηριστικών του συστήµατος.

• Η Καταγραφή συµβάντων στο σύστηµα.

• Η ∆ηµιουργία σεναρίων ως χρονοσειράς συµβάντων.

Μια διαδικασία γάντζων (hook function) µπορεί να ελέγξει τα γεγονότα που συνδέονται είτε µε ένασυγκεκριµένο νήµα είτε µε όλα τα νήµατα στο σύστηµα.

7.2.1 Εγκατάσταση γάντζων

΄Ενα µύνηµα πριν επιτύχει το στόχο του περνά διαδοχικά από ένα σύνολο συναρτήσεων γάντζωνπου ονοµάζεται αλυσίδα γάντζων (hook chain). Στην αλυσίδα αυτή έχουµε τη δυνατότητα να προ-σθέσουµε τη δική µας συνάρτηση για να επεξεργαστούµε το µύνηµα µε το δικό µας τρόπο.

Η συνάρτηση SetWindowsHookEx µπορεί να χρησιµοποιηθεί από µια εφαρµογή για να εγκατα-στήσει µια νέα συνάρτηση γάντζου σε µια ήδη υπάρχουσα αλυσίδα γάντζων.

Listing 7.1: Η συνάρτηση Εγκατάστασης γάντζου1 HHOOK SetWindowsHookEx(2 int idHook, // type of hook to install3 HOOKPROC lpfn, // address of hook procedure4 HINSTANCE hMod, // handle of application instance5 DWORD dwThreadId // identity of thread to install hook for6 ) ;

Παράµετροι

• idHook: Καθορίζει τον τύπο συνάρτησης γάντζου που εγκαθίσταται. Αυτή η παράµετροςµπορεί να έχει µια από τις ακόλουθες προκαθορισµένες τιµές :

WH_CALLWNDPROC: Εγκαθιστά µια συνάρτηση γάντζο που ελέγχει τα µηνύµατα πριν απότην αποστολή τους στη διαδικασία παραθύρων προορισµού.

48 e-mail: [email protected]

Page 49: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

WH_CALLWNDPROCRET: Εγκαθιστά µια συνάρτηση γάντζο που ελέγχει τα µηνύµατα µετάτην επεξεργασία τους από τα παράθυρα προορισµού.

WH_CBT: Εγκαθιστά µια συνάρτηση γάντζο που αποµνηµονεύει ειδικού τύπου µυνή-µατα χρήσιµα σε εφαρµογές κατάρτισης ϐασισµένες σε υπολογιστή (Computer Based

Training programs -CBT-.)

WH_DEBUG: Εγκαθιστά µια συνάρτηση γάντζο χρήσιµη για άλλες διαδικασίες γάντζων.WH_GETMESSAGE: Εγκαθιστά µια συνάρτηση γάντζο που ελέγχει τα µηνύµατα που ταχυ-

δροµούνται σε µια σειρά αναµονής µηνυµάτων.WH_JOURNALRECORD: Εγκαθιστά µια συνάρτηση γάντζο που καταγράφει τα µηνύµατα

εισαγωγής που ταχυδροµούνται στη σειρά αναµονής µηνυµάτων συστήµατος. Αυτός ογάντζος είναι χρήσιµος για µακροεντολές καταγραφής.

WH_JOURNALPLAYBACK: Εγκαθιστά µια συνάρτηση γάντζο που ταχυδροµεί (Αναπαρά-γει) τα µηνύµατα που καταγράφονται προηγουµένως από µια συνάρτηση γάντζο τύπουWH_JOURNALRECORD.

WH_KEYBOARD: Εγκαθιστά µια συνάρτηση γάντζο που ελέγχει τα µηνύµατα πληκτρολό-γησης.

WH_MOUSE: Εγκαθιστά συνάρτηση γάντζο που ελέγχει τα µηνύµατα ποντικιού.WH_SYSMSGFILTER: Εγκαθιστά µια συνάρτηση γάντζο που ελέγχει τα µηνύµατα που

παράγονται ως αποτέλεσµα ενός γεγονότος εισαγωγής σε ένα πλαίσιο διαλόγου, το πα-ϱάθυρο µηνυµάτων, και τα menus.

WH_SHELL: Εγκαθιστά µια συνάρτηση γάντζο που λαµβάνει µυνήµατα που ταχυδροµούν-ται σε εφαρµογές κελύφους του λειτουργικού.

• lpfn: ∆είκτης (pointer) στη συνάρτηση γάντζου. Η συνάρτηση γάντζου µπορεί να ϕιλοξε-νηθεί στην εφαρµογή που εγκαθιστά το γάντζο. Στην περίπτωση αυτή ο γάντζος αγκιστρώνειµόνο τα µυνήµατα που προορίζονται για την εφαρµογή που εγκατέστησε το γάντζο, ή κάποιονήµα µε ταυτότητα dwThreadId, το οποίο δηµιουργήθηκε από την εφαρµογή εγκατάστα-σης. Αν ϑέλουµε ο γάντζος να αγκιστρώνει µυνήµατα που αναφέρονται σε όλα τα νήµατατου συστήµατος τότε η συνάρτηση γάντζου πρέπει να ϕιλοξενηθεί σε ϐιβλιοθήκη δυναµικήςσύνδεσης (DLL).

• hMod: Προσδιορίζει το χειριστήριο (handle) στη ϐιβλιοθήκη δυναµικής σύνδεσης (DLL) πουϕιλοξενεί τη συνάρτηση γάντζου που δείχνεται από την παράµετρο lpfn.

Αν η διαδικασία γάντζων ϕιλοξενείται στην εφαρµογή που εγκαθιστά τον γάντζο πρέπει να τεθείhMod=NULL. Επιπρόσθετα αν dwThreadId=0, αγκιστρώνουµε µυνήµατα που αναφέρονταισε όλα τα νήµατα που δηµιουργήθηκαν από την εφαρµογή που εγκαθιστά και ϕιλοξενεί τησυνάρτηση γάντζου. Ενώ αν dwThreadId6= 0 τότε ϕιλτράρουµε µυνήµατα που αναφέρονταισε ένα νήµα µε ταυτότητα dwThreadId, το οποίο όµως έχει δηµιουργηθεί από την εφαρµογήεγκατάστασης.

• dwThreadId: ΄Εχουµε πει ήδη αρκετά για την παράµετρο αυτή. Προσδιορίζει την ταυ-τότητα του νήµατος, µε το οποίο πρόκειται να συνδεθεί η διαδικασία γάντζων. Εάν αυτή ηπαράµετρος είναι µηδέν, η διαδικασία γάντζων συνδέεται µε όλα τα υπάρχοντα νήµατα.

49 e-mail: [email protected]

Page 50: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Επιστρεφόµενη τιµή

Εάν η λειτουργία εγκατάστασης επιτύχει η επιστρεφόµενη τιµή είναι η τιµή που έχει το χειριστήριοστη συνάρτηση του γάντζου που εγκαθίσταται. Εάν η λειτουργία αποτύχει, η επιστρεφόµενη τιµήείναι NULL.

Παρατηρήσεις

• Εαν hMod=NULL και dwThreadId=0 ή ισούται µε την ταυτότητα ενός νήµατος που δη-µιουργείται από µια άλλη διαδικασία, τότε η εγκατάσταση αποτυγχάνει.

• Η σύνδεση µε την επόµενη διαδικασία γάντζων καλώντας τη λειτουργία CallNextHookEx

είναι προαιρετική. Μια εφαρµογή ή µια ϐιβλιοθήκη µπορεί να καλέσει την επόµενη διαδικα-σία γάντζων είτε πριν από είτε µετά από οποιαδήποτε επεξεργασία στη διαδικασία γάντζων της.Αν και η αλυσόδεση στον επόµενο γάντζο είναι προαιρετική, συστήνεται ιδιαίτερα η κλήση τηςCallNextHookEx διαφορετικά διακόπτεται η αλυσίδα των γάντζων και οι άλλες εφαρµογέςπου έχουν εγκαταστήσει τους δικούς τους γάντζους µπορεί να συµπεριφερθούν ασυνεπώς.

• Πριν ολοκληρώσει µια εφαρµογή που έχει εγκαταστήσει ένα γάντζο ϑα πρέπει να καλέσει τηλειτουργία UnhookWindowsHookEx για να αφαιρέσει το γάντζο που εγκατέστησε από τηναλυσίδα των γάντζων του συστήµατος και να απελευθερώσει τους πόρους που συνδέονται µετο συγκεκριµένο γάντζο.

7.2.2 Η συνάρτηση γάντζος

Η συνάρτησης γάντζου είναι συνάρτηση επανάκλησης (callback function) και έχει την ίδια µορφήγια όλους τους τύπους γάντζων. Αυτό που διαφοροποιείται από τύπο σε τύπο είναι η ερµηνεία τωνορισµάτων που λαµβάνει ως όρισµα. Στον κώδικα 7.2 (σελ. 50) παρουσιάζεται η συνάρτηση γάντζοςπου αγκιστρώνει µυνήµατα ποντικιού. Για το συγκεκριµένο τύπο η ερµηνεία των παραµέτρων έχειως εξής :

Listing 7.2: Η µορφή της συνάρτησης γάντζου ποντικιού1 LRESULT CALLBACK MouseProc(2 int nCode, // hook code3 WPARAM wParam, // message identifier4 LPARAM lParam // mouse coordinates5 ) ;

Παράµετροι

• nCode: Ακέραιος κωδικός, ο οποίος καθορίζει τον τρόπο µε τον οποίο ϑα γίνει η επεξεργασίατου µηνύµατος. Αυτή η παράµετρος µπορεί να λάβει µια από τις ακόλουθες τιµές :

HC_ACTION: Οι παράµετροι wParam και lParam περιέχουν πληροφορίες για το µήνυµαποντικιού.

50 e-mail: [email protected]

Page 51: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

HC_NOREMOVE: Οι παράµετροι wParam και lParam περιέχουν τις πληροφορίες για έναµήνυµα ποντικιών, και το µήνυµα ποντικιών δεν έχει αφαιρεθεί από τη σειρά αναµονήςµηνυµάτων. (Μια εφαρµογή κάλεσε τη λειτουργία PeekMessage, χρησιµοποιώντας τησηµαία PM_NOREMOVE.)Εάν nCode < 0, η συνάρτηση γάντζων πρέπει να περάσει το µήνυµα στη συνάρτησηCallNextHookEx() χωρίς περαιτέρω επεξεργασία και πρέπει να επιστρέψει την τιµήπου επιστρέφεται από την CallNextHookEx.

• wParam: Καθορίζει τον τύπο του µυνήµατος. π.χ. WM_RMOUSEDOWN, WM_RMOUSEUP

• lParam: ∆είκτης (pointer) σε µια δοµή τύπου MOUSEHOOKSTRUCT. ∆ες κώδικα 7.3 (σελ.51).

Επιστρεφόµενη τιµή

Αν η επιστρεφόµενη τιµή είναι µηδέν τότε το µύνηµα επιστρέφει στο σύστηµα και συνεχίζει τηνπορεία του στον προορισµό του. Αν η επιστρεφόµενη τιµή είναι διάφορη του µηδενός τότε τοµήνυµα απορρίπτεται.

Παρατηρήσεις

• Η συναρτήσεις γάντζου ποντικιού δεν πρέπει να εγκαθιστούν συναρτήσεις γάντζων JournalPlaybackProc.

• Μια εφαρµογή εγκαθιστά µια διαδικασία γάντζων ϑέτοντας ως όρισµα τον τύπο γάντζων καιτης διεύθυνσης της διαδικασίας γάντζων σε µια κλήση της SetWindowsHookEx.

• Το όνοµα MouseProc δεν είναι δεσµευµένο, µπορούµε να χρησιµοποιήσουµε ότι όνοµαϑέλουµε µε τους σωστούς ϐέβαια τύπους ορισµάτων.

Listing 7.3: Η ∆οµή MOUSEHOOKSTRUCT1 typedef struct tagMOUSEHOOKSTRUCT // ms2 POINT pt;3 HWND hwnd;4 UINT wHitTestCode;5 DWORD dwExtraInfo;6 MOUSEHOOKSTRUCT;

Μεταβλητές Μέλη

• PT: Καθορίζει µια δοµή τύπου POINT που περιέχει τις Χ και Υ συντεταγµένες του δροµέα,στο σύστηµα συντεταγµένων της οθόνης.

• hwnd: Καθορίζει το χειριστήριο handle του παραθύρου που ϑα λάβει το µήνυµα ποντικιού.

• wHitTestCode: Καθαρίζει την τιµή του χτυπήµατος - δοκιµής HIT-TEST. Για ένανκατάλογο τιµών χτυπηµάτων δοκιµής, δείτε την περιγραφή του µηνύµατος WM_NCHITTEST.

51 e-mail: [email protected]

Page 52: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

• dwExtraInfo: Πρόσθετες πληροφορίες που συνδέονται µε το µήνυµα.

Listing 7.4: Υλοποίηση του γάντζου του ποντικιού1 extern "C" __declspec(dllexport) LRESULT __stdcall CALLBACK MouseProc(int nCode, WPARAM

wParam, LPARAM lParam)2 3 //disable right mouse button4 if (wParam==WM_RBUTTONDOWN || wParam==WM_RBUTTONUP)5 6 return 1;7 8 return 0;9

Listing 7.5: Η εγκατάσταση του γάντζου του ποντικιού1 void __fastcall TForm1::Button1Click(TObject ∗Sender)2 3 HOOKPROC f_mouse;4 //load the dll which contains the hooking procedures5 hdll = LoadLibrary((LPCTSTR) "C:\\Program Files\\Borland\\CBuilder6\\Projects\\hookDll\\

hookDll.dll");6 if (hdll==NULL)MessageBox(NULL,"dll not loaded","",MB_OK);return;7 //get the address of the mouse procedure.8 f_mouse = (HOOKPROC)GetProcAddress(hdll, "MouseProc");9 if (f_mouse==NULL)MessageBox(NULL,"MouseProc Function Address Not Found","",MB_OK);

return;10 //install the mouse HOOK procedure11 h_mouse = SetWindowsHookEx(WH_MOUSE,f_mouse,hdll,0);12 if (! h_mouse)MessageBox(NULL,"Mouse Hook Installation Failed","",MB_OK);13

Listing 7.6: Απεγκατάσταση του γάντζου του ποντικιού1 void __fastcall TForm1::Button2Click(TObject ∗Sender)2 3 UnhookWindowsHookEx(h_mouse);4 FreeLibrary(hdll);5

52 e-mail: [email protected]

Page 53: Ergastirio_Simioseis

Κεφάλαιο 8

Αντικείµενα Αναγγελίας (NotificationObjects)

Στόχος:

• Εισαγωγή στα αντικείµενα αναγγελίας (Notification Objects).

• Αξιοποίηση τους για αναγγελίες συµβάντων που σχετίζονται µε το σύστηµα αρχείων.

8.1 ΄Ασκηση:

1. Να γίνει µια εφαρµογή, η οποία να καταγράφει τις µεταβολές που συµβαίνουν σε ένα κατά-λογο και στους υποκαταλόγους του.

2. Το όνοµα του καταλόγου να δίνεται από µια ϕόρµα σε ένα (Edit Box), όπως επίσης και τοόνοµα του αρχείου στο οποίο ϑα καταγράφονται τα συµβάντα στη µορφή: ΄Ονοµα αρχείου,Τύπος µεταβολής, Ηµεροµηνία & ΄Ωρα

53

Page 54: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

8.2 Θεωρητικά Στοιχεία

Τα αντικείµενα αναγγελίας (Notification Objects) είναι αντικείµενα συγχρονισµού, τα οποία δη-µιουργούνται από από τον πυρήνα µε ϐάση ένα σύνολο ιδιοτήτων που εµείς ϑέτουµε ως ϕίλτρο καισηµατοδοτείται κάθε ϕορά που συµβαίνει ένα γεγονός στο σύστηµα αρχείων, το οποίο ικανοποιείτο ϕίλτρο που έχει τεθεί. Πίσω από τα αντικείµενα αναγγελίας κρύβονται κανονικά αντικείµενασυγχρονισµού, τα οποία όµως είναι εξειδικευµένα και σηµατοδοτούνται αυτόµατα από τον πυρήνακάθε ϕορά που συµβαίνει ένα γεγονός στο σύστηµα αρχείων, το οποίο ικανοποιεί το ϕίλτρο πουέχει τεθεί. ΄Εχουµε τη δυνατότητα να παρακολουθήσουµε ένα κατάλογο ένα αρχείο ή ακόµα καιένα ολόκληρο δίσκο και να ανιχνεύσουµε συµβάντα όπως δηµιουργία, µετονοµασία, διαγραφή,αλλαγή ιδιοτήτων, κ.λ.π. Το WIN32 API µας παρέχει τρεις συναρτήσεις µε τις οποίες µπορούµενα δηµιουργήσουµε και να χρησιµοποιήσουµε αντικείµενα αναγγελίας :

• FindFirstChangeNotification()

• FindNextChangeNotification()

• FindCloseChangeNotification()

Στο σηµείο αυτό πρέπει να σηµειωθεί ότι το πρόθεµα Find είναι ατυχές. στην πραγµατικότητα ηπρώτη συνάρτηση δηµιουργεί αντί να αναζητά ένα αντικείµενο αναγγελίας όπως περιµένει κανείςπαρατηρώντας το όνοµα της. Επίσης, δεν έχουµε τη δυνατότητα να χρησιµοποιήσουµε τη συνάρτη-ση CloseHandle, µε όρισµα το χειριστήριο σε κάποιο αναγγελίας για να το απελευθερώσουµε όπωςσυµβαίνει µε όλα τα υπόλοιπα αντικείµενα συγχρονισµού (σηµαφόροι, αντικείµενα αµοιβαίου απο-κλεισµού κ.τ.λ.). Τέλος, υπογραµµίζεται άλλη µια ϕορά η µεγάλη διαφορά που υπάρχει µεταξύτων αντικειµένων ενηµέρωσης και των κλασικών αντικειµένων συγχρονισµού που έχουµε δει µέχριτώρα: Στα αντικείµενα ενηµέρωσης το αντικείµενο δηµιουργείται από τον πυρήνα µε ϐάση έναϕίλτρο από ιδιότητες που ϑέτει ο χρήστης και σηµατοδοτείται επίσης από τον πυρήνα κάθε ϕοράπου ικανοποιείται το ϕίλτρο το οποίο έχει τεθεί.

΄Οταν γίνεται κλήση στη συνάρτηση FindFirstChangeNotification(), για κάποιο συγκε-κριµένο τύπο αλλαγών δηµιουργείται ένα κρυφό αντικείµενο συγχρονισµού, το οποίο, είναι σε µησηµατοδοτούµενη κατάσταση. ΄Οταν ανιχνευθεί ένα συµβάν, το οποίο ικανοποιεί τη συνθήκη ϕίλ-τρου, η κατάσταση µεταβάλλεται σε σηµατοδοτούµενη και ακολουθεί η επεξεργασία του συµβάντος.

Η συνάρτηση FindFirstChangeNotification(), η οποία λαµβάνει ως όρισµα ένα συγκε-κριµένο ϕίλτρο µας επιστρέφει ένα χειριστήριο αναγγελίας αλλαγής, το οποίο µπορούµε να χρη-σιµοποιήσουµε σε µια συνάρτηση αναµονής προκειµένου να περιµένουµε για κάποια αλλαγή, ηοποία ικανοποιεί το ϕίλτρο που έχει καθοριστεί. Στη συνέχεια αφού επεξεργαστούµε το συγκε-κριµένο γεγονός, έχουµε τη δυνατότητα να καλέσουµε τη συνάρτηση FindNextChangeNotifi

cation(), η οποία αποσηµατοδοτεί το κρυφό αντικείµενο συγχρονισµού ώστε να περιµένουµετο επόµενο συµβάν, το οποίο ϑα ικανοποιεί το ϕίλτρο. Η απελευθέρωση του αντικειµένου αναγ-γελίας και των πόρων που σχετίζονται µε αυτό είναι δυνατόν να γίνει µε κλήση της συνάρτησηςFindCloseChangeNotification().

Η ακριβής σύνταξη της συνάρτησης FindFirstChangeNotification() έχει ως εξής :

54 e-mail: [email protected]

Page 55: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Listing 8.1: Η συνάρτηση δηµιουργίας ενός αντικειµένου αναγγελίας1 HANDLE FindFirstChangeNotification(2 LPCTSTR lpPathName, // pointer to name of directory to watch3 BOOL bWatchSubtree, // flag for monitoring directory or directory tree4 DWORD dwNotifyFilter // filter conditions to watch for5 ) ;

Παράµετροι

• lpPathName: ∆είκτης (pointer) σε ορµαθό χαρακτήρων (string) που καθορίζει το το όνοµατου αρχείου ή του καταλόγου που ϑέλουµε να παρακολουθήσουµε.

• bWatchSubtree: Καθορίζει αν ϑα παρακολουθεί µόνο τον κατάλογο lpPathName ή καιτους υποκαταλόγους του. Αν η τιµή αυτή είναι TRUE παρακολουθούνται και οι υποκατάλογοι.

• dwNotifyFilter: Καθορίζει τις συνθήκες που πρέπει να ικανοποιεί µια αλλαγή στοσύστηµα αρχείων ώστε να σηµατοδοτήσει το αντικείµενο αναγγελίας. Είναι προφανές ότι ανπαρακολουθούµε κάποιο αρχείο αντί για κατάλογο παράµετρος αυτή αγνοείται. Η τιµή τηςκαθορίζεται ως µία ή ως bitwise or περισσότερων από τις ακόλουθες τιµές :

FILE_NOTIFY_CHANGE_FILE_NAME: Σηµατοδότηση αν συµβεί µετονοµασία, διαγραφή,δηµιουργία σε αρχείο στον υπό παρακολούθηση κατάλογο ή στους υποκαταλόγους του(αν bWatchSubtree==TRUE)

FILE_NOTIFY_CHANGE_DIR_NAME: Σηµατοδότηση αν συµβεί µετονοµασία στο υπο πα-ϱακολούθηση κατάλογο στους υποκαταλόγους.

FILE_NOTIFY_CHANGE_ATTRIBUTES: Σηµατοδότηση αν αλλάξει κάποιο ιδιότητα τουαρχείου η καταλόγου που παρακολουθούµε π.χ. ένα αρχείο γίνεται read only.

FILE_NOTIFY_CHANGE_SIZE: Σηµατοδότηση αν τροποποιηθεί το µέγεθος του αρχείουη του καταλόγου που παρακολουθούµε. Για να συµβεί σηµατοδότηση ϑα πρέπει τοαρχείο να έχει γράψει τα περιεχόµενα του στο δίσκο.

FILE_NOTIFY_CHANGE_LAST_WRITE: Σηµατοδότηση όταν τροποποιηθεί η ηµεροµηνίαη ώρα της τελευταίας τροποποίησης του αρχείου η του καταλόγου/υποκαταλόγου πουπαρακολουθούµε.

FILE_NOTIFY_CHANGE_SECURITY: Σηµατοδότηση αν συµβεί τροποποίηση των ιδιοτή-των ασφαλείας του αρχείου ή του καταλόγου/υποκαταλόγου που παρακολουθούµε.

Επιστρεφόµενη τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής η επιστρεφόµενη τιµή είναι µια λαβή στο αντικείµενοαναγγελίας. ∆ιαφορετικά η επιστρεφόµενη τιµή είναι INVALID_HANDLE_VALUE και πρέπει νακληθεί η GetLastError για αναλυτικές πληροφορίες σχετικά µε το λόγο αποτυχίας.

Η συνάρτηση BOOL FindNextChangeNotification(HANDLE hChangeHandle) Αποση-µατοδοτεί το κρυφό αντικείµενο συγχρονισµού και το ετοιµάζει για να σηµατοδοτηθεί από το επό-µενο συµβάν που ϑα ικανοποιεί το ϕίλτρο που έχει τεθεί. Ως όρισµα λαµβάνει τη λαβή που έχειεπιστραφεί από την FindFirstChangeNotification.

55 e-mail: [email protected]

Page 56: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

Η συνάρτηση BOOL FindCloseChangeNotification(HANDLE hChangeHandle) απελευ-ϑερώνει τους πόρους που σχετίζονται µε τη λαβή του αντικειµένου αναγγελίας και σταµατά τηνπαρακολούθηση των τροποποιήσεων του συστήµατος αρχείων.

Εδώ είναι ένα κείµενο πρίν από το εξαµπλε.

Παράδειγµα

΄Εστω ότι ϑέλουµε να ενηµερωθούµε για οποιαδήποτε αλλαγή συµβεί σε οποιοδήποτε αρχείο ήυποκατάλογο του καταλόγου c:\temp\. Κάθε ϕορά που συµβαίνει µια αλλαγή να µας ενηµερώνει.Στον κώδικα 8.2 παρουσιάζεται η σχετική αντιµετώπιση.

Listing 8.2: Παράδειγµα1 #include <windows.h>2 #include <iostream.h>3 void main()4 5 DWORD dwNotifyFilter=FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |

FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY;

6 SetLastError(0);7 HANDLE hNotify=FindFirstChangeNotification("c:\\temp\",TRUE,dwNotifyFilter);8 if (hNotify==INVALID_HANDLE_VALUE)ReportError();exit(1);;9 for (;;)

10 11 WaitForSingleObject(hNotify,INFINITE);12 cout<<endl<<"Something in directory c:\\temp has changed"<<endl;13 FindFirstChangeNotification(hNotify);14 15 FindCloseChangeNotification(hNotify);16 return;17

Στον κώδικα 8.2 παρατηρούµε ότι η διαδικασία παρακολούθησης υλοποιείται σε ένα ατέρµονοϐρόγχο. Για το λόγο αυτό η συνήθης αντιµετώπιση είναι ο ϐρόγχος αυτός να υλοποιείται σε µιανηµατοσυνάρτηση. Με τον τρόπο αυτό µπορούµε να παρακολουθήσουµε και περισσότερα του ενόςαρχεία η καταλόγους δηµιουργώντας ένα νήµα για το κάθε ένα, επιτρέποντας έτσι στο κυρίωςπρόγραµµα να εκτελείται ανεπηρέαστα.

΄Ενα άλλο σηµαντικό Ϲήτηµα είναι µε τα αντικείµενα αναγγελίας µπορούµε να ειδοποιηθούµε µόνογια το γεγονός ότι κάτι έχει συµβεί που ικανοποιεί το ϕίλτρο που έχουµε ϑέσει. ∆εν λαµβάνουµεόµως πληροφορία για το τι ακριβώς συνέβη π.χ. ποιο αρχείο του καταλόγου/υποκαταλόγου άλλαξεκαι τι ακριβώς µεταβολή υπήρξε. Στα winNT υπάρχει µια ϐοηθητική συνάρτηση που ασχολείταιακριβώς µε αυτό η ReadDirectoryChangesW. Η σύνταξη της έχει ως εξής :

Listing 8.3: Παράδειγµα1 BOOL ReadDirectoryChangesW(2 HANDLE hDirectory, // handle to the directory to be watched3 LPVOID lpBuffer, // pointer to the buffer to receive the read results4 DWORD nBufferLength, // length of lpBuffer

56 e-mail: [email protected]

Page 57: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

5 BOOL bWatchSubtree, // flag for monitoring directory or directory tree6 DWORD dwNotifyFilter, // filter conditions to watch for7 LPDWORD lpBytesReturned, // number of bytes returned8 LPOVERLAPPED lpOverlapped, // pointer to structure needed for overlapped I/O9 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // pointer to completion routine

10 ) ;

Παράµετροι

• hDirectory: Λαβή στον κατάλογο η το αρχείο που πρόκειται να παρακολουθηθεί. Το αρ-χείο αυτό ϑα πρέπει να έχει απαραίτητα το δικαίωµα πρόσβασης FILE_LIST_DIRECTORY.Η λαβή στον κατάλογο παρουσιάζεται στο Listing 9.2 (σελ. 58).

• lpBuffer: ∆είκτης σε δοµή τύπου FILE_NOTIFY_INFORMATION Πληροφορίες για τηδοµή αυτή δίνονται παρακάτω. Στη δοµή FILE_NOTIFY_INFORMATION αποθηκεύονταιπληροφορίες που αφορούν την µεταβολή που συνέβη.

• nBufferLength: Καθορίζει το µήκος του lpBuffer σε bytes.

• bWatchSubtree: Καθορίζει αν ϑα παρακολουθείται ολόκληρο το υποδένδρο του κατα-λόγου που καθορίζει η λαβή hDirectory ή αν ϑα παρακολουθείται µόνο ο συγκεκριµένοςκατάλογος.

• dwNotifyFilter: Καθορίζει το ϕίλτρο των συµβάντων, για τα οποία γίνεται παρακολού-ϑηση. (δες και προηγούµενη παράγραφο)

• lpBytesReturned: Καθορίζει τον αριθµό των bytes που γράφτηκαν στον lpBuffer.

• lpOverlapped: Χρησιµοποιείται για ασύγχρονη λειτουργία αναγγελίας. Σε κάθε άλληπερίπτωση είναι NULL.

• lpCompletionRoutine: ∆είκτης σε συνάρτηση που καλείται όταν ολοκληρωθεί η λει-τουργία της αναγγελίας.

Επιστρεφόµενη Τιµή

Αν η κλήση της συνάρτησης είναι επιτυχής η επιστρεφόµενη τιµή είναι διάφορη του µηδενός. Σεπερίπτωση αποτυχίας η επιστρεφόµενη τιµή είναι µηδέν και η συνάρτηση GetLastError παρέχειαναλυτικές πληροφορίες για το λόγο της αποτυχίας.

57 e-mail: [email protected]

Page 58: Ergastirio_Simioseis

Κεφάλαιο 9

Παράρτηµα

9.1 Χρήσιµες Συναρτήσεις

Listing 9.1: Η χρήση της συνάρτησης GetLastError για την αναφορά ενός σφάλµατος του λειτουρ-γικού συστήµατος.

1 void ReportLastError()2 3 long n=GetLastError();4 if (n==0)return;5 char ∗lpMsgBuf;6 FormatMessage(7 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,8 NULL,9 GetLastError(),

10 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Default language11 (LPTSTR) &lpMsgBuf,12 0,13 NULL14 ) ;15 // Display the string.16 MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );17 // Free the buffer.18 LocalFree( lpMsgBuf );19 //Reset the last error counter20 SetLastError(0);21 ;

Listing 9.2: Η συνάρτηση απόκτησης λαβής σε αρχείο η κατάλογο1 HANDLE hDir = CreateFile (2 DirName, // pointer to the file name3 FILE_LIST_DIRECTORY, // access (read−write) mode4 FILE_SHARE_READ|FILE_SHARE_DELETE, // share mode5 NULL, // security descriptor6 OPEN_EXISTING, // how to create7 FILE_FLAG_BACKUP_SEMANTICS, // file attributes8 NULL // file with attributes to copy

58

Page 59: Ergastirio_Simioseis

Λειτουργικά Συστήµατα Εργαστηριακές Ασκήσεις

9 ) ;

59 e-mail: [email protected]