Υλοποίηση Σηματοφόρων 1 lalis@inf.uth.gr
Υλοποίηση σε επίπεδο συστήματος Εσωτερική κατάσταση: Ένας ακέραιος για την αποθήκευση της τιμής του σηματοφόρου Μια ουρά αναμονής που τοποθετείται ένα νήμα όταν επιχειρήσει να μειώσει την τιμή του σηματοφόρου ενώ αυτή είναι 0 Υποθέτουμε τις εξής λειτουργίες: ATOMIC_BEG / ATOMIC_END (έλεγχος εναλλαγής) Q_INIT / Q_WAIT / Q_WAKEUP (αρχικοποίηση, αναμονή, αφύπνιση σε ξεχωριστή ουρά αναμονής) H Q_WAIT οδηγεί σε εναλλαγή 2 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου */ QUEUE q; /* ουρά αναμονής */ int qlen; /* # νημάτων που περιμένουν στην q */ gsem; void init(gsem *s, int val) { s->val = val; Q_INIT(&s->q); s->qlen=0; void down(gsem *s) { ATOMIC_BEG if (s->val >= 1) { s->val--; else { s->qlen++; Q_WAIT(&s->q); ATOMIC_END void up(gsem *s) { ATOMIC_BEG if (s->qlen == 0) { s->val++; else { s->qlen--; Q_WAKEUP(&s->q); ATOMIC_END 3 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου και < 0 => #νημάτων σε αναμονή */ QUEUE q; /* ουρά αναμονής */ gsem; void init(gsem *s, int val) { s->val = val; Q_INIT(&s->q); void down(gsem *s) { ATOMIC_BEG s->val--; if (s->val < 0) { Q_WAIT(&s->q); ATOMIC_END void up(gsem *s) { ATOMIC_BEG s->val++; if (s->val <= 0) { Q_WAKEUP(&s->q); ATOMIC_END 4 lalis@inf.uth.gr
Υλοποίηση σε επίπεδο εφαρμογής Δεν υπάρχει έλεγχος της εναλλαγής Τα κρίσιμα τμήματα της υλοποίησης πρέπει να «προστατευτούν» σε επίπεδο εφαρμογής 5 lalis@inf.uth.gr
Υλοποίηση γενικών σηματοφόρων με test-and-set int val για την αποθήκευση της τιμής αυξάνεται / μειώνεται μέσω της up / down int lock για το κλείδωμα του κρίσιμου τμήματος του κώδικα των up / down γίνεται 1 μέσω TAS πριν την είσοδο στο ΚΤ γινεται 0 μετά την έξοδο από το ΚΤ Η αναμονή μέσα στην down είναι ενεργή Το ξεμπλοκάρισμα των νημάτων μέσω up γίνεται με τυχαία επιλογή (δεν υπάρχει ρητή σειρά αναμονής) 6 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου */ int lock; /* αμοιβαίος αποκλεισμός */ gsem; void init(gsem *s, int val) { s->lock = 0; s->val = val; void down(gsem *s) { int done=0; do { while (TAS(&s->lock)) { yield(); if (s->val > 0) { s->val--; done = 1; s->lock = 0; while (!done); void up(gsem *s) { while (TAS(&s->lock)) { yield(); s->val++; s->lock = 0; 7 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου, #νημάτων σε αναμονή */ int lock; /* αμοιβαίος αποκλεισμός */ QUEUE q; /* ουρά αναμονής */ gsem; void init(gsem *s, int val) { s->lock = 0; s->val = val; Q_ΙΝΙΤ(s->q); void down(gsem *s) { while (TAS(&s->lock)) { yield(); s->val--; if (s->val >= 0) { s->lock = 0; else { s->lock = 0; Q_WAIT(s->q); γιατί αυτό είναι λάθος; void up(gsem *s) { while (TAS(&s->lock)) { yield(); s->val++; if (s->val <= 0) { Q_WAKEUP(s->q); s->lock = 0; 8 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου, #νημάτων σε αναμονή */ int lock; /* αμοιβαίος αποκλεισμός */ QUEUE q; /* ουρά αναμονής */ gsem; void init(gsem *s, int val) { s->lock = 0; s->val = val; Q_ΙΝΙΤ(s->q); void down(gsem *s) { while (TAS(&s->lock)) { yield(); s->val--; if (s->val >= 0) { s->lock = 0; else { s->lock = 0; Q_WAIT(s->q); συνθήκη ανταγωνισμού void up(gsem *s) { while (TAS(&s->lock)) { yield(); s->val++; if (s->val <= 0) { Q_WAKEUP(s->q); s->lock = 0; γιατί αυτό είναι λάθος; 9 lalis@inf.uth.gr
Συνθήκη ανταγωνισμού Η μείωση του μετρητή val και η τοποθέτηση του νήματος στην ουρά q δεν γίνονται ατομικά Λόγω εναλλαγής, ο μετρητής val μπορεί να έχει τιμή που δεν αντικατοπτρίζει την εσωτερική κατάσταση της ουράς μπορεί να είναι <0, χωρίς κάποιο νήμα να έχει μπει ακόμα στην ουρά q (χωρίς να έχει καλέσει ακόμα την λειτουργία Q_WAIT) Ένα άλλο νήμα, αφού αυξήσει και ελέγξει τον μετρητή val, μπορεί να καλέσει Q_WAKEUP χωρίς να υπάρχει νήμα εν αναμονή στην ουρά q Το νήμα που (σε δεύτερο χρόνο) θα καλέσει Q_WAIT και θα μπει στην ουρά, θα μείνει μπλοκαρισμένο εκεί 10 lalis@inf.uth.gr
Υλοποίηση γενικών σηματοφόρων με δυαδικούς σηματοφόρους Υποθέτουμε δυαδικούς σηματοφόρους με λειτουργίες BSEM_INIT, BSEM_DOWN, BSEM_UP int val: τιμή του σηματοφόρου, και #νημάτων εν αναμονή στην ουρά q BSEM q: ουρά αναμονής BSEM mtx: αμοιβαίος αποκλεισμός για val 11 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου, #νημάτων σε αναμονή */ BSEM mtx,q; /* προστασία πρόσβασης, ουρά */ gsem; void init(gsem *s, int val) { s->val = val; BSEM_INIT(s->mtx,1); BSEM_INIT(s->q,0); void down(gsem *s) { s->val--; if (s->val >= 0) { BSEM_UP(s->mtx); else { BSEM_UP(s->mtx); BSEM_DOWN(s->q); γιατί αυτό είναι λάθος; void up(gsem *s) { s->val++; if (s->val <= 0) { BSEM_UP(s->q); BSEM_UP(s->mtx); 12 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου, #νημάτων σε αναμονή */ BSEM mtx,q; /* προστασία πρόσβασης, ουρά */ gsem; void init(gsem *s, int val) { s->val = val; BSEM_INIT(s->mtx,1); BSEM_INIT(s->q,0); void down(gsem *s) { s->val--; if (s->val >= 0) { BSEM_UP(s->mtx); else { BSEM_UP(s->mtx); BSEM_DOWN(s->q); γιατί αυτό είναι λάθος; void up(gsem *s) { s->val++; if (s->val <= 0) { BSEM_UP(s->q); BSEM_UP(s->mtx); συνθήκη ανταγωνισμού 13 lalis@inf.uth.gr
Συνθήκη ανταγωνισμού Η μείωση του μετρητή val και η μείωση του δυαδικού σηματοφόρου q δεν γίνονται ατομικά O μετρητής val μπορεί να έχει τιμή που δεν αντικατοπτρίζει την εσωτερική κατάσταση του σηματοφόρου μπορεί να είναι <0, χωρίς κάποιο νήμα να έχει μπλοκάρει ακόμα στον q (χωρίς να έχει καλέσει ακόμα την λειτουργία BSEM_DOWN) Ένα άλλο νήμα, αφού ελέγξει και μειώσει τον μετρητή val, μπορεί να καλέσει BSEM_UP χωρίς να υπάρχει κάποιο νήμα μπλοκαρισμένο στον q Αν αυτό γίνει παραπάνω από 1 συνεχόμενες φορές, θα προκύψουν χαμένες κλήσεις της BSME_UP και λανθασμένο μπλοκάρισμα νημάτων 14 lalis@inf.uth.gr
typedef struct gs { int val; BSEM mtx,q,q2; /*, αμοιβαίος αποκλεισμός down */ gsem; void init(gsem *s, int val) { s->val = val; BSEM_INIT(s->mtx,1); BSEM_INIT(s->q,0); BSEM_INIT(s->q2,1); void down(gsem *s) { BSEM_DOWN(s->q2); s->val--; if (s->val >= 0) { BSEM_UP(s->mtx); else { BSEM_UP(s->mtx); BSEM_DOWN(s->q); BSEM_UP(s->q2); void up(gsem *s) { s->val++; if (s->val <= 0) { BSEM_UP(s->q); Προγραμματισμός ΙΙΙ 15 lalis@inf.uth.gr
typedef struct gs { int val; BSEM mtx,q,q2; /*, αμοιβαίος αποκλεισμός down */ gsem; void init(gsem *s, int val) { s->val = val; BSEM_INIT(s->mtx,1); BSEM_INIT(s->q,0); BSEM_INIT(s->q2,1); void down(gsem *s) { s->val--; if (s->val >= 0) { BSEM_UP(s->mtx); else { BSEM_DOWN(s->q2); BSEM_UP(s->mtx); BSEM_DOWN(s->q); BSEM_UP(s->q2); void up(gsem *s) { s->val++; if (s->val <= 0) { BSEM_UP(s->q); γιατί αυτό είναι λάθος; Προγραμματισμός ΙΙΙ 16 lalis@inf.uth.gr
Ένα (λίγο) διαφορετικό σκεπτικό Η αναμονή των νημάτων στον σηματοφόρο q σε περίπτωση που ο μετρητής val είναι >=0 μεταφέρεται έξω από το κρίσιμο τμήμα που προστατεύεται μέσω του σηματοφόρου mtx Από τη στιγμή που ένα νήμα «περάσει» από τον σηματοφόρο q, δεν υπάρχει περίπτωση μπλοκαρίσματος μέσα στο κρίσιμο τμήμα που προστατεύεται από τον mtx Αρμοδιότητα αφύπνισης έχουν περνάει πλέον και τα νήματα που μειώνουν τον σηματοφόρο 17 lalis@inf.uth.gr
typedef struct gs { int val; BSEM mtx,q; gsem; Λύση του Barz void init(qsem *s, int val) { s->val = val; BSEM_INIT(s->mtx,1); if (val > 0) {BSEM_INIT(s->q,1); else { BSEM_INIT(s->q,0); void down(qsem *s) { BSEM_DOWN(s->q); s->val--; if (s->val > 0) { BSEM_UP(s->q); BSEM_UP(s->mtx); void up(qsem *s) { s->val++; if (s->val == 1) { BSEM_UP(s->q); BSEM_UP(s->mtx); Προγραμματισμός ΙΙΙ 18 lalis@inf.uth.gr
Υλοποίηση γενικών σηματοφόρων σε επίπεδο εφαρμογής με γενικούς σηματοφόρους σε επίπεδο συστήματος Το κόστος των κλήσεων συστήματος δεν είναι αμελητέο, ιδίως αν αυτές γίνονται συχνά Αν η πιθανότητα ανταγωνισμού των νημάτων στο ΚΤ είναι μικρή, οι περισσότερες κλήσεις για αυξομείωση του σηματοφόρου που χρησιμοποιείται για την προστασία του ΚΤ γίνονται «άσκοπα» Ιδέα: Μεταφορά προκαταρκτικού ελέγχου σε επίπεδο εφαρμογής, και κλήση των (ακριβών) λειτουργιών του συστήματος μόνο αν πραγματικά απαιτείται μπλοκάρισμα και ξεμπλοκάρισμα των νημάτων 19 lalis@inf.uth.gr
typedef struct { int val; /* τιμή σηματοφόρου */ int lock; /* αμοιβαίος αποκλεισμός */ GSEM q; /* γενικός σηματοφόρος συστήματος */ gsem; void init(gsem *s, int val) { s->lock = 0; s->val = val; GSΕΜ_ΙΝΙΤ(s->q,0); void down(gsem *s) { while (TAS(&s->lock)) { yield(); s->val--; if (s->val >= 0) { s->lock = 0; else { s->lock = 0; GSEM_DOWN(s->q); γιατί εδώ δεν υπάρχει συνθήκη ανταγωνισμού void up(gsem *s) { while (TAS(&s->lock)) { yield(); s->val++; if (s->val <= 0) { GSEM_UP(s->q); s->lock = 0; 20 lalis@inf.uth.gr