ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 2ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 11 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1
Διαχείριση εργασιών ◦ fork(), exit(), exec() Επικοινωνία διεργασιών ◦ signals, sockets και client/server, shared memory Συγχρονισμός διεργασιών ◦ semaphores - 2 -
Ο µοναδικός τρόπος να δηµιουργηθεί µια νέα διεργασία στο UNIX είναι µε την κλήση του συστήµατος fork ◦ Η διεργασία που καλεί την fork ονοµάζεται γονέας ◦ Η νέα διεργασία ονοµάζεται παιδί Σύνταξη της κλήσης του συστήµατος fork ◦ pid = fork(); Οταν επιστρέψει η κλήση της fork ◦ Η δύο διεργασίες έχουν ολόιδια αντίγραφα του χώρου µνήµης (σε επίπεδο χρήστη) ◦ Εκτός από την τιµή της µεταβλητής pid ◦ Για την διεργασία-γονέας η pid είναι η ταυτότητα (ID) της διεργασίας-παιδί ◦ Για την διεργασία-παιδί η pid = 0 ◦ Πώς ξεχωρίζουμε στον κώδικα ποια διεργασία είναι παιδί και ποια πατέρας? - 3 -
- 4 - #include #include main() { int pid = 0; printf("hello world\n"); pid = fork(); if (pid == 0) { // Child process executes here printf("this is the child\n"); } else { // parent process executes here printf("this is the parent\n"); }printf("bye\n"); return 0; } Τι θα δείξει η εκτέλεση του προγράμματος?
- 5 - #include #include main() { int pid = 0; printf("hello world\n"); pid = fork(); if (pid == 0) { // Child process executes here printf("this is the child\n"); exit(0);} // only the parent process executes this printf("this is the parent\n"); exit(0);}
- 6 - #include #include main() { int i = 0; if (fork() == 0) { // Child process executes here i = 1; } printf("My value:%d\n", i); exit();} Η διεργασία-γονέας εμφανίζει 0 Η διεργασία-παιδί εμφανίζει 1
Διαχείριση εργασιών ◦ fork(), exit(), exec() Επικοινωνία διεργασιών ◦ signals, sockets και client/server, shared memory Συγχρονισμός διεργασιών ◦ semaphores - 7 -
Ένα signal είναι µια σύντοµη ανακοίνωση που στέλνεται στην διεργασία ◦ Ενηµερώνει για την εµφάνιση ενός ‘σηµαντικού’ γεγονότος Μοιάζει µε τα interrupts ◦ Το στέλνει το ΛΣ (και όχι το υλικό όπως στα interrupts) Όταν ‘έρθει’ ένα νέο signal ◦ Η ϱοή εκτέλεσης του κώδικα διακόπτεται ◦ Εκτελείται ο signal handler -- χειρίζεται το signal ◦ Επιστρέφει η ϱοή στο σηµείο που έγινε η διακοπή Υπάρχουν πολλών ειδών signals Η εντολή kill -l εμφανίζει όλη την λίστα π.χ. ο diogenis υποστηρίζει: HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS RTMIN RTMIN+1 RTMIN+2 RTMIN+3 RTMAX-3 RTMAX-2 RTMAX-1 RTMAX - 8 -
Τα πιο βασικά signals µπορούµε να τα στείλουµε µε το πληκτρολογιο ◦ CTRL-C -- ∆ηµιουργεί ένα SIGINT signal ◦ CTRL-Z -- ∆ηµιουργεί ένα SIGTSTP signal ◦ CTRL-\ -- ∆ηµιουργεί ένα SIGABRT signal Μπορούµε να στείλουµε όλα τα signals από τη γραµµή εντολών kill - PID π.χ. kill -INT 2115 στέλνει ένα SIGINT signal στη διεργασία µε PID 2115 Προγραµµατιστικά µπορούµε να στείλουµε signals µε την κληση του συστήµατος kill(...) pid_t my_pid = getpid(); kill(my_pid, SIGSTOP);
Ολα τα signals µπορούµε να τα χειριστούµε, εκτός από τα ◦ KILL signal -- Η διεργασία τερµατίζει ◦ STOP signal -- Η διεργασία σταµατάει Προκαθορισµένοι signal handlers ◦ Αν δεν ορίσουµε έναν συγκεκριµένο handler για κάποιο τύπου signal το σύστηµα χρησιµοποιεί τους προκαθορισµένους ◦ π.χ. όταν έρθει ένα TERM signal -- ο προκαθορισµένος handler κάνει exit(...) Ορίζουµε handlers µε την κλήση του συστήματος signal(...) ◦ Πρώτη παράμετρος – ο τύπος του signal ◦ ∆εύτερη παράμετρος – η συνάρτηση του handle
Οταν ο χρήστης πατήσει CTRL-C ϑα κληθεί η catch_int() και ϑα τυπώσει " Don’t do that " Προσοχη: – Αµέσως µετά, το σύστηµα ϑα αφαιρέσει την catch_int() από το signal SIGINT Αν ϑέλουµε η catch_int() να χειρίζεται όλα τα signals τύπου SIGINT πρέπει να το ϑέσουµε ξεκάθαρα ◦ Μέσα στην catch_int() ϑα πρέπει να καλέσουµε ξανά την signal(SIGINT, catch_int); void catch_int(int sig_num) { printf("Don’t do that\n"); }... signal(SIGINT, catch_int);
Αν ϑέλουµε η catch_int() να χειρίζεται όλα τα signals τύπου SIGINT πρέπει να το θέσουµε ξεκάθαρα: Το σύστηµα προσφέρει 2 έτοιµους signal handlers ◦ SIG_IGN - Αγνοεί το signal -δηλ. ο handler δεν κάνει τίποτα ◦ signal(SIGINT, SIG_IGN); ◦ SIG_DFL -- Επαναφέρει τον προκαθορισμένο handler ◦ signal(SIGTSTP, SIG_DFL); void catch_int(int sig_num) { signal(SIGINT, catch_int); // re-set for next signal printf("Don’t do that\n"); }
Υλοποιούµε έναν signal handler Θέτουµε signal( SIGCHLD, sig_chld ); /* Avoid "zombie" child-proccesses */ void sig_chld(int signo){ signal( SIGCHLD, sig_chld ); pid_t pid; int stat; while( (pid=waitpid(-1,&stat,WNOHANG)) > 0){ printf( "Child %d terminated.\n", pid ); }}