Διάλεξη 14: Διεργασίες: Έλεγχος & Σήματα (Processes: Control & Signals)

Σχετικά έγγραφα
Εργαστήριο 5 fork(), exec(), signals

Ντίρλης Νικόλαος- ΕΤΥ 2 ο Φροντιστήριο Παρασκευή, 18/10/2013 Β4. Λειτουργικά Συστήματα- Φροντιστήριο 2

Πανεπιστήμιο Θεσσαλίας Τμήμα Πληροφορικής

Προγραμματισμός συστημάτων UNIX/POSIX. Διεργασίες (processes)

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ. Διεργασίες και Νήματα Εργαστηριακές Ασκήσεις

Εργαστήριο 7 fork(), exec(), signals

Θέτοντας και επιστρέφοντας την τιµή της προτεραιότητας διεργασίας

Δημιουργία & Τερματισμός Διεργασιών. Προγραμματισμός II 1

Εργαστήριο ΔΙΕΡΓΑΣΙΕΣ - ΔΙΑΧΕΙΡΙΣΗ

Διαχείριση Διεργασιών και Διαδιεργασιακή Επικοινωνία

Προγραμματισμός συστημάτων UNIX/POSIX. Σήματα (signals)

Δημιουργία & Τερματισμός Διεργασιών. Προγραμματισμός II 1

Δίκτυα Επικοινωνιών ΙΙ: Network Programming UDP Sockets, Signals

Διαχείριση Διεργασιών και Διαδιεργασιακή Επικοινωνία

Χρονοδρομολογητής Κυκλικής Επαναφοράς

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ II. Υφαντόπουλος Νικόλαος Υποψήφιος Διδάκτορας Contact:

UNIX System Programming

Λειτουργικά Συστήματα

Εκφωνήσεις ασκήσεων εργαστηρίου 1

Προγραμματισμός συστημάτων UNIX/POSIX. Διαδιεργασιακή επικοινωνία: αγωγοί (IPC inter-process communication: pipes)

$./jms console -w <jms in> -r <jms out> -o <operations file> namedpipe. (standard input).

Λειτουργικά Συστήματα (ΗΥ-345) Χειμερινό Εξάμηνο

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκή περίοδος

ιεργασίες και νήµατα Προγραµµατισµός ΙΙΙ 1 lalis@inf.uth.gr

Προγραμματισμός συστημάτων UNIX/POSIX

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ ΔΙΕΡΓΑΣΙΕΣ

Λειτουργικά Συστήματα

Παράλληλη Επεξεργασία

Α. unsigned int Β. double. Γ. int. unsigned char x = 1; x = x + x ; x = x * x ; x = x ^ x ; printf("%u\n", x); Β. unsigned char

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκή περίοδος

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

#include <stdlib.h> Α. [-128,127] Β. [-127,128] Γ. [-128,128]

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκή περίοδος

Α Β Γ static; printf("%c\n", putchar( A +1)+2); B DB BD. int i = 0; while (++i); printf("*");

Εισαγωγή εκτελέσιμου κώδικα σε διεργασίες

Εισαγωγή στον Προγραμματισμό

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ II. Υφαντόπουλος Νικόλαος Υποψήφιος Διδάκτορας Contact:

Κεφάλαιο , 3.2: Συναρτήσεις II. ( ιάλεξη 12) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Προγραµµατισµός 2 The shell

Μεθόδων Επίλυσης Προβλημάτων

Εργαστήριο Λειτουργικών Συστημάτων 8ο εξάμηνο, Ακαδημαϊκή περίοδος

Κεφάλαιο , 3.2: Συναρτήσεις II. (Διάλεξη 12)

Λειτουργικά Συστήματα (ΗΥ321)

Λειτουργικά Συστήματα (Λ/Σ)

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

Διεργασίες και Νήματα (1/2)

Προγραμματισμός Ι. Προχωρημένα Θέματα. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( )

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ. Διεργασίες και Νήματα

Λειτουργικά. Συστήματα Ι. Φ ρ ο ν τ ι σ τ ή ρ ι ο. Αριστείδης Ηλίας. Εργαστήριο Ηλεκτρονικών Υπολογιστών

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής

Η γλώσσα προγραμματισμού C Συνδεδεμένες Λίστες

Εικονική Μνήμη (Virtual Memory) Προγραμματισμός II 1

Υπολογισμός - Εντολές Επανάληψης

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Κεφάλαιο 3.1, : Συναρτήσεις I. (Διάλεξη 11)

Διάλεξη 5η: Εντολές Επανάληψης

Περιγραφή και Έλεγχος ιεργασιών

Η γλώσσα προγραμματισμού C

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής

Διάλεξη 18η: Διαχείρηση Αρχείων

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ II. Υφαντόπουλος Νικόλαος Υποψήφιος Διδάκτορας Contact:

Επεξεργασία Αρχείων Κειµένου

Σε γενικές γραμμές, είναι καλή πρακτική να γράϕουμε προγράμματα C που αποτελούνται από πολλές και μικρές συναρτήσεις, παρά από λίγες και μεγάλες.

Εισαγωγή στον Προγραμματισμό

Εικονική Μνήμη (Virtual Memory) Προγραμματισμός II 1

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος

(Κεφάλαιο 2.7 και 12) Αρχεία στην C. (Διάλεξη 15)

Διάλεξη 13: Δομές Δεδομένων ΙΙ (Ταξινομημένες Λίστες)

Συναρτήσεις. Εισαγωγή

ΤΕΜ-101 Εισαγωγή στους Η/Υ Εξεταστική Ιανουαρίου 2011 Θέματα Β

ΑΡΧΕΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ

Εισαγωγή στους Η/Υ. Γιώργος Δημητρίου. Μάθημα 3-4: Προγραμματισμός MIPS. Πανεπιστήμιο Θεσσαλίας - Τμήμα Πληροφορικής

Μερικές άλλες χρήσιμες εντολές

Μεθόδων Επίλυσης Προβλημάτων

Ανάπτυξη Μεγάλων Εφαρµογών στη Γλώσσα C (2)

1. Εισαγωγή. Λειτουργικά Συστήματα Η/Υ. Διεργασίες. Ορισμός ΚΕΦΑΛΑΙΟ 3 - ΔΙΕΡΓΑΣΙΕΣ. Κεφάλαιο 3 «Διεργασίες»

Κεφάλαιο 3.1, : Συναρτήσεις I. ( ιάλεξη 11) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Συστήματα Παράλληλης και Κατανεμημένης Επεξεργασίας

Οι εντολές ελέγχου της ροής ενός προγράμματος.

Προγραμματισμό για ΗΜΥ

Εργαστήριο 9: Αρχεία

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκό Έτος Κανονική Εξέταση Λύσεις

Εισαγωγή στον Προγραμματισμό

Κεφάλαιο 2.6: Είσοδος / Έξοδος εδοµένων, Μορφοποίηση εδοµένων Εξόδου. ( ιάλεξη 7) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Προγραμματισμός Ι. Προεπεξεργαστής. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Ο πιο κάτω πίνακας περιγράφει σε ποιες περιπτώσεις χρησιμοποιούμε τους τρεις πιο πάνω τρόπους:

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Η/Υ (ΤΛ2007 )

12. Συναρτήσεις (Μέρος ΙI)

Σημειώσεις όγδοης εβδομάδας

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκό Έτος

Προγραμματισμός συστημάτων UNIX/POSIX. Χρονομέτρηση

Προγραμματισμός Ι. Δείκτες. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Μεθόδων Επίλυσης Προβλημάτων

Εισαγωγή στον Προγραµµατισµό. Διάλεξη 8 η : Συναρτήσεις Χειµερινό Εξάµηνο 2011

ΕΘΝΙΚΟ ΚΑΙ ΚΑΠΟΔΙΣΤΡΙΑΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΘΗΝΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΤΗΛΕΠΙΚΟΙΝΩΝΙΩΝ ΧΕΙΜΕΡΙΝΟ ΕΞΑΜΗΝΟ ΜΑΘΗΜΑ: ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ

Ορισµός Νήµα (thread) είναι µια ακολουθιακή ροή ελέγχου (δηλ. κάτι που έχει αρχή, ακολουθία εντολών και τέλος) σ ένα

ΤΕΙΘ ΣΤΕΦ - ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΜΑΘΗΜΑ: ΕΙΣΑΓΩΓΗ ΣΤΑ ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ (UNIX) ΕΡΓΑΣΤΗΡΙΟ. 6o ΕΡΓΑΣΤΗΡΙΟ

Transcript:

ΕΠΛ371 - Προγραμματισμός Συστημάτων Διάλεξη 14: Διεργασίες: Έλεγχος & Σήματα (Processes: Control & Signals) (Κεφάλαιο 8,10 - Stevens & Rago) Δημήτρης Ζεϊναλιπούρ 14-1

Περιεχόμενο Διάλεξης A. Η οικογένεια συναρτήσεων exec() (8.10) B. Η κλήση συστήματος system() (8.13) Γ. Σήματα στην C (10.1, 10.2) Δ. Η κλήση συστήματος signal() (10.3) Ε. Σήματα και Διεργασίες Ζ. Η Κλήση συστήματος alarm() Η. Δια-διεργασιακά Σήματα (kill()) 14-2

Α. Συναρτήσεις exec() Γνωρίζουμε ότι με την κλήση της fork() κλωνοποίουμε το process image (στοίβα, κώδικα, σωρό, etc) μιας διεργασίας. Οι εντολές exec από την άλλη, αντικαθιστούν το process image μιας διεργασίας Α με αυτά ενός εκτελέσιμου αρχείου (π.χ. ls, sort, uniq, οτιδήποτε εκτελέσιμο!) Στην συνέχεια η Α θα συνεχίσει να εκτελείται από το main() του αρχείου (και όχι από εκεί που έγινε το fork). Με την κλήση της exec το Β θα έχει το ίδιο PID, PPID, File Descriptors, alarms, κτλ. του πατέρα Α. (δες σελ. 234) Memory fork() Memory exec() A Fork() B A B Exec() 14-3

Α. Παράδειγμα 1 - execl() Να γραφεί ένα πρόγραμμα το οποίο να παρουσιάζει το περιεχόμενο του καταλόγου.., κάνοντας χρήση του execl 14-4

Α. Παράδειγμα 1 - execl() /* File: execl_demo.c */ #include <stdio.h> /* For printf */ main() { printf("i am process %d and I will execute an 'ls -l..'\n", getpid()); if (execl("/bin/ls", "ls", "-l", "..", NULL) == -1) { perror("execl"); Παράδειγμα Εκτέλεσης $ execl_demo I am process 13974 and I will execute an 'ls -l..' total 488 drwx-----x 53 antonis faculty 155648 Jan 5 09:52 antonis drwx-----x 47 chryssis faculty 4096 Mar 1 13:46 chryssis 14-5

Α. Συναρτήσεις exec() Υπάρχουν 6 είδη από τα οποία μας ενδιαφέρουν μόνο τα ακόλουθα #include <unistd.h> arguments int execl(char *path, char *arg0, char *arg1,..., char *argn, NULL); int execv(char *path, char *argv[]); Όπου path είναι σχετικό ή απόλυτο μονοπάτι. int execlp(char *path, char *arg0, char *arg1,..., char *argn, NULL); int execvp(char *path, char *argv[]); Όπου στο path δεν χρειάζεται να ορίσει την τοποθεσία του εκτελέσιμου εφόσον θα γίνει χρήση της μεταβλητής περιβάλλοντος PATH για να βρεθεί το εκτελέσιμο. Αυτό είναι χρήσιμο γιατί μπορεί να μην ξέρουμε που ακριβώς βρίσκεται ένα εκτελέσιμο π.χ. sort, uniq, etc). Και οι τέσσερις επιστρέφουν -1 σε λάθος ενώ σε επιτυχία τίποτα. Η διαφορά της execl(list) / execv(vector) είναι ότι το execl παίρνει σαν όρισμα: arg0=«όνομα εκτελέσιμου», arg1=«μεταβλητή 1»,., argn=«μεταβλητή n», arg(n+1)=null ενώ η execv argv[0], argv[1],, argv[n], argv[n+1]=null. H ίδια διαφορά ισχύει και για το ζεύγος execlp(list,path) / execvp (vector,path). Οι πιο πάνω εντολές είναι εντολές βιβλιοθήκης στο Linux που βασίζονται στην κλήση συστήματος execve (vector environment). 14-6 Δηλώνει ότι εδώ τελειώνει το arg list (εναλλακτικός ορισμός (char *)0)

Α. Παράδειγμα 2 - execvp() #include <stdio.h> /* For printf */ int main() { int pid, status; char *argv[2]; if ((pid = fork()) == -1) { /* Check for error */ perror("fork"); exit(1); else if (pid == 0) { /* Child process */ argv[0] = "date"; argv[1] = NULL; printf("i am child process %d and I will replace myself by 'date'\n", getpid()); if (execvp("date", argv) == -1 ) { perror("execvp"); exit(1); else { /* Parent process */ exit(0); if (wait(&status)!= pid) { /* Wait for child */ perror("wait"); exit(1); printf("i am parent process %d\n", getpid()); printf("child terminated with exit code %d\n", status >> 8); Περιγραφή Δημιουργήστε μια διεργασία παιδί η οποία εκτελεί την date. Μόλις ολοκληρώσει το παιδί ο πατέρας συνεχίζει την εκτέλεση του. Αποτέλεσμα Εκτέλεσης $./execvp-example2 I am child process 508 and I will replace myself by 'date' Sun Mar 4 12:50:06 RST 2007 I am parent process 3340 Child terminated with exit code 0 14-7

Α. Παράδειγμα 3 Υλοποίηση Απλού Κελύφους με execvp Να γραφεί ένα πρόγραμμα C που να λειτουργεί σαν ένα απλό κέλυφος: Να δέχεται από το πληκτρολόγιο εντολές τις οποίες να αναλαμβάνει να εκτελέσει μια διεργασία-παιδί που θα δημιουργεί. Memory Parent Wait() fork() A child Execvp() 14-8

Α. Παράδειγμα 3 Υλοποίηση Απλού Κελύφους /* File: simpleshell.c */ #include <stdio.h> /* For fgets, printf */ void parse(char *buf, char **argv); /* Function prototype */ void execute(char **argv); /* Function prototype */ int main() { char buf[1024]; char *argv[64]; while (1) { printf("command: "); /* Get input */ if (fgets(buf, sizeof(buf), stdin) == NULL) { printf("\n"); exit(0); parse(buf, argv); /* Split buf into arguments copied into argv */ execute(argv); return 0; Memory fork() Parent Wait() Get user command into buf A child Execvp() 14-9

Α. Παράδειγμα 3 Υλοποίηση Απλού Κελύφους // Tokenizes buf arguments into argv table void parse(char *buf, char **argv) { char *tmp; // used to perform a linear scan of buf tmp = buf; // initialize buf to the same position with buf while (*tmp!= '\0') { if ((*tmp == ' ') (*tmp == '\t') (*tmp == '\n')) { *tmp = '\0'; // replace whitespace with NULL *argv++ = buf; // first set *argv=buf then *argv++ //printf("%s\n", *--argv); buf = ++tmp; // set buf to the next string tmp++; *argv=null; /* End of arguments */ Memory Parent Wait() fork() A child Execvp() tmp π.χ. buf= ls a l i Argv[ 0,1,2,3,NULL ] Vector + PATH void execute(char **argv) { int pid, status; if ((pid = fork()) < 0) { /* Create a child */ perror("fork"); exit(1); else if (pid == 0) { /* Child process */ if (execvp(argv[0], argv) == -1) { perror(argv[0]); exit(1); else { /* Parent process */ if (wait(&status)!= pid) { perror("wait"); exit(1); 14-10

Β. Κλήση Συστήματος system() Σε πολλές περιπτώσεις, όπως το προηγούμενο παράδειγμα, θέλουμε το παιδί να εκτελέσει κάποιο υφιστάμενο προγ. π.χ., #include <unistd.h> int main() { system("ls -al sort > output.txt"); exit(0); int system(const char *cmdstring); επιστρέφει -1 (out of process ή 0 ΟΚ) Memory A c)wait() Το πρόγραμμα C περιμένει μέχρι να ολοκληρώσει η εντολή system. Η εντολή system υλοποιείται ουσιαστικά μέσω fork (παιδί), exec(παιδί), waitpid (πατέρας περιμένει το παιδί). (μελετήστε το παράδειγμα ΕΠΛ 371 Προγραμματισμός στο σχήμα Συστημάτων 8.22 (σελίδα 247) System() a)fork() B b)exec() 14-11

Β. Παράδειγμα 3 - system() Πόσες συνολικά διεργασίες θα δημιουργηθούν από την εκτέλεση του προγράμματος sysexec.c το οποίο χρησιμοποιεί την εντολή system για να καλέσει το πρόγραμμα κελύφους batch.sh; $ cat sysexec.c #include <unistd.h> int main() { system("./batch.sh"); exit(0); $cat./batch.sh i=0; while [ $i -lt 1000 ]; do echo -n " $i "; ls -al sort uniq rev > /tmp/file; ((i++)); done 14-12

Β. Παράδειγμα 3 - system() Η εκτέλεση του sysexec(7228) δημιουργεί μια νέα διεργασία sh(7229) η οποία κάνει 1000 επαναλήψεις. Σε κάθε από τις 1000 επαναλήψεις δημιουργούνται 4 διεργασίες (ls, rev, sort, uniq). Θυμίζουμε ότι το ls, rev, sort, uniq δεν είναι built-in εντολές του κελύφους (δηλαδή δεν βρίσκονται μέσα στο εκτελέσιμο αρχείο του shell). Επομένως κάθε κλήση τους δημιουργεί μια νέα διεργασία. Επομένως θα έχουμε 4*1000 (ls,sort,rev,uniq) + 1 (sysexec(7228) ) + 1 (sh(7229) ) = 4002 διεργασίες Πιο κάτω φαίνονται δυο από τα 1000 στιγμιότυπα με την εντολή pstree. $ pstree -Gp dzeina sshd(31040) bash(31042) sysexec (2709) sh(7229) ls(2852) rev(2854) sort(2853) uniq(2855) $ pstree -Gp dzeina sshd(31040) bash(31042) sysexec (2709) sh(7229) ls(2953) rev(2955) sort(2954) uniq(2956) 14-13

Γ. Διαχείριση Σημάτων (Signals) Signals (software interrupts): Μικρά μηνύματα, τα οποία στέλνονται σε μια καθορισμένη διεργασία ή ομάδες διεργασιών. Τα σήματα στέλνονται μεταξύ Διεργασιών (εάν υπάρχουν τα κατάλληλα δικαιώματα, π.χ. kill()) ή από το Πυρήνα στην Διεργασία (π.χ. alarm()) Όταν λάβει το signal μια διεργασία, διακόπτει άμεσα την εκτέλεση της, και διαχειρίζεται το signal. Η διαχείριση του σήματος γίνεται ουσιαστικά με ένα signal handler Εναλλακτικά, η διεργασία μπορεί να αγνοήσει το σήμα ( αλλά όχι όλα τα σήματα) Εδώ θα μελετήσουμε τα σήματα στο πλαίσιο της γλώσσας C ενώ τα είχαμε δει επίσης στο πλαίσιο του κελύφους. 14-14

Γ. Διαχείριση Σημάτων (Signals) Κάθε σήμα στο UNIX αποτελείται: από ένα αριθμό (π.χ., 2) ή αντίστοιχα από ένα ισοδύναμο συμβολικό όνομα (π.χ., SIGINT), Ένα διαχειριστή σήματος (signal handler) ο οποίος μπορεί να αντικαταστήσει τη μέθοδο με την οποία ένα πρόγραμμα διαχειρίζεται το σήμα. Για να προγραμματίσουμε τα σήματα στην C πρέπει να συμπεριλάβουμε την βιβλιοθήκη #include <signal.h> 14-16

Δ. Κλήση Συστήματος signal() #include <signal.h> void cntl_c_handler(int sig) { printf("just received signal %d\n", sig); main() { int I; void (*fp)(int); // default sighandler variable fp = signal(sigint, cntl_c_handler); for (i=0;i<5;i++) { printf("still running\n"); sleep(1); signal(sigint, fp); // επαναφορά default Sighandler Set New signal Handler & Backup previous one in fp Αριθμός σήματος που θα ενεργοποιήσει τον handler Παράδειγμα Εκτέλεσης $./simplesignal $./a Still running Just received signal 2 Still running Just received signal 2 Still running Just received signal 2 Still running Just received signal 2 εδώ διακόπτεται * Υποθέστε ότι στέλνουμε το σήμα SIGINT από το κέλυφος 14-17

E. Σήματα και Διεργασίες Εάν δηλώσουμε ένα signal handler και κάνουμε fork τότε αυτός ο signal handler ισχύει και για το παιδί. #include <signal.h> void signal_handler(int sig) { printf("a signal %d was received by %d \n", sig, getpid()); int main(void) { pid_t pid; /* Install signal handler */ signal(sigint, signal_handler); if ( (pid = fork()) < 0) perror("fork error"); else if (pid == 0) while (1) { printf("child (orphan) still alive\n"); sleep(1); else { printf("parent has finished\n"); exit(0); Σημειώστε ότι πρόκειται για ένα ορφανό process Για να δούμε ότι παραλαμβάνετε από το παιδί μπορούμε να στείλουμε από το κέλυφος $kill -2 4256 Βλέπουμε ότι θα παραληφθεί από το παιδί child (orphan) still alive child (orphan) still alive A signal 2 was received by 4256 child (orphan) still alive child (orphan) still alive child (orphan) still alive 14-21

E. Σήματα και Διεργασίες Ασύγχρονη Αποφυγή Zombies με Signals Στην διάλεξη 13 (τελευταίο παράδειγμα) είδαμε πως μπορούμε να αποφύγουμε τα zombies με κάποιο σύγχρονο τρόπο. Δηλαδή, εκτελούμε wait() μέχρι να μας επιστρέψει το exit code το παιδί. Αυτό όμως σταματούσε την εκτέλεση του πατέρα (στο wait()) μέχρι να απαντήσει το παιδί. (δηλ. ο πατέρας έκανε blocking wait) Τώρα θα δούμε ένα εναλλακτικό, και προγραμματιστικά πιο ορθό τρόπο, για την ασύγχρονη αποφυγή των zombies. Θα κάνουμε χρήση του SIGCHLD, το οποίο στέλνεται από το παιδί στο πατέρα όποτε το παιδί τερματίσει η σταματήσει προσωρινά την λειτουργία του. 14-22

E. Σήματα και Διεργασίες Ασύγχρονη Αποφυγή Zombies με Signals #include <signal.h> #include <unistd.h> // STDOUT_FILENO void signal_handler(int sig) { int pid; int status; printf( #%d: A signal %d was received \n", getpid(), sig); pid = wait(&status); printf( #%d: Exit Code %d %d\n", getpid(), pid, status>>8); int main() { int pid; signal(sigchld, signal_handler); /* Install signal handler */ pid = fork(); if (pid == -1) { /* Check for error */ perror("fork"); exit(1); else if (pid == 0) { /* The child process */ printf( #%d: Exit!\n, getpid()); exit(37); /* Exit with a silly number */ else { /* The parent process */ while (1) /* Never terminate */ sleep(1000); Τοποθέτηση wait() μέσα στον signal_handler έτσι ώστε να μπορεί το παιδί να παραδώσει το exit code ανεξάρτητα με το πόσο απασχολημένος είναι ο πατέρας. Αυτό περιορίζει τα zombie processes. Αποτέλεσμα Εκτέλεσης $./chldsignal #5288: Exit! (Child) #2920: A signal 20 was received #2920: Exit Code 5288 37 Αυτό προηγούμενα ΕΠΛ 371 Προγραμματισμός μας δημιουργούσε Συστημάτων zombie processes 14-23

Η. Δια-διεργασιακά Σήματα Κλήση Συστήματος kill() Μέχρι τώρα είδαμε πως μπορούμε να στείλουμε σήματα από τον πυρήνα (π.χ. SIGALRM) και από το κέλυφος (π.χ. SIGINT) σε μια διεργασία. Τι γίνεται εάν θέλουμε να στείλουμε σήματα μεταξύ διεργασιών; Υπάρχει η κλήση συστήματος kill int kill(int pid, int sigcode); Επιστρέφει -1 σε αποτυχία ή 0 σε επιτυχία Η kill είναι μόνο εφικτή εάν η sending διεργασία έχει τον ίδιο ιδιοκτήτη με την receiving διεργασία (ή o sender είναι root) Για δια-εργασιακή αποστολή σημάτων μπορούμε να χρησιμοποιήσουμε τα μη-δεσμευμένα για άλλο σκοπό SIGUSR1, SIGUSR2. 14-24

Η. Δια-διεργασιακά Σήματα Κλήση Συστήματος kill() #include <stdio.h> /* For printf */ #include <signal.h> /* For SIGTERM, SIGSTOP, SIGCONT */ int main() { int pid1, pid2; if ((pid1 = fork()) == -1) { perror("fork"); return 1; else if (pid1 == 0) { // first child loop while (1) { /* Infinite loop */ printf("process 1 is alive\n"); sleep(1); else { // parent if ((pid2 = fork()) == -1) { perror("fork"); return 1; else if (pid2 == 0) { // second child loop while (1) { /* Infinite loop */ printf("process 2 is alive\n"); sleep(1); // parent s code sleep(2); kill(pid1, SIGSTOP); /* Suspend first child */ sleep(2); kill(pid1, SIGCONT); /* Resume first child */ sleep(2); kill(pid1, SIGTERM); /* Terminate first child */ kill(pid2, SIGTERM); /* Terminate second child */ return 0; Περιγραφή Δημιουργούμε δυο παιδιά τα οποία εκτελούν από ένα άπειρο βρόχο. Στη συνέχεια σταματάμε το 1 ο, το συνεχίζουμε και τέλος τερματίζουμε και τις δυο διεργασίες. $./a.exe Process 1 is alive Process 2 is alive Process 1 is alive Process 2 is alive Process 1 is alive Process 2 is alive 14-25

Ζ. Κλήση Συστήματος alarm() #include <unistd.h> unsigned int alarm(unsigned int count); Επιστρέφει 0 ή τον αριθμό δευτερολ. μέχρι το προηγούμενο set alarm. Δίνει την εντολή στον πυρήνα να στείλει μετά από count δευτερόλεπτα το σήμα SIGALRM στην καλούσα διεργασία. Ο μετρητής κρατείται μέσα στον πυρήνα. Επισημάνσεις To alarm ΔΕΝ κληρονομείται στο παιδί. Επίσης το alarm γίνεται reset κάθε φορά που φθάνει στην διεργασία οπόταν πρέπει να ξαναγίνει installed εάν θέλουμε να το λαμβάνουμε συνέχεια (δες παράδειγμα) 14-29

Ζ. Παράδειγμα alarm() #include <signal.h> // signal, alarm #include <unistd.h> // fork, getpid #include <time.h> // time_t, time Alarm signal handler void signal_handler(int sig) { time_t rawtime; time ( &rawtime ); /* compute the current time */ printf("#%d: The kernel sent a SIGALRM(%d) on: %s", getpid(), sig, ctime (&rawtime)); alarm(1); /* we re-install the alarm here */ int main() { int pid; /* Install signal handler and set the alarm clock */ signal(sigalrm, signal_handler); alarm(1); pid = fork(); if (pid == -1) { perror("fork"); exit(1); else if (pid == 0) { /* The child process */ while (1) { printf("#%d\n", getpid()); sleep(3); else { while (1) { printf("#%d\n", getpid()); sleep(3); Αποτέλεσμα Εκτέλεσης #6020 (child) #2280 (parent) #2280: The kernel sent a SIGALRM(14) on: Sun Mar 4 23:58:58 2007 #2280 #2280: The kernel sent a SIGALRM(14) on: Sun Mar 4 23:58:59 2007 #2280 #2280: The kernel sent a SIGALRM(14) on: Sun Mar 4 23:59:00 2007 #2280 #6020 Μόνο ο πατέρας 14-30 παραλαμβάνει το alarm