Λειτουργικά Συστήματα Φροντιστηριακή ενότητα #2 : Διαχείριση μνήμης και διεργασίες Τμήμα: Πληροφορικής
Χρηματοδότηση Το παρόν εκπαιδευτικό υλικό έχει αναπτυχθεί στα πλαίσια του εκπαιδευτικού έργου του διδάσκοντα. Το έργο «Ανοικτά Ακαδημαϊκά Μαθήματα στο Οικονομικό Πανεπιστήμιο Αθηνών» έχει χρηματοδοτήσει μόνο τη αναδιαμόρφωση του εκπαιδευτικού υλικού. Το έργο υλοποιείται στο πλαίσιο του Επιχειρησιακού Προγράμματος «Εκπαίδευση και Δια Βίου Μάθηση» και συγχρηματοδοτείται από την Ευρωπαϊκή Ένωση (Ευρωπαϊκό Κοινωνικό Ταμείο) και από εθνικούς πόρους. 2
Άδειες Χρήσης Το παρόν εκπαιδευτικό υλικό υπόκειται σε άδειες χρήσης Creative Commons. 3
Σκοποί ενότητας Διαχείριση της μνήμης με έμφαση στα πιθανά λογικά λάθη (Memory leaks, κ.ο.κ.) Χρήση δεικτών σε πίνακες, πέρασμα παραμέτρων, κ.ο.κ. Διεργασίες, επικοινωνία μεταξύ διεργασιών, και συναφής χρήσιμες κλήσεις συστήματος (dup, exec) 4
Περιεχόμενα ενότητας Δείκτες και θέματα διαχείρισης μνήμης Διεργασίες Διαδιεργασιακή επικοινωνία 5
Δείκτες και θέματα διαχείρισης μνήμης Μάθημα: Λειτουργικά Συστήματα, Φροντιστηριακή Ενότητα # 2: Προγραμματισμός με τη γλώσσα C για γνώστες της γλώσσας Java Τμήμα: Πληροφορικής
Μεταβλητές και δείκτες http://www.thegeekstuff.com/2012/01/advanced-cpointers/ Κάθε μεταβλητή έχει Τη τιμή της και τη διευθυνση της μνήμης που της αντιστοιχεί Δείκτης (pointer): είναι μια μεταβλητή που έχει ως τιμή του τη διευθυνση μιας μεταβλητής Ο τύπος του δείκτη ταιριάζει με εκείνον της μεταβλητής στην οποία δείχνει. int a = 10; int * a_ptr = &a; 7
Μεταβλητές και δείκτες variable name *a_ptr memory address 0X3020 a 10 0X3016 &a 0X3012 0X3008 a_ptr 0X3016 0X3004 0X3000 8
Παραδείγματα δεικτών unsigned int *ui_ptr; int **ptr2ptr; // δείκτης σε δείκτη struct somestruct *sstrct_ptr; int i = 1; int *ptr = &i; int j = *ptr; // aνάκτηση και ανάθεση της τιμής του i *ptr = 43; // το i ισούται τώρα με 43 9
Πέρασμα παραμέτρων κατά αναφορά 1. #include <stdio.h> 2. void swap(int*x, int *y){ 3. int temp = *x; 4. *x = *y; 5. *y = temp; 6. } 7. int main(void) { 8. int x = 9; 9. int y = 5; 10. swap(&x, &y); 11. printf("x = %d, y = %d\n", x, y); 12. return 0; 13. } 10
Δείκτες και structs 1. #include <stdio.h> 2. 3. struct data { 4. int counter; 5. double value; 6. }; 1. #include <stdio.h> 2. 3. struct data { 4. int counter; 5. double value; 6. }; 7. void add(struct data d, double value) { 8. d.counter++; 9. d.value += value; 10. } 7. void add(struct data * d, double * value) { 8. d >counter++; 9. d >value += *value; 10. } 11
&arr[0] arr Πίνακας σταθερός δείκτη int * p; arr = p; Λάθος! Ο arr είναι const δείκτης Δε μπορεί να δείξει σε άλλη διεύθυνση μνήμης void foo(int arr[], int len) void foo(int * arr, int len) 12
C Function Pointers (1/2) Ό,τιδήποτε <return type> (*<pointer>) (arguments) Πχ: int (*fptr)(int, void*); void test() { printf("hello world\n"); } void f(void (*a)()) { a(); // καλεί ό,τι περαστεί ως a } int main() { f(&test); return 0; } 13
C Function Pointers (2/2) 1. int max(int a, int b) { 2. return (a > b? a : b); 3. } 4. 5. int foo(int a, int b, int(*func) ( int p1, int p2)) { 6. return func(a, b); 8. int main(void) { 9. int result = foo(11, 22, &max); 10. printf("%d\n", result); 11. return 0; 12. } 7. } 14
Memory Leaks (1/2) 1. int main() { 2. int *i = (int*) malloc(3*sizeof(int)); 3. i = 0; /*Wxxxxx, EXASA TON pointer STH DYNAMIKA DESMEYMENH MNHMH*/ 4. free(???); /* DE KSERW PLEON PWS..*/ 5. } 15
Memory Leaks (2/2) 1. #include <stdio.h> 2. #include <stdlib.h> 3. int main() { 4. int *array = (int *) malloc(10*sizeof(int)); 5. if (array == NULL) { 6. printf("out of memory!\n"); 7. return -1; 8. } 9. return 0; 10. } 16
Διαχείρηση δυναμικής μνήμης Function malloc realloc calloc free Description allocates the specified number of bytes increases or decreases the size of the specified block of memory. Reallocates it if needed allocates the specified number of bytes and initializes them to zero releases the specified block of memory back to the system http://en.wikipedia.org/wiki/c_dynamic_memory_allocation 17
Διεργασίες Μάθημα: Λειτουργικά Συστήματα, Φροντιστηριακή Ενότητα # 2: Προγραμματισμός με τη γλώσσα C για γνώστες της γλώσσας Java Τμήμα: Πληροφορικής
Εισαγωγικές έννοιες Διεργασία: πρόγραμμα εν εκτελέσει Δεν επηρεάζει άμεσα η μία διεργασία την άλλη (isolation) Shared memory? Καλύτερα Νήματα αν πρέπει να μοιραστούν μνήμη 19
Δημιουργία Διεργασίας (1/4) Μια διεργασία δημιουργείται από μια άλλη Πχ από το shell ανοίγω μια διεργασία gedit «γενικό πλάισιο» (context): PID process ID Μνήμη: Πρόγραμμα εντολών + δεδομένα Μετρητή προγράμματος program counter Χειριστές σημάτων signal handlers 20
Δημιουργία Διεργασίας (2/4) 1. #include <sys/types.h> 2. #include <unistd.h> 3. pid_t fork(void); /* Δημιουργεί διεργασία παιδί ακριβές αντίγραφο της διεργασίας από το σημείο εκτέλεσης και μετά */ fork 21
Δημιουργία Διεργασίας (3/4) 1. #include <sys/types.h> 2. #include <unistd.h> 3. pid_t getpid(void); // μαθαίνεις την PID σου fork 4. pid_t getppid(void); // PID του πατέρα σου 22
Δημιουργία Διεργασίας (4/4) ~Πατέρας~ 1. getpid() => 1985 2. getppid() => 1453 ~Παιδί~ 1. getpid() => 1986 2. getppid() => 1985 3. x => 0 fork ~Πατέρας~ 1. getpid() => 1985 2. getppid() => 1453 3. x => 1986 Σημείωση: Η fork() επιστρέφει -1 αν αποτύχει
Τι κάνει το ακόλουθο; 1. pid_t pid; 2. pid = fork(); 3. if (pid<0) { 4. perror( LA8OS STHN fork()"); 5. exit(1); 6. } 7. if (!pid) // ισοδύνο του (pid==0) 8. printf("i am the child process of %d\n, getppid()); 9. else /*if(pid) if (pid!=0) 10. printf("i am the parent process of %d\n, pid); 24
Zombies! Zombie status: μια διεργασία που τελείωσε Δε διαγράφεται από το σύστημα Παραμένει έως ο πατέρας να λάβει το termination status Αν ο πατέρας πεθάνει νωρίτερα; Υιοθετούνται από το process με PID 1 (init) Παραμένουν τα ζόμπι και τρώνε τους πόρους του συστήματος Αποφυγή ζόμπι: ο πατέρας περιμένει ρητά με τη wait() Εναλλακτικά φτιάχνει συνάρτηση signal handler για το SIGCHLD 25
Χρήση της wait 1. #include <sys/types.h> 2. #include <sys/wait.h> 3. 4. pid_t wait(int *status);/* PERIMENEI * OPOIODHPOTE PAIDI. PAIRNEI POINTER * GIA NA EPISTREPSEI STATUS */ 5. // pid_t waitpid(pid_t pid /*-1 opoios- // dhpote*/, int *status, int options); // option=wnohang makes the call return // immediately if no children completed; // otherwise, use option=0 26
Εναλλακτικά: SIGCHLD signal handler 1. void sig_chld(int sig) { 2. pid_t pid; 3. int stat; 4. while ((pid=waitpid(-1,&stat,wnohang))>0){ 5. printf("child %d terminated with status %d\n",pid,stat); 6. } 7. signal(sigchld,sig_chld);/* DHLWNOUME STO PYRHNA POIA singal hendler SYNARTHSH 8A KLH8EI APO TO PROGRAMMA MAS..*/ 8. }? http://www.thegeekstuff.com/2012/03/catchsignals-sample-c-code/ 27
fork() + exec() 1. pid_t pid=fork(); 2. if (pid==0) { // AN EISAI TO PAIDI 3. exec("hello"); // PATH PROS TO hello? 4. perror("error exec!\n"); 5. exit(1); 6. } 28
execl ή execv execlp ή execvp // execl() // execlp() @ $PATH 1. execl("/bin/ls", "ls", "-l", "/home/csuser", NULL); // execv() // execvp() @ $PATH 1. char *params[4]= {"ls", "-l", "/home/csuser", NULL}; 2. execv("/bin/ls", params); 29
Διαδιεργασιακή επικοινωνία Μάθημα: Λειτουργικά Συστήματα, Φροντιστηριακή Ενότητα # 2: Προγραμματισμός με τη γλώσσα C για γνώστες της γλώσσας Java Τμήμα: Πληροφορικής
Εισαγωγικές έννοιες Πολλές διεργασίες που επικοινωνούν μεταξύ τους πάνω από το δίκτυο: Κατανεμημένος προγραμματισμός Πολλές διεργασίες εντός του ίδιου μηχανήματος => Inter-Process Communication (IPC) Message/Signal passing, Shared memory, Pipes 31
Pipes (1/2) 1. #include <unistd.h> 2. int pipe(int fd[2]); fd[0] is the file descriptor for reading fd[1] is the file descriptor for writing 32
Pipes (2/2) // #include <unistd.h> 1. pid_t pid; 2. int fd[2]; 3. char buf[64]; 4. if (pipe(fd)<0) 5. exit(1); 6. if ((pid=fork())==0) { //Child process 7. close(fd[0]); //DIOTI MONO GRAFEI TO PAIDI 8. write(fd[1],"hello, world!",14); 9. } else { //Parent process 10. close(fd[1]); 11. if (read(fd[0],buf,64)>0) 12. printf("recv: %s\n",buf); 13. waitpid(pid,null,0); 14. } 33
FIFO σωληνώσεις Γνωστές και ως «Named pipes» Ειδικό αρχείο σε ρόλο pipe 2 διεργασίες συμφωνούν στο όνομα του αρχείου mkfifo() open() read() write() close() 34
dup, dup2, dup3 Κλήσεις συστήματος input/output redirection int dup (int oldfd); int dup2 (int oldfd, int newfd); int dup3 (int oldfd, int newfd, int flags); Δημιουργούν αντίγραφο ενός file descriptor Η dup κάνει χρήση του πρώτου διαθεσιμου file descriptor Με τη dup2, επιλέγει ο χρήστης το νέο file descriptor (αρκεί να είναι διαθέσιμο) + αυτόματα κλείνει το παλιό file descriptor Για τη dup3 και λοιπές λεπτομέρειες ψάξτε το man dup ή http://linux.die.net/man/2/dup2 35
Παράδειγμα dup2 1. //PARE file descriptor TOY ARXEIOU GIA REDIRECTION 2. int file = open("myfile.txt", O_APPEND O_WRONLY); 3. if(file < 0) return 1; //APOTYXIA 4. //Now we redirect standard output to the file using dup2 5. if(dup2(file,1) < 0) return 1; 6. //Now standard out has been redirected, we can write to 7. // the file 8. printf("this will print in myfile.txt\n); 9. return 0; 36
Παράδειγμα dup (1/4) // Παράδειγμα από: http://en.wikipedia.org/wiki/dup_%28system_ call%29 1. #include <stdio.h> 2. #include <stdlib.h> 3. #include <unistd.h> 4. void die(const char *msg) { perror(msg); exit(exit_failure); } 5. void die(const char*); /*function prototype */ 37
Παράδειγμα dup (2/4) 6. int main(int argc, char **argv) { 7. int pdes[2]; // file descriptors 8. pid_t pid; 9. 10. if(-1 == (pipe(pdes))) { 11. die("pipe()"); 12. } 13. if(-1 == (pid = fork()) ){ die("fork()"); 14. } 38
Παράδειγμα dup (3/4) 15. if(0 == pid) {/* PAIDI process */ 16. close(1); /* close stdout */ 17. if(-1 == dup(pdes[1])) { 18. die("dup()"); 19. } 20. /* now stdout and pdes[1] are equivalent (dup returns lowest free descriptor) */ 21. if(-1 == execlp("program1", program1", "arg1", NULL)){ 22. die("execlp()"); 23. } 24. _exit(exit_success); 25. }// if((pid_t)0 == child) 39
Παράδειγμα dup (4/4) 26. else { /* PATERAS process */ 27. close(0); /* close stdin */ 28. if(-1 == dup(pdes[0])){ 29. die("dup()"); 30. }/* now stdin and pdes[0] are equivalent (dup returns lowest free descriptor)*/ 31. if(-1 == execlp("program2", "program2", "arg1", NULL)) { die("execlp()"); 32. } 33. exit(exit_success); 34. }//else 35. return 0; 36. } 40
Συγχρονισμός Συναγωνισμός (Race Condition) Οι διεργασιες εκτελούνται παράλληλα Μας ενδιαφέρει η σειρά εκτέλεσης; Πατέρα-παιδιού; Πατέρα-παιδιών; Παιδιού-παιδιου; Λύση: Σημαφόροι (semaphores) Συγχρονισμός πρόσβασης σε μοιραζόμενο πόρο Συγχρονισμός σειράς εκτέλεσης Κάποιος/κάποιοι πρέπει να περιμένουν.. 41
Σημαφόροι (1/7) Μη αρνητικοί ακέραιοι Κλήσεις με ατομικές εκτελέσεις: DOWN UP Αν ο σημαφόρος == 0, «παγώνει» η εκτέλεση Αν ο σημαφόρος >=1, τότε μειώνει κατά ένα τη τιμή του σημαφόρου και συνεχίζει η εκτέλεση Αυξάνει κατά ένα τη τιμή του σημαφόρου και συνεχίζει η εκτέλεση Αν κάποιος άλλος περιμένει στον σημαφόρο, «ξεπαγώνει» η εκτέλεση του 42
Σημαφόροι (2/7) Χρήση για αμοιβαίο αποκλεισμό. Mutual exclusion mutexing των «πόρων» Shared memory (μεταβλητή, δομή,..), critical code section Αρχικά semph = 1 DOWN(semph): παίρνεις αποκλειστικά τον «πόρο» UP(semph): σηματοδοτεί την απελευθέρωση του πόρου Παίζει ρόλο η σειρά κλήσεων; Παίζει ρόλο το πλήθος των κλήσεων; 43
Σημαφόροι (3/7) Χρήση για σηματοδότηση γεγονότος Αρχικά semph = 0 DOWN(semph): περιμένεις γεγονός UP(semph): σηματοδοτεί ένα γεγονός Παίζει ρόλο η σειρά κλήσεων; Παίζει ρόλο το πλήθος των κλήσεων; 44
Σημαφόροι (4/7) 1. #include <sys/types.h> 2. #include <sys/ipc.h> 3. #include <sys/sem.h> 45
Σημαφόροι (5/7) // Δημιουργία int semget(key_t key, int nsems, int semflg) key: IPC_PRIVATE nsems: # σημ/ων που δημιουργείς semflg: access rights Επιστρέφει το ID του συνόλου σημ/ων // εκτέλεση operation στο semaphore 1. int semop (int semid, struct sembuf *sops, unsigned nsops) semid: το ID του semaphore set sops: πίνακας από εντολές (sem. operations) nsops: το πλήθος των sops
Σημαφόροι (6/7) 1. struct sembuf { 2. short sem_num; // semid: 0 για το // πρώτο σημαφόρο, κ.ο.κ. 3. short sem_op; // semaphore operation 4. short sem_flg; // operation flags 5. }; 47
Σημαφόροι (7/7) 1. int semctl(int semid, int semnum, int cmd,...); semid: το array identifier του σημαφόρου semnum: το νούμερο που αντιστοιχεί στον σημαφόρο cmd: command IPC RMID: καταστροφή του σηματοφόρου GETVAL: return the value of the semaphore 48
Παράδειγμα Σημαφόρων 1. struct sembuf up = {0, 1, 0}; // ορίζουμε // διαδικασία UP 2. struct sembuf down = {0, -1, 0}; //αντίστοιχα //για DOWN 3. int my_sem, v1, v2, v3, v4; // τιμή σημαφόρου, και άλλες τιμές ακεραίων 4. my_sem = semget( IPC_PRIVATE, 1, 0600); // δημιουργεία 5. v1 = semctl(my_sem, 0, GETVAL); 6. semop(my_sem, &up, 1); //UP() 7. v2 = semctl(my_sem, 0, GETVAL); 8. semop(my_sem, &down, 1); /* DOWN() */ 9. v3 = semctl(my_sem, 0, GETVAL); 10. semctl(my_sem, 0, IPC_RMID); /* destroy */ 11. v4 = semctl(my_sem, 0, GETVAL);// RUNTIME // LA8OS Ή SKOUPIIDA 12. printf("semaphore values: %d %d %d %d\n", v1, v2, v3, v4); 49
Τέλος Φροντιστηριακής Ενότητας # 2 Μάθημα: Λειτουργικά Συστήματα, Φροντιστηριακή Ενότητα # 2: Προγραμματισμός με τη γλώσσα C για γνώστες της γλώσσας Java, Τμήμα: Πληροφορικής