Κλείδωμα αρχείων

54
Προγραμματισμός ΙΙΙ 1 [email protected] Κλείδωμα αρχείων

Transcript of Κλείδωμα αρχείων

Page 1: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 1 [email protected]

Κλείδωμα αρχείων

Page 2: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 2 [email protected]

• Η ύπαρξη ξεχωριστών δομών πρόσβασης για κάθε διεργασία εγγυάται πως διαφορετικές διεργασίες δεν «παρεμποδίζουν» η μια την άλλη κατά την ανάγνωση και εγγραφή –με την έννοια ότι η μια διεργασία δεν αλλάζει τις θέσεις πρόσβασης στο αρχείο των άλλων.

• Αυτό όμως δεν εγγυάται συνέπεια δεδομένων!• Αν απαιτείται αμοιβαίος αποκλεισμός σε πιο

«ψηλό» επίπεδο μεταξύ των διεργασιών που ίσως χρησιμοποιούν το ίδιο αρχείο ταυτόχρονα, αυτό μπορεί να γίνει με– αρχεία λουκέτα (lock files)– κλείδωμα αρχείων (file/record locking)– σημαφόρους …

Κλείδωμα αρχείων

Page 3: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 3 [email protected]

• Αν μια διεργασία επιθυμεί να χρησιμοποιήσει το αρχείο filename κατ’ αποκλειστικότητα, τότε πρώτα δημιουργεί το αρχείο filename.lck.

• Αν το filename.lck υπάρχει, σημαίνει πως μια άλλη διεργασία χρησιμοποιεί το αρχείο filename.

• Για να αποφευχθούν συνθήκες ανταγωνισμού το αρχείο filename.lck πρέπει να δημιουργείται με flags O_CREAT | O_EXCL (οπότε αν το αρχείο υπάρχει ήδη, τότε η κλήση αποτυγχάνει).

• Για να επιτραπεί η πρόσβαση στο αρχείο filenameαπομακρύνεται το αρχείο filename.lck, με κλήση της unlink, οπότε η αμέσως επόμενη απόπειρα δημιουργίας του αρχείου filename.lck θα πετύχει.

Αρχεία λουκέτα

Page 4: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 4 [email protected]

int main(int argc, char *argv[]) {int fdl,fd; char c,p[15];

if (!fork()) {strcpy(p,“child”);} else {strcpy(p,“parent”);}do {fdl=open(“test.txt.lck”,O_CREAT|O_EXCL,S_IRWXU);if (fdl<0) {if (errno!=EEXIST) {perror(“open”); exit(1);}printf(“%s: file locked, retry in 3secs\n”,p);sleep(3);

}} while (fdl<0);close(fdl);

fd=open(“test.txt”,O_RDWR|O_CREAT,S_IRWXU);/* read/write file as desired */printf(“%s: got access, press enter to continue”,p); do {c=getchar();} while (c!=‘\n’);close(fd);

unlink(“test.txt.lck”); /* remove lock file */

}

Page 5: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 5 [email protected]

• Το αρχείο κλειδώνεται ολόκληρο, και δεν υπάρχει διαχωρισμός ανάμεσα σε κλείδωμα για ανάγνωση και κλείδωμα για εγγραφή.

• Η χρήση αρχείων λουκέτων μια σύμβαση μεταξύ των προγραμματιστών, χωρίς να δίνονται εγγυήσεις ότι αυτή θα τηρηθεί (σωστά) από όλους.

• Ένα οποιοδήποτε πρόγραμμα μπορεί να προσπελάσει ένα αρχείο, χωρίς να ελέγξει την ύπαρξη του αρχείου λουκέτου ή να μην σβήσει το αρχείο λουκέτο αφού τερματιστεί η επιθυμητή πρόσβαση στο αρχείο.

• Η διεργασία είναι αναγκασμένη να πραγματοποιεί (ημι)ενεργή αναμονή χωρίς να υπάρχει εγγυημένη αποφυγή προσπεράσματος.

Σχόλιο

Page 6: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 6 [email protected]

• Υπάρχουν ειδικές λειτουργίες για το «πραγματικό» κλείδωμα ενός αρχείου σε επίπεδο λειτουργικού.

• Υπάρχει διαφοροποίηση ανάμεσα σε κλείδωμα για ανάγνωση και κλείδωμα για γράψιμο.

• Πολλές διεργασίες μπορούν να διαβάζουν ένα αρχείο ταυτόχρονα, αλλά μόνο μια διεργασία μπορεί να γράφει σε ένα αρχείου ανά πάσα στιγμή.

• Το κλείδωμα (και ο αμοιβαίος αποκλεισμός μεταξύ αναγνωστών και εγγραφέων) μπορεί να γίνει και για ένα τμήμα του αρχείου, αντί για ολόκληρο το αρχείο.

• Οι λειτουργίες κλειδώματως και ξεκλειδώματος πραγματοποιούνται μέσω της fcntl, όπου η αίτηση προς εκτέλεση περνιέται σαν δείκτης σε δομή flock.

Κλείδωμα αρχείων

Page 7: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 7 [email protected]

void getlock(int fd, int ltype);

int main(int argc, char *argv[]) {int fd; char c,p[15]; struct flock lck;

if (!fork()) {strcpy(p,“child”);} else {strcpy(p,“parent”);}fd=open(“test.txt”,O_RDWR|O_CREAT,S_IRWXU);if (fd<0) {perror(“open”); exit(1);}

lck.l_type=F_WRLCK; /* write lock */lck.l_whence=SEEK_SET; /* start from begin of file */lck.l_start=0; /* absolute position is 0 */lck.l_len=0; /* lock entire file */while (fcntl(fd,F_SETLK,&lck)) {printf(“%s: lock conflict, retry in 3secs\n”,p);sleep(3);

}/* write file as desired */ printf(“%s: got write lock, press enter to continue”,p);do {c=getchar();} while (c!=‘\n’);lck.l_type=F_UNLCK; /* free lock */fcntl(fd,F_SETLK,&lck);close(fd);

}

Page 8: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 8 [email protected]

Συγχρονισμός με σημαφόρους

Page 9: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 9 [email protected]

Σημαφόροι

• Πολλές φορές οι διεργασίες ανταγωνίζονται μεταξύ τους για κοινούς πόρους, για τους οποίους το λειτουργικό σύστημα δεν παρέχει (ικανοποιητικό) συγχρονισμό.

• Π.χ. ταυτόχρονη πρόσβαση σε συσκευές, αρχεία κλπ.• Το λειτουργικό σύστημα προσφέρει στις διεργασίες το μηχανισμό των σημαφόρων σαν ένα εργαλείο γενικής χρήσης που μπορεί να χρησιμοποιηθεί με ευέλικτο τρόπο ώστε να επιτευχθεί ο επιθυμητός συγχρονισμός.

• Πιο συγκεκριμένα, το λειτουργικό υλοποιεί σύνολααπό γενικούς σημαφόρους με ατομικές πράξεις αυξομείωσης πολλών σημαφόρων μαζί.

Page 10: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 10 [email protected]

Δημιουργία σημαφόρων

• int semget(key_t key, int n, int flags): επιτρέφει μια δομή πρόσβασης για ένα σύνολο με nγενικούς σημαφόρους και αναγνωριστικό key.

• Αν η παράμετρος key έχει τιμή IPC_PRIVATE, τότε δημιουργείται ένα καινούργιο (ανώνυμο) σύνολο.

• Αν παράμετρος flags συμπεριλαμβάνει την τιμή IPC_CREAT τότε δημιουργείται ένα καινούργιο σύνολο με αναγνωριστικό key, αν δεν υπάρχει ήδη (στην οποία περίπτωση η κλήση αποτυγχάνει αν το flagsσυμπεριλαμβάνει και την τιμή IPC_EXCL).

• Σε περίπτωση δημιουργίας νέου συνόλου σημαφόρων, η παράμετρος flags προσδιορίζει και τα δικαιώματα πρόσβασης (παρόμοια λογική όπως για τα αρχεία).

Page 11: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 11 [email protected]

• sd=semget(IPC_PRIVATE,N,0700)Δημιουργία καινούργιου συνόλου με Ν σημαφόρους. Μόνο διεργασίες πού ανήκουν στον ίδιο χρήστη έχουν πρόσβαση σε αυτό το σύνολο.

• sd=semget(id,N,0700|IPC_CREAT|IPC_EXCL)Δημιουργία καινούργιου συνόλου Ν σημαφόρων με αναγνωριστικό id (πετυχαίνει μόνο αν αυτό δεν υπάρχει). Μόνο διεργασίες πού ανήκουν στον ίδιο χρήστη έχουν πρόσβαση σε αυτό το σύνολο.

• sd=semget (id,0,0)

Πρόσβαση στο σύνολο σημαφόρων με αναγνωριστικό id. Η κλήση αποτυγχάνει αν δεν υπάρχει σύνολο με αυτό το αναγνωριστικό ή αν η καλούσα διεργασία δεν έχει πρόσβαση σε αυτό το σύνολο σημαφόρων.

Page 12: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 12 [email protected]

Αρχικοποίηση / καταστροφή σημαφόρων

• int semctl(int sd, int n, int cmd, union semun su): εκτελεί την λειτουργία cmd στο σύνολο σημαφόρων που αντιστοιχεί στη δομή πρόσβασης sd.

• Aνάλογα με την τιμή της cmd, δίνεται προαιρετικά ως επιπλέον παράμετρος μια δομή τύπου union semun.

• Αν κληθεί με την cmd ίση με SETVAL, αναθέτει στον n-οστό σημαφόρο την τιμή su.val, ενώ αν κληθεί με την cmd ίση με SETALL, αναθέτει στους σημαφόρους τις αντίστοιχες τιμές του πίνακα su.array.

• Αν κληθεί με την cmd ίση με IPC_RMID, καταστρέφει το σύνολο σημαφόρων (οποιαδήποτε κλήση που αφορά το σύνολο σημαφόρων θα αποτύχει με την errno της διεργασίας να λαμβάνει τη τιμή EIDRM).

Page 13: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 13 [email protected]

Πράξεις down / up• semop(int sd,struct sembuf *sops,int n) : εκτελεί τις πράξεις sops στο σύνολο σημαφόρων που αντιστοιχεί στη δομή πρόσβασης sd.

• Η παράμετρος sops δείχνει στην αρχή ενός πίνακα από δομές struct sembuf κάθε στοιχείο του οποίου προσδιορίζει μια πράξη (αύξησης ή μείωσης της τιμής) για ένα διαφορετικό σημαφόρο του συνόλου, ενώ η παράμετρος n δίνει το μήκος του πίνακα.

• Οι πράξεις εκτελούνται ατομικά –η κλήση μπλοκάρει μέχρι να μπορούν να εκτελεστούν όλες οι πράξεις μαζί.

• Αν έστω και μια από τις πράξεις προσδιοριστεί ως μη ανασταλτική και δεν μπορεί να πετύχει άμεσα, τότε τερματίζεται η κλήση (χωρίς να πραγματοποιηθεί καμία πράξη) και η errno λαμβάνει τη τιμή EAGAIN.

Page 14: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 14 [email protected]

Προσδιορισμός πράξεων• struct sembuf {

unsigned short sem_num;short sem_op;short sem_flag;

};• Το πεδίο sem_num προσδιορίζει το σημαφόρο που αφορά η πράξη, το πεδίο sem_op την πράξη προς εφαρμογή στο σημαφόρο (θετική τιμή -> up, αρνητική τιμή -> down, μηδέν -> αναμονή για μηδενισμό), και το πεδίο sem_flg μπορεί να περιέχει τις τιμές IPC_NOWAIT (επιστροφή της κλήσης αν η πράξη δεν μπορεί να πετύχει άμεσα) και SEM_UNDO (αυτόματη ακύρωση αλλαγών με τον τερματισμό της διεργασίας).

Page 15: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 15 [email protected]

int main(int argc, char *argv[]) {int sd; struct sembuf sb[2];

sd = semget(IPC_PRIVATE,2,0700);

if (!fork()) {sb[0].sem_num = 0; sb[0].sem_op = -2; sb[0].sem_flg = 0;sb[1].sem_num = 1; sb[1].sem_op = -1; sb[0].sem_flg = 0;printf (“child: dec sem[0] by 2 and sem[1] by 1\n");semop(sd,sb,2);printf (“child: done\n");semctl(sd,0,IPC_RMID);exit(0);

} sb[0].sem_num = 0; sb[0].sem_op = 1; sb[0].sem_flg = 0;printf (“parent: inc sem[0] by 1\n");semop(sd,sb,1);printf (“parent: inc sem[0] by 1\n");semop(sd,sb,1);sb[0].sem_num = 1;printf (“parent: inc sem[1] by 1\n");semop(sd,sb,1);

}

Page 16: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 16 [email protected]

Επικοινωνία με ανταλλαγή μηνυμάτων

Page 17: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 17 [email protected]

Δημιουργία/καταστροφή ουράς μηνυμάτων

• int msgget(key_t key, int flags):επιστρέφει δομή πρόσβασης για την ουρά μηνυμάτων με αναγνωριστικό key και με βάση τις τιμές που περιέχει η παράμετρος flags.

• Οι «κανόνες χρήσης» για τις παραμέτρους είναι όπως και για τα σύνολα σημαφόρων (δηλαδή την semget).

• int msgctl(int msqd, int cmd, structmsgq_id *p): εκτέλεση ειδικών λειτουργιών πάνω στην ουρά που αντιστοιχεί στη δομή πρόσβασης msqd.

• Η ουρά καταστρέφεται αν η παράμετρος cmd έχει τιμή IPC_RMID (οποιαδήποτε κλήση που αφορά την ουρά μηνυμάτων θα αποτύχει με την errno της διεργασίας να λαμβάνει τη τιμή EIDRM).

Page 18: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 18 [email protected]

Δομή μηνυμάτων• struct msgbuf {

long type;… /* application data */

};

• Η παραπάνω μορφή είναι ενδεικτική.• Η δομή που χρησιμοποιούν τα προγράμματα πρέπει να οριστεί κατάλληλα ώστε να εξυπηρετεί τον σκοπό της μεταξύ τους επικοινωνίας.

• Το πρώτο πεδίο της δομής πρέπει να είναι τύπου long (όμως αυτό δεν ελέγχεται συντακτικά και είναι στην ευθύνη του προγραμματιστή) και συμβολίζει τον «τύπο» του μηνύματος.

• Ο τύπος μηνυμάτων που στέλνονται πρέπει να έχει θετική τιμή (πάλι με ευθύνη του προγραμματιστή).

Page 19: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 19 [email protected]

Αποστολή μηνύματος

• int msgsnd(int msqd, struct msgbuf *msg,size_t mlen, int flags): στέλνει το μήνυμα στο οποίο δείχνει η παράμετρος msg με μέγεθος mlenστην ουρά που αντιστοιχεί στη δομή πρόσβασης msqd.

• Η παράμετρος mlen πρέπει να προσδιορίζει το μέγεθος του μηνήματος *msg επιπλέον του πεδίου τύπου του μηνύματος (ευθύνη του προγραμματιστή).

• Η κλήση μπλοκάρει αν δεν υπάρχει διαθέσιμος χώρος στην ουρά, εκτός και αν η παράμετρος flags περιέχει την τιμή IPC_NOWAIT οπότε η κλήση αποτυγχάνει και η errno της διεργασίας λαμβάνει τη τιμή EAGAIN).

• Το μέγεθος της ουράς μπορεί να ελεγχθεί ή/και να αυξομειωθεί μέσω κατάλληλης κλήσης της msgctl.

Page 20: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 20 [email protected]

Παραλαβή μηνύματος

• ssize_t msgrcv(int msqd, struct msgbuf *msg, size_t mlen, long type, int flags): απομακρύνει το επόμενο μήνυμα που βρίσκεται στην ουρά msqd και το αποθηκεύει στη δομή *msg.

• Ανάλογα με την τιμή του type επιλέγεται: το πρώτο μήνυμα στην ουρά (0), το πρώτο μήνυμα με αυτό το τύπο (>0), και το πρώτο μήνυμα με τον μικρότερο τύπο που είναι μικρότερος του |type| (<0).

• Αν το flags περιέχει την τιμή MSG_EXCEPT και type>0, επιλέγεται το πρώτο μήνυμα με διαφορετικό τύπο.

• Αν το flags περιέχει την τιμή MSG_ΝΟERROR και το μήνυμα είναι μεγαλύτερο mlen, αντιγράφονται mlenbytes, διαφορετικά η κλήση αποτυγχάνει με E2BIG.

• Αν το flags περιέχει την τιμή IPC_NOWAIT, υπάρχει η κλασική μη ανασταλτική συμπεριφορά με ENOMSG.

Page 21: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 21 [email protected]

#define MSGSIZE 128#define REQ 1#define ACK 2

struct msgbuf {long type;char data[MSGSIZE];

};

int main (int argc, char *argv[] ) {int msqd,n; struct msgbuf msg;

msqd = msgget(IPC_PRIVATE,0700);if (!fork()) {msg.type=REQ; strcpy(msg.data,“hi to you”);msgsnd(msqd,&msg,MSGSIZE,0);n=msgrcv(msqd,&msg,MSGSIZE,ACK,0); msg.data[n]=‘\0’;printf(“child: got msg %s\n”,msg.data);msgctl(msqd,IPC_RMID,NULL);exit(0);

} n=msgrcv(msqd,&msg,MSGSIZE,REQ,0); msg.data[n]=‘\0’;printf(“parent: got msg %s\n”,msg.data);msg.type=ACK; strcpy(msg.data,“hi to you too”);msgsnd(msqd,&msg,MSGSIZE,0);

}

Page 22: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 22 [email protected]

Κοινόχρηστες περιοχές μνήμης

Page 23: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 23 [email protected]

• Κάθε διεργασία έχει δική της ιδιωτική μνήμη.• Μια διεργασία δεν μπορεί να γράψει/διαβάσει από/σε θέσεις μνήμης που ανήκουν σε άλλες διεργασίες, ούτε φυσικά και σε/από θέσεις μνήμης του λειτουργικού.

• Όλες οι μορφές επικοινωνίας και συγχρονισμού που εξετάσαμε γίνονται αποκλειστικά διαμέσω (και με έλεγχο) του λειτουργικού με κλήσεις συστήματος.

• Όμως ... στην πραγματικότητα ο Η/Υ διαθέτει μια κοινή μνήμη (για λειτουργικό και διεργασίες).

• Η ψευδαίσθηση της ιδιωτικής μνήμης υλοποιείται από το ίδιο το λειτουργικό σύστημα μέσω του μηχανισμού της εικονικής μνήμης.

Ξεχωριστή μνήμη

Page 24: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 24 [email protected]

• Κάθε διεργασία λαμβάνει μια περιοχή από εικονικέςδιευθύνσεις 0-Ν (όπου μάλιστα Ν >> size of RAM),και θεωρεί πως έχει όλη την (εικονική) μνήμη διαθέσιμη αποκλειστικά για δική της χρήση.

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

• Για καλή απόδοση, αυτό υποστηρίζεται από ειδικό υλικό της CPU (memory management unit –MMU).

• Η απεικόνιση γίνεται με βάση πίνακες αντιστοίχισης εικονικών διευθύνσεων σε πραγματικές διευθύνσεις τους οποίους διαχειρίζεται το λειτουργικό σύστημα.

Εικονικός χώρος διευθύνσεων

Page 25: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 25 [email protected]

Λειτουργικό σύστημα Πραγματική μνήμη

0Κ-4Κ

διεργασία Pi

4Κ-8Κ

8Κ-12Κ

12Κ-16Κ

16Κ-20Κ

60Κ-64Κ

Μηχανισμόςαντιστοίχισηςμνήμης

56Κ-60Κ

52Κ-56Κ

48Κ-52Κδιεργασία Pj

0Κ-4Κ

4Κ-8Κ

8Κ-12Κ

0Κ-4Κ

4Κ-8Κ

8Κ-12Κ

εικονικήμνήμη

εικονικήμνήμη

Page 26: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 26 [email protected]

Αποφυγή «συγκρούσεων»

• Δύο ή και περισσότερες διεργασίες μπορεί να χρησιμοποιούν τις ίδιες εικονικές διευθύνσεις.

• Το λειτουργικό σύστημα φροντίζει ώστε αυτές να αντιστοιχίζονται σε διαφορετικές πραγματικές διευθύνσεις της μνήμης.

• Έτσι εξασφαλίζεται ότι η διεύθυνση Χ της P1 δεν είναι τελικά η ίδια με την διεύθυνση Χ της διεργασίας P2.

• Επιπλέον, το λειτουργικό ελέγχει (μέσω της MMU)κατά πόσο οι εικονικές διευθύνσεις που προσπελαύνει μια διεργασία όντως αντιστοιχούν σε (στατικά ή δυναμικά) δεσμευμένο χώρο μνήμης.

• Αν όχι, η διεργασία τερματίζεται με αντίστοιχο κωδικό / σήμα λάθους, π.χ. SIGSEGV.

Page 27: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 27 [email protected]

Χρήση κοινής μνήμης

• Το λειτουργικό επιτρέπει σε συνεργαζόμενες διεργασίες να δεσμεύσουν και να χρησιμοποιήσουν κοινή μνήμη.

• Αυτό γίνεται (από το λειτουργικό) αντιστοιχίζοντας συγκεκριμένες εικονικές διευθύνσεις των διεργασιών στις ίδιες πραγματικές διευθύνσεις.

• Για τον προγραμματιστή είναι σα να δεσμεύει μια «ειδική» περιοχή δυναμικής μνήμης στην οποία έχουν απ’ ευθείας πρόσβαση και άλλες διεργασίες.

• Η «επικοινωνία» μέσω κοινής μνήμης γίνεται απλά γράφοντας σε και διαβάζοντας από προσυμφωνημένα σημεία της –φυσικά, η ταυτόχρονη πρόσβαση σε κοινή μνήμη δημιουργεί και τα ήδη γνωστά προβλήματα συγχρονισμού μεταξύ των διεργασιών.

Page 28: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 28 [email protected]

Λειτουργικό σύστημα Πραγματική μνήμη

0Κ-4Κ

διεργασία Pi

4Κ-8Κ

8Κ-12Κ

12Κ-16Κ

16Κ-20Κ

60Κ-64Κ

Μηχανισμόςαντιστοίχισηςμνήμης

56Κ-60Κ

52Κ-56Κ

48Κ-52Κδιεργασία Pj

0Κ-4Κ

0Κ-4Κ

8Κ-12Κ

4Κ-8Κ

4Κ-8Κ

εικονικήμνήμη

εικονικήμνήμη

8Κ-12Κ

Page 29: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 29 [email protected]

Δημιουργία / πρόσβαση σε κοινή μνήμη

• int shmget(key_t key, int s, int flags):επιστρέφει μια δομή πρόσβασης για ένα τμήμα κοινής με αναγνωριστικό key και με βάση την τιμή flags.

• Αν η παράμετρος key έχει τιμή IPC_PRIVATE, τότε δημιουργείται μια καινούργια (ανώνυμη) περιοχή μεγέθους s.

• Αν παράμετρος flags συμπεριλαμβάνει την τιμή IPC_CREAT τότε δημιουργείται μια καινούργια περιοχή μεγέθους s με αναγνωριστικό key, αν δεν υπάρχει ήδη (στην οποία περίπτωση η κλήση αποτυγχάνει αν το flags συμπεριλαμβάνει και την τιμή IPC_EXCL).

• Σε περίπτωση δημιουργίας νέας περιοχής κοινόχρηστης μνήμης, η παράμετρος flags προσδιορίζει και τα δικαιώματα πρόσβασης (λογική όπως για τα αρχεία).

Page 30: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 30 [email protected]

• shmd = shmget(IPC_PRIVATE,N,0700)

Δημιουργία ανώνυμης κοινής περιοχής μνήμης μεγέθους Ν. Μόνο διεργασίες πού ανήκουν στον ίδιο χρήστη έχουν πρόσβαση σε αυτή τη περιοχή.

• shmd = shmget (id,N,0700|IPC_CREAT)

Πρόσβαση σε κοινή περιοχή μνήμη με αναγνωριστικό id ή δημιουργία αυτής με μέγεθος N αν δεν υπάρχει (με δικαίωμα πρόσβασης μόνο σε διεργασίες πού ανήκουν στον ίδιο χρήστη).

• shmd = shmget (id,N,0)

Σύνδεση στην υπάρχουσα περιοχή κοινής μνήμης με αναγνωριστικό id. Η κλήση αποτυγχάνει αν αυτή δεν υφίσταται ή η καλούσα διεργασία δεν έχει πρόσβαση.

Page 31: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 31 [email protected]

Προσάρτηση

• int shmat(int shmd, void *p, int flags): προσαρτεί την κοινή περιοχή μνήμης που αντιστοιχεί στη δομή πρόσβασης shmd αρχίζοντας από την διεύθυνση p και με βάση την τιμή της παραμέτρου flags, και επιστρέφει την διεύθυνση από την οποία (τελικά) «αρχίζει» η κοινόχρηστη περιοχή μνήμης.

• Αν η παράμετρος p δεν είναι NULL, τότε η τιμή της πρέπει να είναι πολλαπλάσιο του PAGE_SIZE του λειτουργικού είτε η flags πρέπει να περιέχει την τιμή SHM_RND οπότε η αντιστοίχιση αρχίζει από το πλησιέστερο μικρότερο πολλαπλάσιο.

• Αν η flags περιέχει την τιμή SHM_RDONLY τότε η επιτρεπόμενη πρόσβαση είναι μόνο για ανάγνωση.

Page 32: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 32 [email protected]

Αποπροσάρτηση και αποδέσμευση

• int shmdt(void *p): αποπροσαρτά την δεύθυνση p από την κοινόχρηστη περιοχή μνήμης στην οποία είχε προσαρτηθεί προηγουμένως (αυτό γίνεται και αυτόματα όταν τερματίζεται η διεργασία).

• int shmctl(int shmd, int cmd, structshmid_ds *p): ειδικές λειτουργίες πάνω σε μια κοινόχρηστη περιοχή μνήμης –γίνεται αποδέσμευση της κοινόχρηστης περιοχής μνήμης αν η παράμετρος cmd είναι ίση με IPC_RMID.

• Η αποδέσμευση, άσχετα με τον χρόνο της κλήσης της shmctl, γίνεται όταν δεν υπάρχει πλέον διεργασία που να έχει προσαρτήσει κάποια διεύθυνση της στη συγκεκριμένη περιοχή μνήμης.

Page 33: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 33 [email protected]

P1 P2

shared memory

private memory

Page 34: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 34 [email protected]

struct mview {int readok;char msg [1024];

};

int main (int argc, char * argv []) {int shmd; struct shm_view *p;

shmd=shmget(IPC_PRIVATE,sizeof(struct mview),0700);p=(struct mview *)shmat(shmd,NULL,0700); shmctl(shmd,IPC_RMID,NULL); /* destroy … later */

p->readok=0;if (!fork()) {strcpy(p->msg,“hi from child”); p->readok=1;shmdt(p); exit(0);

} while (!p->readok) ; /* busywait for child to write */printf (“parent: %s\n”,p->msg);shmdt(p);

}

Page 35: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 35 [email protected]

Σχόλιο

• Το γεγονός ότι δύο ή περισσότερες διεργασίες έχουν δημιουργήσει μια περιοχή κοινόχρηστη μνήμης δεν συνεπάγεται ότι μπορεί να ανταλλάσσουν (χωρίς προσοχή) τιμές από μεταβλητές δείκτες.

• Αν μια διεργασία «στείλει» σε μια άλλη διεργασία μια τιμή δείκτη, αυτή θα πρέπει (α) να αντιστοιχεί σε κοινόχρηστη περιοχή μνήμης και (β) οι δύο διεργασίες θα πρέπει να έχουν προσαρτήσει την κοινόχρηστη περιοχή μνήμης αρχίζοντας από την ίδια διεύθυνση.

• Σημείωση: η διεύθυνση στην οποία επιθυμεί μια διεργασία να προσαρτήσει μια κοινόχρηστη περιοχή μνήμης δίνεται σαν παράμετρος της shmat.

Page 36: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 36 [email protected]

• void *mmap(void *p, long len, int prot, int flags, int fd, long off): αντιστοιχίζει τα περιεχόμενα του αρχείου που αντιστοιχεί στη δομή πρόσβασης fd, από την θέση offset μέχρι τη θέση offset+len, στην διεύθυνση p –επιστρέφει τη διεύθυνση από την οποία αρχίζει η αντιστοίχιση.

• Πολλές δυνατότητες «ρυθμίσεων» μέσω της flags. • int msync(void *p, long len, int flags): ενημερώνει (σύγχρονα, ασύγχρονα) το αρχείο (στον δίσκο) για τυχόν αλλαγές που έγιναν στην μνήμη.

• int munmap(void *p, long len): ακυρώνει την απεικόνιση (αυτό γίνεται και αυτόματα όταν τερματίζεται η διεργασία).

Απεικόνιση αρχείων στην μνήμη

Page 37: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 37 [email protected]

process

file memory mapped area

Page 38: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 38 [email protected]

int main(int argc, char *argv[]) {int fd,n,i; char *p; struct stat sb;

fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}if (fstat(fd,&sb)) {perror(“fstat”); exit(1);}n=sb.st_size;

p=mmap(NULL,n+1,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (p==((void *)-1)) {perror(“mmap”); exit(1);}close(fd); /* file descriptor is not needed */

p[n]=‘\0’; printf(“%s\n”,p);for (i=0; i<n; i++) {p[i]++;} /* inc each byte by 1 */printf(“%s\n”,p);

msync(p,n,MS_SYNC); /* force write on disk */munmap(p,n+1);

}

Page 39: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 39 [email protected]

• Αν μια διεργασία αντιστοιχίσει τα περιεχόμενα ενός αρχείου μέσω της mmap, και η παράμετρος flagsπεριέχει την τιμή MAP_SHARED, αυτή η περιοχή μνήμης είναι κοινή (α) με τα παιδιά που δημιουργεί η διεργασία καθώς και (β) με όποια άλλη διεργασία πραγματοποιήσει (μέσω mmap) μια αντιστοιχία της ίδιας ή επικαλυπτόμενης περιοχής του ίδιου αρχείου.

• Σε αυτή τη περίπτωση αυτές οι διεργασίες μπορεί να επικοινωνήσουν μεταξύ τους μέσω κοινής μνήμης, όπως αυτό γίνεται και για τις κοινόχρηστες περιοχές.

• Η μια διεργασία μπορεί να «βλέπει» τις αλλαγές που κάνει η άλλη, ακόμα και αν αυτές δεν προωθούνται στο δίσκο μέσω της msync.

Κοινή μνήμη μέσω memory mapped files

Page 40: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 40 [email protected]

P1

file memory mapped area

P2

Page 41: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 41 [email protected]

int main(int argc, char *argv[]) {int pid,fd,n,i; char *p; struct stat sb;

fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}if (fstat(fd,&sb)) {perror(“fstat”); exit(1);}n=sb.st_size;

if (!(pid=fork())) {p=mmap(NULL,n+1,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (p==((void *)-1)) {perror(“mmap”); exit(1);}sleep(1); /* wait for parent to change mem contents */p[n]=‘\0’; printf(“%s\n”,p);exit(0);

}

p=mmap(NULL,n,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if (p==((void *)-1)) {perror(“mmap”); exit(1);}for (i=0; i<n; i++) {p[i]++;} /* inc each byte by 1 */wait(NULL); /* wait for child to terminate */

}

Page 42: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 42 [email protected]

int main(int argc, char *argv[]) {int pid,fd,n,i; char *p; struct stat sb;

fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}if (fstat(fd,&sb)) {perror(“fstat”); exit(1);}n=sb.st_size;

p=mmap(NULL,n,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (p==((void *)-1)) {perror(“mmap”); exit(1);}

if (!(pid=fork())) {sleep(1); /* wait for parent to change mem contents */p[n]=‘\0’; printf(“%s\n”,p);exit(0);

}

for (i=0; i<n; i++) {p[i]++;} /* inc each byte by 1 */wait(NULL); /* wait for child to terminate */

}

Page 43: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 43 [email protected]

• Όλες οι αλλαγές που πραγματοποιούνται σε μνήμη στην οποία έχει απεικονιστεί μια περιοχή ενός αρχείου αντικατοπτρίζονται και στα «περιεχόμενα» του αρχείου που προσπελαύνονται μέσω των δομών πρόσβασης που έχουν δημιουργηθεί για αυτό.

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

• Αυτή η αυτόματη «μεταφορά» των αλλαγών γίνεται ακόμα και αν οι αλλαγές που γίνονται στις θέσεις μνήμης / μέσω δομής πρόσβασης δεν προωθούνται σύγχρονα στο δίσκο με msync / fsync αντίστοιχα.

Σχόλιο

Page 44: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 44 [email protected]

P1

file memory mapped area;also present in file cache

P2fd

i

Page 45: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 45 [email protected]

int main(int argc, char *argv[]) {int pid,fd,n,i; char *p,s[Ν]; struct stat sb;

if (!(pid=fork())) {fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}sleep(1); /* wait for parent to change file */for (i=0; (i<Ν-1) && (read(fd,&s[i],1)==1); i++);s[i]=‘\0’; printf(“%s\n”,s);exit(0);

}

fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}if (fstat(fd,&sb)) {perror(“fstat”); exit(1);}n=sb.st_size;p=mmap(NULL,n,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (p==((void *)-1)) {perror(“mmap”); exit(1);}for (i=0; i<n; i++) {p[i]++;} /* inc each byte by 1 */wait(NULL); /* wait for child to terminate */

}

Page 46: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 46 [email protected]

int main(int argc, char *argv[]) {int pid,fd,n,i; char *p,s[Ν]; struct stat sb;

if (!(pid=fork())) {fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}sleep(1); /* wait for parent to mmap file */for (n=0; (n<Ν) && (read(fd,&s[n],1)==1); n++);for (i=0; i<n; i++) {s[i]++;} /* inc each byte by 1 */lseek(fd,0,SEEK_SET);for (i=0; i<n; i++) {write(fd,&s[i],1);}sleep(5); /* wait for parent to read */exit(0);

}

fd=open(“test.txt”,O_RDWR);if (fd<0) {perror(“open”);exit(1);}if (fstat(fd,&sb)) {perror(“fstat”); exit(1);}n=sb.st_size;p=mmap(NULL,n+1,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (p==((void *)-1)) {perror(“mmap”); exit(1);}sleep(2); /* wait for child to write file */p[n]=‘\0’; printf(“%s\n”,p);

}

Page 47: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 47 [email protected]

Επικοινωνία με sockets

Page 48: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 48 [email protected]

• Ειδικές δομές πρόσβασης για την επικοινωνία διπλής κατεύθυνσης ανάμεσα σε διεργασίες που βρίσκονται στο ίδιο ή ακόμα και διαφορετικό Η/Υ.

• Η χρήση ενός socket εξαρτάται από το πρωτόκολλο που χρησιμοποιείται για την επικοινωνία καθώς και το είδος ροής δεδομένων που υποστηρίζεται.

• Υπάρχουν πολλοί συνδυασμοί και υποστηρίζονται πολλά πρωτόκολλα όπως π.χ. TCP/IP, UDP/IP, Unix.

• Σαν παράδειγμα θα εξετάσουμε τα Unix domain sockets που είναι για επικοινωνία ανάμεσα σε διεργασίες στο ίδιο Η/Υ.

• Για τα υπόλοιπα: Κατανεμημένα Συστήματα.

Sockets

Page 49: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 49 [email protected]

• int socket(int domain, int type, intprotocol): επιστροφή δομής πρόσβασης για ένα socket του τύπου domain-type-protocol.

• Για Unix domain sockets το domain είναι PF_UNIX.• Οι πιο κλασικές τιμές για το type είναι:

– SOCK_STREAM: αξιόπιστη μετάδοση bytes προς τις δύο κατευθύνσεις με σειρά παραλαβής FIFO.

– SOCK_DGRAM: μη αξιόπιστη μετάδοση πακέτων χωρίς εγγυημένη παράδοση (στη σωστή σειρά).

– SOCK_SEQPACKET: αξιόπιστη μετάδοση πακέτων με εγγυημένη παράδοση με σειρά FIFO.

• Το protocol προσδιορίζει ένα πρωτόκολλο για τον συνδυασμό domain-type (όταν υπάρχει επιλογή).

Δημιουργία socket

Page 50: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 50 [email protected]

• Για να μπορέσει η διεργασία P1 να επικοινωνήσει με τη διεργασία P2, πρέπει να γνωρίζει το αναγνωριστικό του socket που δημιούργησε η P2.

• int bind(int sd, struct sockaddr *sadr, size_t sadrlen): συσχέτιση του socket που αντιστοιχεί στη δομή πρόσβασης sd με το όνομα που περνιέται μέσω της παραμέτρου sadr με μέγεθος sadrlen –αποτυγχάνει αν η δομή δεν περιέχει τα αναμενόμενα δεδομένα ή το συγκεκριμένο όνομα έχει ήδη συσχετιστεί με ένα άλλο socket.

• Για Unix domain sockets χρησιμοποιείται η ίδια σύμβαση ονοματολογίας όπως για τα αρχεία ή/και τους επώνυμους σωλήνες FIFO.

Ονομασία socket

Page 51: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 51 [email protected]

• Αν μια διεργασία έχει δημιουργήσει και ονοματίσει ένα socket τύπου SOCK_STREAM μπορεί πλέον να αναμείνει για σύνδεση με μια άλλη διεργασία.

• int listen(int sd, int backlog): αναμονή στο socket που αντιστοιχεί στη δομή πρόσβασης sdγια σύνδεση με μια άλλη διεργασία –επιστρέφει μια δομή πρόσβασης μέσω της οποίας μπορεί να γραφτούν και να διαβαστούν δεδομένα.

• Η παράμετρος backlog προσδιορίζει τον αριθμό των συνδέσεων που μπορεί να βρίσκονται εν’ αναμονή(μέχρι την επόμενη κλήση της listen) χωρίς να επιστραφεί κωδικός λάθους στην αντίστοιχη διεργασία που επιχειρεί την σύνδεση.

Αναμονή για σύνδεση με sockets τύπου SOCK_STREAM

Page 52: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 52 [email protected]

• Αν μια διεργασία έχει δημιουργήσει ένα socket τύπου SOCK_STREAM μπορεί να ζητήσει την σύνδεση της με μια άλλη διεργασία.

• int connect(int sd, struct sockaddr*sadr, size_t sadrlen): σύνδεση του socketπου αντιστοιχεί στη δομή πρόσβασης sd με ένα άλλο socket το όνομα του οποίου περνιέται μέσω της παραμέτρου sadr με μέγεθος sadrlen.

• Η κλήση της connect αποτυγχάνει όταν είτε δεν υπάρχει socket με το όνομα που προσδιορίζεται είτε δεν αναμένεται σύνδεση σε αυτό το socket (δηλαδή δεν κληθεί η listen) είτε υπάρχουν πολλές συνδέσεις εν’ αναμονή στο συγκεκριμένο socket.

Σύνδεση με sockets τύπου SOCK_STREAM

Page 53: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 53 [email protected]

int main(int argc, char *argv[]) {int sl,s,n,adrlen; char msg[N]; struct sockaddr_un adr;

adr.sun_family=AF_UNIX; strcpy(adr.sun_path,“test-socket”);adrlen=sizeof(adr.sun_family)+strlen(adr.sun_path);

if (!fork()) {if ((c=socket(PF_UNIX,SOCK_STREAM,0))<0) {exit(1);}sleep(1); /* wait for parent to bind & listen */if (connect(c,(struct sockaddr *)&adr,adrlen)<0) {exit(1);}write(c,“hello parent”,12);close(c);exit(0);

}if ((sl=socket(PF_UNIX,SOCK_STREAM,0))<0) {exit(1);}unlink(“test-socket”); /* remove old socket, if any */if (bind(sl,(struct sockaddr *)&adr,adrlen)) {exit(1);}if (listen(sl,1)) {exit(1);}if (!(c=accept(sl,NULL,NULL))) {exit(1);}n=read(c,msg,N); msg[n]=‘\0’; printf(“%s\n”,msg);close(c); close(sl);unlink(“test-socket”); /* remove socket */

}

Page 54: Κλείδωμα αρχείων

Προγραμματισμός ΙΙΙ 54 [email protected]

• Οι δομές πρόσβασης για sockets SOCK_STREAMπροσπελαύνονται μέσω των «συμβατικών» πράξεων read και write όπως και για αρχεία ή σωλήνες, οπότε μπορεί να χρησιμοποιηθούν και για την ανακατεύθυνση εισόδου / εξόδου μιας διεργασίας.

• Υπάρχουν sockets SOCK_STREAM που μεταφέρουν δεδομένα πάνω από το δίκτυο (χρησιμοποιείται το domain PF_INET και αλλάζει η δομή ονόματος / διεύθυνσης που δέχονται οι bind και connect).

• Ο τύπος SOCK_DGRAM δεν απαιτεί σύνδεση ούτευποστηρίζει τις read και write –η ανταλλαγή μηνυμάτων γίνεται με ειδικές λειτουργίες, όμοιες με αυτές που χρησιμοποιούνται σε ουρές μηνυμάτων.

Σχόλιο