170 likes | 358 Views
ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ Ι. Ντίρλης Νίκος- ΕΤΥ 4 ο ΦΡΟΝΤΙΣΤΗΡΙΟ Παρασκευή 4-11- 2013 Β4. Κοινή Μνήμη και Συγχρονισμός. Κοινή Μνήμ η μεταξύ διεργασιών -> Γρηγορότερη μορφή IPC Τα δεδομένα δεν αντιγράφονται πάνω από μία φορά
E N D
ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ Ι Ντίρλης Νίκος- ΕΤΥ 4ο ΦΡΟΝΤΙΣΤΗΡΙΟ Παρασκευή 4-11-2013 Β4
Κοινή Μνήμη και Συγχρονισμός Κοινή Μνήμη μεταξύ διεργασιών -> Γρηγορότερη μορφή IPC Τα δεδομένα δεν αντιγράφονται πάνω από μία φορά Server/Client model. Όταν ο Server γράφει, ο client πρέπει να διαβάσει όταν ο server έχει τελειώσει. Χρειάζεται συγχρονισμός Χρήση semaphors
Δημιουργία και χρήση κοινής μνήμης System V IPC Shared Memory • Δημιουργία κοινής μνήμης από διεργασία • shmget() • Προσκόλληση διεργασίας στης κοινή μνήμη • shmat() • Αποκόλληση διεργασία; από κοινή μνήμη • shmdt() • Έλεγχος κοινής μνήμης (περιλαμβάνει διαγραφή) • shmctl() • man (shmget) man(shmat) man(shmdt) man(shmctl)
shmget() Με την shmget() παίρνουμε ένα shared memory identifier #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> intshmget(key_t key, int size, int flag); Η shmget() επιστρέφει ένα shared memory ID if OK και-1 αν error Το «key» είναι συνήθως η σταθερά “IPC_PRIVATE”, που ζητά από τονkernel έναν non-negative integer identifier που η τιμή του αυξάνει μέχρι μια μέγιστη τιμή πριν μηδενιστεί. Το «size» είναι το μέγεθοςτων shared memory segment σε bytes Το «flag» μπορεί να είναι “SHM_R”, “SHM_W” ή“SHM_R | SHM_W”
shmat() Όταν ένα shared memory segment έχει δημιουργηθεί, το process το κάνει attach στο address space του καλώντας τηνshmat(): void *shmat(intshmid, void* addr, int flag); Η shmat() επιστρέφει ένανpointer σε έναshared memory segment αν OK και-1 αν error Η συνήθης προτεινόμενη προγραμματιστική τεχνική είναι να θέτονται το addrκαιflag στο μηδέν, πχ: char* buf = (char*)shmat(shmid,0,0); ΣτοUNIX οι εντολές “ipcs” και“ipcrm” χρησιμοποιούνται για να κάνουν list και removeshared memory segments στο current machine. Τοdefault action για ένα shared memory segment είναι να παραμένει στο σύστημα ακόμα και μετά το θάνατο μιας διεργασίας. Αν θέλουμε να το αποφύγουμε αυτό (και συνήθως το θέλουμε!) χρησιμοποιούμε την shmctl().
shmctl() Η shmctl() κάνει αρκετά πραγματάκια: intshmctl (intshmid, intcmd, structshmid_ds *buf); Το «cmd»μπορεί να είναι ένα από τα IPC_STAT, IPC_SET, ή IPC_RMID. Μπορούμε εύκολα να δούμε τι κάνει το καθένα από αυτά. Με ένα googlάρισμα βρίσκουμε: – IPC_STAT fills the buf data structure – IPC_SET can change the uid, gid, and mode of the shmid – IPC_RMID sets up the shared memory segment to be removedfrom the system once the last process using the segmentterminates or detaches from it – a process detaches from ashared memory segment using shmdt(void* addr), which issimilar to free() Η shmctl() επιστρέφει 0 αν OK και-1 ανerror
Παραδείγματα shm_id = shmget(SHM_KEY, SHM_SIZE, 0600); if (shm_id < 0) { printf("Could not create shared memory!\n"); exit(1); } data = shmat(shm_id, NULL, 0); //δείκτης προς την αρχή της sm if (data == (char *)-1) { printf(“Could not attach to shared memory!\n”); exit(1); }
και άλλο ένα… char* ShareMalloc(int size) { intshmId; char* returnPtr; if ((shmId=shmget(IPC_PRIVATE, size, (SHM_R | SHM_W)) < 0) Abort(“Failure on shmget\n”); if (returnPtr=(char*)shmat(shmId,0,0)) == (void*) -1) Abort(“Failure on shmat\n”); shmctl(shmId, IPC_RMID, (structshmid_ds *) NULL); return (returnPtr); }
Race Conditions (1/2 ) Τι θα συμβεί αν τρέξουμε το παρακάτω κομμάτι κώδικα; intmain(void) { pid_tpid; if ((pid = fork()) < 0) Abort(“Fork Error”); else if (pid == 0) charatatime(“output from child\n”); else charatatime(“output from parent\n”); exit(0); } Έχουμε το φαινόμενο του «compile timenon-determinism»! Διαδοχικά run του παραπάνω κώδικα δίνουν και διαφορετικά αποτελέσματα!
Race Conditions(2/2 ) Αν θέλουμε να επέμβουμε στη σειρά εκτέλεσης (γιατί για παράδειγμα η διεργασία-γονιός χρειάζεται κάποια δεδομένα από τη διεργασία παιδί) μπορούμε να κάνουμε wait. Σε αυτή την περίπτωση χρησιμοποιούμε signals. Διαφορετικά κάνουμε busy waiting κοιτώντας ένα flag, κάτι όμως που φυσικά θέλουμε να το αποφεύγουμε! Όμως για θυμηθείτε το παράδειγμα των Φιλοσόφων! Χρειαζόμαστε και έναν «διαιτητή» (ή έναν «σερβιτόρο» στην περίπτωση των φιλοσόφων). Αυτός είναι ο ρόλος του semaphor
Τhe «Critical Section Problem» Code that modifies shared data usuallyhas the following parts: – Entry section: The code that requestspermission to modify the shared data. – Critical Section: The code that modifies theshared variable. – Exit Section: The code that releases access tothe shared data. – Remainder: The remaining code The critical section problem refers to the problem of executingcritical sections in a fair, symmetric manner. Solutions to the criticalsection problem must satisfy each of the following: – Mutual Exclusion: At most one process is in its critical section atany time – Progress: If no process is executing its critical section, a processthat wishes to enter can get in – Bounded Waiting: No process is postponed indefinitely An atomic operation is an operation that, once started, completes ina logical indivisible (=αδιαίρετος) way. Most solutions to the critical sectionproblem rely on the existence of certain atomic operations
Semaphors (1/2) Ένας semaphore είναι μια ακέραια μεταβλητή με δύο atomic operations: wait and signal. Άλλες ονομασίες που θα βρείτε για αυτές είναι down, P και lock για το wait και up, V, unlock και post για το signal. Μία process που κάνει wait σε έναν semaphoreS δεν μπορεί να συνεχίσει μέχρι η τιμή του S είναι θετική. Μετά μειώνει κατά ένα την τιμή του isS. Όταν γίνει «signal» η τιμή του S μεγαλώνει κατά ένα. Τι δεν θέλουμε να κάνει ένας semaphor; void wait(int *s) { while (*s <= 0); /* END WHILE*/ (*s)--; } void signal(int *s) { (*s)++; }
Semaphors (1/2) Το πρόβλημα με τον κώδικα αυτό είναι αρχικά το busy waiting και επίσης το γεγονός ότι οι πράξεις «- -» και «+ +» δεν είναι atomic! Αυτό που θέλουμε να κάνουμε είναι το ακόλουθο: wait(&s); /* critical section */ signal(&s); /* remainder section */ και αυτό γίνεται με τη χρήση των system callssemget() και semop()
Semaphors (2/2) • Δημιουργία - Άνοιγμα σημαφόρου • sem_open() • Κλείδωμα σημαφόρου • sem_wait() • Απελευθέρωση σημαφόρου • sem_post() • Κλείσιμο σημαφόρου • sem_close() • Διαγραφή σημαφόρου • sem_unlink()
semget() #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> intsemget(key_t key, intnsems, intsemflg); Δημιουργεί έναν semaphore και αρχικοποιεί κάθε στοιχείο του στο μηδέν. int semID = semget(IPC_PRIVATE, 1, S_IRUSR |S_IWUSR); Όπως στο shared memory, έτσι και εδώ το ipcsκαιipcrmμπορούν να κάνουν list και remove semaphores
semop() intsemop(intsemid, structsembuf *sops, unsigned nsops); Increment, decrement, or test semaphore elements for a zero value Από το <sys/sem.h>: sops->sem_num, sops->sem_op, sops->sem_flg Αν το sem_opείναι θετικόή semop() προσθέτει ένα στην τιμή και ξυπάνει τη διαδικασία που περιμένει. Αν το sem_opείναι αρνητικό , η semop() προσθέτει ένα στην τιμή και αν <0 ή semop() sets to 0 and blocks until it increases Αν το sem_opείναι μηδέν και το semaphore element value όχι μηδέν, η semop() blocks the calling process until the value becomes zero Αν η semop() is interrupted by a signal, it returns –1 with errno = EINTR
Παράδειγμα structsembufsemWait[1] = {0,-1,0}; semSignal[1] = {0,1,0}; intsemID; semop(semID,semSignal,1); /* init to 1 */ while((semop(semID,semWait,1) == -1) && (errno == EINTR)); { /* Critical Section */ } while((semop(semID,semSignal,1) == -1) && (errno == EINTR));