Σήματα και χειρισμός σημάτων

Slides:



Advertisements
Παρόμοιες παρουσιάσεις
Το αλφαριθμητικό (string)
Advertisements

Λογισμικο συστηματοσ Κεφάλαιο 4ο
Προγραμματισμός Ι (αποφύγετέ τον!) 3) Διακοπτόμενος βρόχος: (αποφύγετέ τον!) float energy;......while(TRUE){drink_water(); if(energy
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 2ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 26 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 11.
Συστήματα εισόδου/εξόδου
Η επιστήμη των υπολογιστών
Λειτουργικά Συστήματα
Λειτουργικό Σύστημα 2ο μέρος.
Κεφάλαιο 1ο: ΒΑΣΙΚΕΣ ΕΝΝΟΙΕΣ ΤΩΝ ΛΕΙΤΟΥΡΓΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ
Message Passing Interface (MPI)
Τύποι πραγματικών αριθμών
HY100 : ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΕΠΙΣΤΗΜΗ ΥΠΟΛΟΓΙΣΤΩΝ ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΡΗΤΗΣ, ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ, ΤΜΗΜΑ ΕΠΙΣΤΗΜΗΣ ΥΠΟΛΟΓΙΣΤΩΝ ΔΙΔΑΣΚΟΝΤΕΣ Αντώνιος Σαββίδης, Χρήστος.
Λειτουργικά Συστήματα
Συναρτήσεις Κληση/Επιστροφη Παραμετροι
24/11/2003Message Passing Interface (MPI)1 Αθήνα, Νοέμβριος 2003 Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων.
22/11/2004Message Passing Interface (MPI)1 Αθήνα, Νοέμβριος 2004 Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων.
ΗΥ-150 Προγραμματισμός Εντολές Ελέγχου Ροής.
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 2ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 11 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1.
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 3ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 19 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1.
Νήματα Οι διεργασίες έχουν τα παρακάτω συστατικά:
ΕΠΑΝΑΛΗΨΗΕΠΑΝΑΛΗΨΗ ΠΡΟΓΡΑΜΜΑΤΑ. ΠΡΟΓΡΑΜΜΑ 1 ΕΞΗΓΗΣΤΕ ΤΙ ΕΞΟΔΟ ΠΑΡΑΓΕΙ ΤΟ ΠΑΡΑΚΑΤΩ ΠΡΟΓΡΑΜΜΑ #include int main() { char ch; int i; float fl; printf("dose.
Η ΓΛΩΣΣΑ C ΜΑΘΗΜΑ 2.
Τι είναι διεργασία Ένα πρόγραμμα σε εκτέλεση Η διεργασία περιλαμβάνει:
1 Ολυμπιάδα Πληροφορικής Μάθημα 7. 2 Στόχοι μαθήματος Δημιουργία συναρτήσεων από το χρήστη Δομή προγράμματος με συναρτήσεις Συναρτήσεις και παράμετροι.
Ολυμπιάδα Πληροφορικής
ΣΥΝΑΡΤΗΣΕΙΣ.
Microsoft Excel 4.4 Τύποι και Συναρτήσεις
Message Passing Interface (MPI) Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων Αθήνα, Δεκέμβριος 2002.
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Αρχεία.
Επικοινωνία Ανθρώπου Μηχανής HTML CGI JAVASCRIPT Κουμπούλης Χρήστος Α.Μ. 921 Χαλαβαζής Βασίλης Α.Μ. 988.
Templates Standard Template Library (STL) Exceptions Μεταπτυχιακό Πρόγραμμα Σπουδών, Τμήμα Εφαρμοσμένης Πληροφορικής.
Κεφάλαιο 10 – Υποπρογράμματα
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Αναδρομή (1/2)
Διεργασίες Λειτουργικά Συστήματα. Λειτουργικά Συστήματα/ Slide 2 Η Έννοια της Διεργασίας * Διεργασία (Process) – ο μηχανισμός εκτέλεσης ενός προγράμματος.
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Συναρτήσεις.
ΗΥ 150 – Προγραμματισμός Ξενοφών Ζαμπούλης ΗΥ -150 Προγραμματισμός Αρχεία.
ΗΥ150 – ΠρογραμματισμόςΞ. Ζαμπούλης ΗΥ-150 Προγραμματισμός Αρχεία.
Κεφάλαιο 4 Εξαιρέσεις. Όταν σε ένα πρόγραμμα συμβεί κάποιο λάθος, ο κώδικας εγείρει (throw) μία εξαίρεση. Στη Java oι εξαιρέσεις εκπροσωπούνται από αντικείμενα.
ΛΟΓ201: Τεχνολογία Λογισμικού ΙΙ Διδάσκων: Νίκος Παπασπύρου 1Νίκος ΠαπασπύρουΛΟΓ201:
1 Κέλυφος Bash – Τι μάθαμε? Μεταξύ άλλων…  Συνθήκες ελέγχου (if, case…) και βρόχοι επανάληψης (for, while)  Πράξεις ακεραίων (let, expr) και δεκαδικών.
Διεργασίες.
ΗΥ150 – ΠρογραμματισμόςΞενοφών Ζαμπούλης ΗΥ-150 Προγραμματισμός Αναδρομή (1/2)
ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΕΣ ΤΕΧΝΙΚΕΣ Διδάσκοντες:Γιάννης Μαΐστρος Στάθης Ζάχος Νίκος Παπασπύρου
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Συναρτήσεις (μέρος δεύτερο) και Μεταβλητές.
Κεφάλαιο 3 Τύποι Δεδομένων - Τελεστές. Πρωτογενείς τύποι δεδομένων: int, float, double, chars ΤύποςΌνομαΜέγεθος byte 8-bit signed, short 16-bit.
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Τύποι Μεταβλητών Τελεστές Βασική Είσοδος/Έξοδος.
Νήματα με την χρήση των Posix Threads (pthreads)‏.
1 ο Εργαστήριο: Κατασκευή Εφαρμογών Client-Server echoserver και echoclient – Κώδικας εφαρμογών σε C Μπαλόμπας Παναγιώτης 8ο ΕΠΑΛ Θεσσαλονίκης 1 /* Αρχείο.
ΠΑΡΑΔΕΙΓΜΑ: ΤΑ ΕΠΙΠΕΔΑ ΥΛΙΚΟΥ – ΛΟΓΙΣΜΙΚΟΥ ΣΕ ΕΝΑΝ ΥΠΟΛΟΓΙΣΤΗ.
Διοίκηση Επιχειρήσεων Βάσεις Δεδομένων και Ευφυή Πληροφοριακά Συστήματα Επιχειρηματικότητας Βάσεις Δεδομένων και Ευφυή Πληροφοριακά Συστήματα Επιχειρηματικότητας.
Για μτ από ατ μέχρι ττ [με_βήμα β] εντολές Τέλος_επανάληψης : περιοχή εντολών μτ : η μεταβλητή της οποίας η τιμή θα περάσει από την αρχική.
ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ Ι
Αρχεσ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Η/Υ ΤΑξη Β΄
Χειρισμός Χρόνου και Μεθοδολογίες Προσομοίωσης
ΑΛΓΟΡΙΘΜΟΣ ΠΡΟΒΛΗΜΑ ΑΛΓΟΡΙΘΜΟΣ ΛΥΣΗ
Κατανεμημένα Συστήματα
Ανάπτυξη Μοντέλων Διακριτών Συστημάτων Μέρος Β
Λειτουργικά Συστήματα και Ασφάλεια Πληροφοριακών Συστημάτων ΔΙΕΡΓΑΣΙΕΣ
for (παράσταση_1; παράσταση_2; παράσταση_3)
ΣΥΝΑΡΤΗΣΕΙΣ (Functions)
Εισαγωγή στον Προγ/μό Υπολογιστών
Κεφάλαιο 10 Streams.
Processes.
ΔΙΕΡΓΑΣΙΕΣ.
ΗΥ-150 Προγραμματισμός Αναδρομή (1/2).
Προγραμματισμός ΗΥ Ενότητα 12: Αρχεία Δομών. Διδάσκων: Ηλίας Κ Σάββας,
Αρχές Προγραμματισμού (C)
Αναδρομή Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα:
ΕΚΦΡΑΣΕΙΣ, ΑΝΑΜΟΝΕΣ (DELAYS), ΗΧΟΙ
Λήψη Αποφάσεων και Συναρτήσεις Ελέγχου
Μεταγράφημα παρουσίασης:

Σήματα και χειρισμός σημάτων

Εισαγωγή Τα σήματα είναι διακοπές μέσω λογισμικού Παρέχουν τα μέσα για χειρισμό ασύγχρονων γεγονότων π.χ. το πάτημα του CTRL-C στο πληκτρολόγιο ή την πρόσβαση σε άκυρη διεύθυνση μνήμης Αποστέλλονται από τον πυρήνα σε διεργασίες ή από μία διεργασία σε μία άλλη Έχουν ενσωματωθεί από τις πρώτες εκδόσεις του UNIX, ωστόσο μόνο πρόσφατα έχουν προτυποποιηθεί πλήρως δυνατότητα για αναστολή παράδοσης σημάτων όταν εκτελούνται κρίσιμα τμήματα αξιοπιστία σημάτων (κανένα σήμα δεν χάνεται)

Βασικές έννοιες Κάθε σήμα έχει ένα όνομα (συμβολική σταθερά, θετικός ακέραιος) Όλα τα ονόματα ξεκινούν με το πρόθεμα SIG, π.χ. SIGABRT, SIGSEGV, SIGINTR Τα ονόματα και όλες οι σχετικές σταθερές και συναρτήσεις ορίζονται στο αρχείο <signal.h> Τα σήματα δημιουργούνται από διάφορες αιτίες: Τερματικό: πάτημα CTRL-C, CTRL-Z ή αποσύνδεση από το τερματικό Υλικό: διαίρεση με το 0, πρόσβαση σε άκυρη θέση μνήμης, εκτέλεση άκυρης εντολής Προγραμματιστικά: το πρόγραμμα χρησιμοποιεί την κλήση συστήματος kill για να στείλει σε κάποια άλλη διεργασία ένα σήμα Κατ’ εντολή του χρήστη: ο χρήστης (ή διαχειριστής) χρησιμοποιεί την εντολή kill για να στείλει σε κάποια διεργασία ένα σήμα Ενημέρωση διεργασίας: εφιστάται η προσοχή της διεργασίας σε κάτι που πρέπει να γνωρίζει π.χ. άφιξη επισπευσμένων δεδομένων από το δίκτυο, εκπνοή χρονομετρητή, εγγραφή σε σωλήνωση χωρίς αναγνώστες κ.λπ.

Χειρισμός σημάτων Όταν φθάσει ένα σήμα σε μία διεργασία αυτή μπορεί να: το αγνοήσει. Είναι δυνατόν για τα περισσότερα σήματα εκτός από τα SIGKILL και SIGSTOP. Το να αγνοήσουμε σήματα που σχετίζονται με το υλικό (π.χ. SIGSEGV, SIGFPE) δεν είναι καλή ιδέα το παγιδεύσει, δηλ. να υποδείξει στον πυρήνα να καλέσει μία συγκεκριμένη συνάρτηση που θα κάνει όποιες ενέργειες είναι απαραίτητες π.χ. SIGINT σε φλοιό  τερματισμό της εντολής που εκτελείται – όχι του φλοιού, SIGTERM  κλείσιμο και διαγραφή προσωρινών αρχείων, SIGALRM  ενημέρωση ρολογιού στην οθόνη αφήσει να εκτελεστεί η εξ ορισμού ενέργεια για το συγκεκριμένο σήμα (συνήθως τερματισμός ή αγνόηση)

Σήματα & εξ ορισμού ενέργειες (1/2) ΟΝΟΜΑ ΠΕΡΙΓΡΑΦΗ Εξ ορισμού ΣΧΟΛΙΑ SIGABRT Μη κανονικός τερματισμός τερμ/core SIGALRM Εκπνοή χρονομετρητή τερμ SIGBUS Πρόβλημα αρτηριών δεδομένων/διευθύνσ. SIGCHLD Αλλαγή κατάστασης θυγατρικής διεργασίας αγνόηση έλεγχ. διεργ. SIGCONT Συνέχιση διεργασίας που έχει σταματήσει συνέχεια/αγνόηση SIGEMT Πρόβλημα υλικού SIGFPE Σφάλμα σε αριθμητική πράξη SIGHUP Αποσύνδεση τερματικού ελέγχου SIGILL Άκυρη εντολή σε επίπεδο υλικού SIGINFO Αίτηση αναφοράς κατάστασης από πληκτρολογ. SIGINT Χαρακτήρας διακοπής από πληκτρολόγιο SIGIO Ολοκλήρωση ασύγχρονης εισόδου/εξόδου τερμ/αγνόηση SIGIOT Σφάλμα υλικού σε είσοδο/έξοδο SIGKILL Τερματισμός SIGPIPE Εγγραφή σε σωλήνωση χωρίς αναγνώστες SIGPOLL Συμβάν που μπορεί να διαβαστεί SIGPROF Χρονομετρητής καταγραφής επιδόσεων

Σήματα & εξ ορισμού ενέργειες (2/2) ΟΝΟΜΑ ΠΕΡΙΓΡΑΦΗ Εξ ορισμού ΣΧΟΛΙΑ SIGPWR Διακοπή/αποκατάσταση παροχής ρεύματος αγνόηση SIGQUIT Χαρακτήρας τερματισμού από πληκτρολόγιο τερμ/core SIGSEGV Αναφορά σε άκυρη θέση μνήμης SIGSTOP Αναστολή εκτέλεσης διεργασίας αναστολή έλεγχ. διεργ. SIGSYS Άκυρη κλήση συστήματος SIGTERM Τερματισμός SIGTRAP Σφάλμα υλικού SIGTSTP Χαρακτήρας αναστολής από πληκτρολόγιο SIGTTIN Αίτηση ανάγνωσης από πληκ. από διεργασία παρασκ. SIGTTOU Αίτηση εγγραφής σε οθόνη από διεργασία παρασκ. SIGURG Επισπευσμένα δεδομένα από το δίκτυο SIGUSR1,2 Οριζόμενα από τον χρήστη τερμ SIGVTALRM Εκπνοή χρονομετρητή SIGWINCH Αλλαγή μεγέθους παραθύρου SIGXCPU Υπέρβαση ορίου χρήσης ΚΜΕ SIGXFZ Υπέρβαση ορίου μεγέθους αρχείων

Η συνάρτηση signal Ο απλούστερος τρόπος χρήσης των δυνατοτήτων των σημάτων Πρότυπο: void (*signal(int signo, void (*func)(int)))(int); Πιο απλά: typedef void (*Sigfunc)(int); SigFunc signal(int signo, Sigfunc func); Υποδεικνύουμε στον πυρήνα τη συνάρτηση που πρέπει να κληθεί (func) όταν παραδίδεται το σήμα signo Ειδικές τιμές: SIG_IGN  να αγνοείται το σήμα SIG_DFL  να εκτελείται η εξ ορισμού ενέργεια Τιμή επιστροφής: SIG_ERR αν προέκυψε σφάλμα, αλλιώς η προηγούμενη ρύθμιση για χειρισμό του σήματος

Η συνάρτηση signal – παράδειγμα [signal.c] #include <stdio.h> #include <signal.h> void sigusr(int signo) { if (signo == SIGUSR1) printf("Caught signal USR1\n"); else if (signo == SIGUSR2) printf("Caught signal USR2\n"); else fprintf(stderr, "Unknown signal %d in sigusr\n", signo); return; } int main(void) { int i; printf("Signals example pid = %ld\n", (long)getpid()); if (signal(SIGUSR1, sigusr) == SIG_ERR) fprintf(stderr, "Cannot catch SIGUSR1\n"); if (signal(SIGUSR2, sigusr) == SIG_ERR) fprintf(stderr, "Cannot catch SIGUSR2\n"); for (i = 1; i < 20; i++) sleep(1); return 0;

Εκκίνηση μιας διεργασίας Με την εκτέλεση της fork, η θυγατρική διεργασία έχει ταυτόσημο χειρισμό με τη γονική όλες οι ρυθμίσεις έχουν νόημα και στη θυγατρική Μετά την εκτέλεση της exec, ο χειρισμός όλων των σημάτων είναι είτε ο εξ ορισμού είτε η αγνόηση αν η διεργασία που κάλεσε την exec αγνοούσε το σήμα, το σήμα εξακολουθεί να αγνοείται αλλιώς ισχύει ο εξ ορισμού χειρισμός Παράδειγμα: Εκτελούμε μία διεργασία στο παρασκήνιο cc –o bigprog bigprog.c & δεν πρέπει να επηρεάζεται από το CTRL-C οπότε ο φλοιός κανονίζει το σήμα να αγνοείται: if (fork() == 0) { signal(SIGINT, SIG_IGN); execlp("cc", "cc", "-o", "bigprog", "bigprog.c", NULL); }

Διευθέτηση ανάλογα με την τρέχουσα ρύθμιση Αν το σήμα SIGINT δεν αγνοείται, να παγιδευθεί με τη συνάρτηση f1 if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, f1); Αν το σήμα SIGINT αγνοείται, να παγιδευθεί με τη συνάρτηση f1 αλλιώς να μείνει ως έχει void (*oldhdl)(int); if ((oldhdl = signal(SIGINT, SIG_IGN)) == SIG_IGN) else signal(SIGINT, oldhdl); Για να δούμε την τρέχουσα ρύθμιση πρέπει πρώτα να την αλλάξουμε (αντιμετωπίζεται από τη sigaction)

Μη αξιόπιστα σήματα Στις πρώτες εκδόσεις του Unix τα σήματα ήταν μη αξιόπιστα ένα σήμα μπορούσε υπό συνθήκες να χαθεί δηλ. η διεργασία να μην πληροφορηθεί ποτέ ότι δημιουργήθηκε Οι διεργασίες είχαν περιορισμένο έλεγχο στα σήματα παγίδευση, αγνόηση, εξ ορισμού ενέργεια δεν υπήρχε η δυνατότητα να ανασταλεί η παράδοση ενός σήματος int we_have_a_sigint = 0; void sigusr(int signo) {return;} void sigint(int) { we_have_a_sigint = 1; signal(SIGINT, sigint); } int main(void) { signal(SIGINT, SIG_IGN); signal(SIGUSR1, sigusr); while (we_have_a_sigint == 0) { printf("Waiting for signal to arrive…"); signal(SIGINT, sigint); pause(); } Τι θα συμβεί αν το σήμα εμφανιστεί μετά τον έλεγχο αλλά πριν την signal/pause();

Μη αξιόπιστα σήματα Ο χειρισμός σημάτων επανερχόταν στην εξ ορισμού ρύθμιση σε κάθε εμφάνιση του σήματος απαιτούνταν εκ νέου κλήση της signal, συνήθως στη συνάρτηση χειρισμού void sig_int(int siginfo) { /* χειρισμός σήματος */ signal(SIGINT, SIG_IGN); } int main(void) { … και πάλι για ένα διάστημα στη συνάρτηση χειρισμού ισχύει ο εξ ορισμού τρόπος χειρισμού

Διακοπή κλήσεων συστήματος Στις πρώτες εκδόσεις του UNIX όταν ένα σήμα έφτανε σε διεργασία που είχε ανασταλεί σε μία «αργή» κλήση συστήματος, η κλήση συστήματος διακόπτεται, επιστρέφει –1 και θέτει το errno σε EINTR «αργές» κλήσεις συστήματος: ανάγνωση από πηγές που μπορεί να μην έχουν έτοιμα δεδομένα (δίκτυο, τερματικό, σωληνώσεις), εγγραφή σε προορισμούς που μπορεί να αναστείλουν τον εγγραφέα μέχρι την αποδοχή τους (δίκτυο, σωληνώσεις), άνοιγμα αρχείων που μπορεί να οδηγήσουν σε αναστολή (σωληνώσεις, μόντεμ), η συνάρτηση pause και μερικές εντολές ioctl (π.χ. επανατύλιξη ταινίας) Οι κλήσεις συστήματος που σχετίζονται με αρχεία δίσκου δεν θεωρούνται αργές και δεν διακόπτονται Η διακοπή των κλήσεων είναι απαραίτητη – π.χ. πάτημα CTRL-C όταν η διεργασία προσπαθεί να διαβάσει από σωλήνωση που αργεί πολύ να στείλει δεδομένα

Διακοπή κλήσεων συστήματος Η πρακτική της διακοπής των κλήσεων συστήματος επιφορτίζει την εφαρμογή με την αναγκαιότητα του ελέγχου για τέτοιες περιπτώσεις: while (1){ if ((n = read(fd, buff, BUFSIZE)) > 0) break if (errno != EINTR) break; } Στο 4.2 BSD εισήχθη η έννοια της αυτόματης επανεκκίνησης των κλήσεων συστήματος Οι κλήσεις ioctl, read, readv, write, writev, wait και waitpid επανεκκινούνταν αυτόματα (οι πέντε πρώτες διακοπτόταν αν ήταν αργές, οι δύο τελευταίες πάντα) Στο 4.3+ BSD επιτρέπεται στην εφαρμογή να ορίζει αν θέλει να διακόπτονται οι κλήσεις συστήματος από τα σήματα Ο ορισμός γίνεται ανά σήμα

Συναρτήσεις αποστολής σημάτων int raise(int signo); Αποστέλλει το σήμα στον εαυτό της int kill(pid_t pid, int signo); Αν pid > 0 αποστέλλεται στη διεργασία με ταυτότητα = pid Αν pid = 0 αποστέλλεται στην ομάδα διεργασιών που ανήκει και η αποστέλλουσα διεργασία [και που υπάρχει δικαίωμα αποστολής] Αν pid < 0 αποστέλλεται σε όλες τις διεργασίες της ομάδας abs(pid) unsigned int alarm(unsigned int nseconds); Μετά από n δευτερόλεπτα θα σταλεί το σήμα SIGALRM στη διεργασία Μόνο ένας μετρητής ανά πάσα χρονική στιγμή ανά διεργασία – αν ορίσουμε μετρητή ενώ υπάρχει εκκρεμής καταργείται ο εκκρεμής και η συνάρτηση επιστρέφει το πλήθος των δευτερολέπτων που απέμεναν Η κλήση της alarm με τιμή 0 καταργεί τον εκκρεμή χρονομετρητή void abort(void); Η διεργασία στέλνει το SIGABRT στον εαυτό της int pause(void); Αναστέλλει τη διεργασία μέχρι το επόμενο σήμα

Kill – Παράδειγμα [ckill.c] void sighdl(int signo) { printf("pid %d: Signal %d received\n", getpid(), signo); signal(signo, sighdl); } int main(void) { pid_t childpid; signal(SIGINT, sighdl); signal(SIGSEGV, sighdl); signal(SIGUSR1, sighdl); if ((childpid = fork()) == -1) {perror("Cannot fork"); exit(1);} else if (childpid == 0) { /* child */ kill(getppid(), SIGINT); kill(getppid(), SIGSEGV); kill(getppid(), SIGUSR1); return 0; waitpid(childpid, NULL, 0);

Alarm – Παράδειγμα [alarm.c] void sigalrm(int signo) { return; } int main(void) { char reply[128]; int n; signal(SIGALRM, sigalrm); printf("Enter the password in 5 sec: "); fflush(stdout); alarm(5); if ((n = read(STDIN_FILENO, reply, 128)) < 0) { if (errno == EINTR) { printf("\n\n** Time expired. Ka-boom! **\n\n"); return 1; } alarm(0); reply[n] = '\0'; if (strcmp(reply, "secret\n") == 0) printf("OK, proceed\n"); else printf("Wrong password\n"); return 0;

Συναρτήσεις αποστολής σημάτων int sigsend(idtype_t idtype, id_t id, int sig); idtype: P_PID για μία διεργασία P_PGID για ομάδα διεργασιών P_SID για σύνοδο P_UID για όλες τις διεργασίες με τη συγκεκριμένη ενεργό ταυτότητα χρήστη P_GID για όλες τις διεργασίες με τη συγκεκριμένη ενεργό ταυτότητα ομάδας P_ALL για όλες τις διεργασίες στο σύστημα int sigsendset(procset_t *psp, int sig); Σε όλες τις διεργασίες που περιέχονται στο psp

Σήματα και δυνατότητα επανεισόδου σε κώδικα [sig-reent.c] Ας θεωρήσουμε το εξής παράδειγμα κώδικα: FILE *pwfile; char buff[512]; int getuserid(char *str) { int result = -1; int len = strlen(str); if ((pwfile = fopen("/etc/passwd", "r")) == NULL) return -1; while (fgets(buff, sizeof(buff) - 1, pwfile) != NULL) { if ((strncmp(buff, str, len) == 0) && (buff[len] == ':')) { result = atoi(strchr(buff + len + 1, ':') + 1); break; } } fclose(pwfile); return result; } void myalarm(int signo) { printf("In my alarm\n"); printf("Uid id of root = %d\n", getuserid("root")); signal(SIGALRM, myalarm); alarm(1); } int main(void) { signal(SIGALRM, myalarm); alarm(1); for ( ; ; ) { printf("User id of user nobody is %d\n", getuserid("nobody")); } }

Σήματα και δυνατότητα επανεισόδου σε κώδικα Το πρόγραμμα δεν λειτουργεί σωστά διότι αν το σήμα έρθει ενόσω εκτελείται η getuserid (μεταξύ fopen και fclose) ο χειριστής σήματος καλεί ξανά τη getuserid και οι τιμές των pwfile και buff της νέας εκτέλεσης επικαλύπτουν τις παλιές Το ίδιο μπορεί να συμβεί και με αρκετές διαδικασίες βιβλιοθήκης π.χ. gethostbyaddr Στον χειριστή σήματος πρέπει να χρησιμοποιούνται μόνο συναρτήσεις που χαρακτηρίζονται ως Async-signal-safe (ή MT-safe) – καθορισμένο από το POSIX Επίσης, προσοχή στις κλήσεις συστήματος που επηρεάζουν την τιμή errno – καλό είναι να αποφεύγονται στον χειριστή σήματος

Αξιόπιστα σήματα Ορολογία Ένα σήμα δημιουργείται όταν λαμβάνει χώρα το σχετικό συμβάν Ένα σήμα παραδίδεται στη διεργασία όταν εκτελείται η ενέργεια που συνδέεται μ’ αυτό (εξ ορισμού ή χειριστής) Στο μεσοδιάστημα το σήμα είναι εκκρεμές Μία διεργασία έχει τη δυνατότητα να αναστείλει την παράδοση ενός σήματος Αν δημιουργηθεί ένα σήμα που έχει ανασταλεί (και δεν αγνοείται) αυτό δεν παραδίδεται στη διεργασία αλλά παραμένει εκκρεμές μέχρι η διεργασία να άρει την αναστολή ή να ορίσει ότι το αγνοεί Αν ο χειριστής σήματος αλλάξει ενόσω ένα σήμα είναι υπό αναστολή, θα κληθεί ο νέος χειριστής Αν ένα σήμα δημιουργηθεί πολλές φορές ενώ η παράδοσή του έχει ανασταλεί, μπορεί να παραδοθεί είτε ακριβώς μία (το πιο συνηθισμένο) είτε περισσότερες φορές Αν εκκρεμούν πολλά σήματα για παράδοση σε μία διεργασία, η σειρά με την οποία θα παραδοθούν είναι αυθαίρετη Ο ορισμός ποια σήματα έχουν ανασταλεί και ποια όχι φυλάσσεται σε μία μάσκα σημάτων (ανά διεργασία)

Αλλαγές για αξιοπιστία Όταν καλείται ο χειριστής ενός σήματος, αυτόματα αναστέλλονται παραδόσεις άλλων σημάτων ίδιου τύπου Υπάρχει η δυνατότητα να αναστέλλονται αυτόματα παραδόσεις και άλλων σημάτων Υπάρχει η δυνατότητα οι κλήσεις συστήματος να επανεκκινούνται αυτόματα Ο χειρισμός σήματος δεν επανέρχεται στον εξ ορισμού κατάσταση κάθε φορά που εμφανίζεται ένα σήμα Παρέχεται η δυνατότητα χρήσης εναλλακτικής στοίβας κατά τον χειρισμό των σημάτων

Σύνολα σημάτων Οι συναρτήσεις αξιόπιστης διαχείρισης σημάτων χρησιμοποιούν σύνολα σημάτων Ειδικός τύπος sigset_t που ορίζεται στο signal.h Συναρτήσεις χειρισμού: int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); Επιστρέφουν 0 σε επιτυχία, -1 για σφάλμα int sigismember(const sigset_t *set, int signo); Επιστρέφει 1 αν αληθές, 0 αν ψευδές

Αναστολή παράδοσης σημάτων int sigprocmask(int how, const sigset_t *set, sigset_t *oset); How: SIG_BLOCK για ανασταλεί η παράδοση των σημάτων που περιέχονται στο set (τα άλλα μένουν ως έχουν) SIG_UNBLOCK για να αρθεί η αναστολή της παράδοσης των σημάτων που περιέχονται στο set (τα άλλα μένουν ως έχουν) SIG_SETMASK για να ορισθεί ως μάσκα σημάτων το set ανεξάρτητα από την τρέχουσα ρύθμιση Αν το set είναι NULL, οι τρέχουσες ρυθμίσεις δεν αλλάζουν και η τιμή του how είναι αδιάφορη Σε κάθε περίπτωση, στην παράμετρο oset επιστρέφονται οι ρυθμίσεις που ίσχυαν πριν την κλήση στην sigprocmask

Παράδειγμα [sigprocmask.c] #include <signal.h> #include <stdio.h> int main(void) { sigset_t theSet, oldSet; sigemptyset(&theSet); sigaddset(&theSet, SIGINT); sigaddset(&theSet, SIGFPE); puts("Blocking signals..."); sigprocmask(SIG_BLOCK, &theSet, &oldSet); puts("Generating signals..."); raise(SIGINT); raise(SIGFPE); puts("Waiting..."); sleep(5); sigdelset(&theSet, SIGINT); puts("Unblocking signals..."); sigprocmask(SIG_UNBLOCK, &theSet, &oldSet); return 0; }

Εξετάζοντας τα εκκρεμή σήματα Μπορούμε να εξετάσουμε ποια σήματα εκκρεμούν για παράδοση μέσω της sigpending int sigpending(sigset_t *set); Παράδειγμα: sigpending.c #include <signal.h> #include <stdio.h> int main(void) { sigset_t theSet, oldSet; int i; sigemptyset(&theSet); sigaddset(&theSet, SIGINT); puts("Blocking signal..."); sigprocmask(SIG_BLOCK, &theSet, &oldSet); puts("Generating signal..."); raise(SIGINT); puts("Querying pending signal..."); sigpending(&oldSet); for (i = 1; i < NSIG; i++) if (sigismember(&oldSet, i)) { printf("%d\n", i); signal(i, SIG_IGN); } puts("Unblocking signals..."); sigprocmask(SIG_UNBLOCK, &theSet, &oldSet); return 0; }

Ορισμός χειριστών σημάτων Η συνάρτηση sigaction επιτρέπει τον ορισμό και την αναφορά των χειριστών σημάτων int sigaction(int signo, const struct sigaction *buff, struct sigaction *oact); Η δομή struct sigaction ορίζεται ως: struct sigaction { void (*sa_handler)(); /* Χειριστής ή SIG_IGN ή SIG_DFL */ sigset_t sa_mask; /* Πρόσθετα σήματα για αναστολή */ int sa_flags; /* Ενδείξεις */ } sa_mask: πριν ο πυρήνας καλέσει τον χειριστή προσθέτει τα σήματα σ’ αυτό το σύνολο στη μάσκα σημάτων της διεργασίας. Πριν επιστρέψει στην κανονική ροή, η κανονική μάσκα επαναφέρεται sa_flags: ενδείξεις για τον χειρισμό του σήματος (επόμενη διαφάνεια)

Ενδείξεις χειρισμού σημάτων Ένδειξη Περιγραφή SA_NOCLDSTOP Αν κάποια θυγατρική διεργασία σταματήσει (SIGSTOP, SIGTSTP) δεν παράγεται σήμα SIGCHLD. Μόνο για signo == SIGCHLD SA_RESTART Κλήσεις συστήματος που διακόπτονται από το σήμα αυτό επανεκκινούνται αυτόματα SA_ONSTACK Κατά τον χειρισμό του σήματος χρησιμοποιείται εναλλακτική στοίβα που ορίζεται με τη sigaltstack SA_NODEFER Όταν εκτελείται ο χειριστής δεν αναστέλλεται αυτόματα η παράδοση άλλων σημάτων ίδιου τύπου (όπως στα μη αξιόπιστα σήματα) SA_RESETHAND Όταν παραδίδεται ένα σήμα ο χειρισμός του επανέρχεται στην εξ ορισμού κατάσταση (όπως στα μη αξιόπιστα σήματα) SA_SIGINFO Παρέχονται πρόσθετες πληροφορίες στον χειριστή του σήματος

sigaction – παράδειγμα [alarm-sigaction.c] void sigalrm(int signo) { return; } int main(void) { char reply[128]; int n; struct sigaction alrmact; alrmact.sa_handler = sigalrm; sigemptyset(&alrmact.sa_mask); alrmact.sa_flags = SA_RESTART; sigaction(SIGALRM, &alrmact, NULL); printf("Enter the password in 5 sec: "); fflush(stdout); alarm(5); if ((n = read(STDIN_FILENO, reply, 128)) < 0) { if (errno == EINTR) { /* Never happens !*/ printf("\n\n** Time expired. Ka-boom! **\n\n"); return 1; } alarm(0); reply[n] = '\0'; printf("\nreply = %s\n", reply); if (strcmp(reply, "secret\n") == 0) printf("OK, proceed\n"); else printf("Wrong password\n"); return 0;

sigsetjmp, siglongjmp Πολλές φορές οι χειριστές σημάτων δεν πρέπει να επιστρέψουν στην διακοπείσα ροή εκτέλεσης αλλά σε πολύ προγενέστερο σημείο π.χ. το sigalrm τερματίζει τον τρέχοντα υπολογισμό, το SIGPIPE την επικοινωνία με άλλη διεργασία κ.λπ. Αυτό μπορεί να μοντελοποιηθεί με τις συναρτήσεις setjmp/longjmp αλλά αν κληθούν μέσα από χειριστή σήματος το σήμα του οποίου ο χειριστής εκτελείται παραμένει υπό αναστολή Λύση: sigsetjmp, siglongjmp όπου μπορεί να προσδιοριστεί αν επιθυμούμε αποθήκευση/αποκατάσταση της μάσκας σημάτων int sigsetjmp(sigjmp_buf env, int savemask); void siglongjmp(sigjmp_buf env, int val);

sigsetjmp, siglongjmp – Παράδειγμα [siglongjmp.c] sigjmp_buf progstate; int result; void alrm(int signo) { siglongjmp(progstate, 1); } void work() { int newVal; result = 0; for (; ;) { newVal = rand(); if (newVal > result) result = newVal; } int main(void) { if (sigsetjmp(progstate, 1) == 0) { /* Normal flow */ signal(SIGALRM, alrm); alarm(3); work(); } else { printf("Computed max = %d\n", result); } return 0;

Άρση αναστολής και αναμονή για σήμα Δείτε το ακόλουθο πρόγραμμα [sig-pause-prob.c]: void sigalrm(int signo) {return;} int main(int argc, char *argv[]) { sigset_t theSet, oldSet; signal(SIGALRM, sigalrm); alarm(3); sigemptyset(&theSet); sigaddset(&theSet, SIGALRM); puts("Blocking alarm signal for critical region..."); sigprocmask(SIG_BLOCK, &theSet, &oldSet); puts("Executing critical region..."); sleep((argc > 1) ? (atoi(argv[1])) : 1); puts("Done critical region, unblocking signals..."); sigprocmask(SIG_UNBLOCK, &theSet, &oldSet); puts("Waiting for alarm signal..."); pause(); return 0; } Αν το σήμα έχει δημιουργηθεί πριν την άρση αναστολής, η pause δεν τερματίζει

sigsuspend – άρση αναστολής σήματος και αναμονή ως ατομική λειτουργία int sigsuspend(const sigset_t *sigmask); Θέτει τη μάσκα σημάτων στην παρεχόμενη και αναμένει για ένα σήμα. Μετά την ολοκλήρωση η μάσκα επαναφέρεται στην προηγουμένως ισχύουσα. Πάντα επιστρέφει –1 με το errno στην τιμή EINTR – sigsuspend.c void sigalrm(int signo) {return;} int main(int argc, char *argv[]) { sigset_t theSet, oldSet; signal(SIGALRM, sigalrm); alarm(3); sigemptyset(&theSet); sigaddset(&theSet, SIGALRM); puts("Blocking alarm signal for critical region..."); sigprocmask(SIG_BLOCK, &theSet, &oldSet); puts("Executing critical region..."); sleep((argc > 1) ? (atoi(argv[1])) : 1); puts("Done critical region, waiting for alarm signal..."); sigfillset(&theSet); sigdelset(&theSet, SIGALRM); sigsuspend(&theSet); return 0; }

Επεκτάσεις extern char *sys_siglist[]; Πίνακας με ονόματα σημάτων (ή _sys_siglist) void psignal(int sig, const char *s); void psiginfo(siginfo_t *pinfo, char *s); Αντίστοιχες της perror για σήματα Πρόσθετο όρισμα στον χειριστή, αν έχει χρησιμοποιηθεί η ένδειξη SA_SIGINFO στη sigaction struct siginfo *info; όπου struct siginfo { int si_signo; /* Αριθ. Σήματος */ int si_errno; /* αν != 0, τιμή errno */ int si_code; /* Πρόσθετες πληροφορίες ανάλογα με το σήμα */ pid_t si_pid; /* Ταυτότητα αποστέλλουσας διεργασίας */ uid_t si_uid; /* Ταυτότητα χρήστη αποστέλλουσας διεργασίας */ Παράδειγμα si_code: Αν si_signo == SIGFPE, FPE_INTDIV  ακέραια διαίρεση με 0, FPE_FLTOVF = υπερχείλιση σε πράξη κινητής υποδιαστολής κ.ο.κ.