Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

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

Παρόμοιες παρουσιάσεις


Παρουσίαση με θέμα: "Σήματα και χειρισμός σημάτων"— Μεταγράφημα παρουσίασης:

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

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

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

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

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

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

7 Η συνάρτηση 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 αν προέκυψε σφάλμα, αλλιώς η προηγούμενη ρύθμιση για χειρισμό του σήματος

8 Η συνάρτηση 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;

9 Εκκίνηση μιας διεργασίας
Με την εκτέλεση της 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); }

10 Διευθέτηση ανάλογα με την τρέχουσα ρύθμιση
Αν το σήμα 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)

11 Μη αξιόπιστα σήματα Στις πρώτες εκδόσεις του 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();

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

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

14 Διακοπή κλήσεων συστήματος
Η πρακτική της διακοπής των κλήσεων συστήματος επιφορτίζει την εφαρμογή με την αναγκαιότητα του ελέγχου για τέτοιες περιπτώσεις: 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 επιτρέπεται στην εφαρμογή να ορίζει αν θέλει να διακόπτονται οι κλήσεις συστήματος από τα σήματα Ο ορισμός γίνεται ανά σήμα

15 Συναρτήσεις αποστολής σημάτων
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); Αναστέλλει τη διεργασία μέχρι το επόμενο σήμα

16 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);

17 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;

18 Συναρτήσεις αποστολής σημάτων
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

19 Σήματα και δυνατότητα επανεισόδου σε κώδικα [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")); } }

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

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

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

23 Σύνολα σημάτων Οι συναρτήσεις αξιόπιστης διαχείρισης σημάτων χρησιμοποιούν σύνολα σημάτων Ειδικός τύπος 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 αν ψευδές

24 Αναστολή παράδοσης σημάτων
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

25 Παράδειγμα [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; }

26 Εξετάζοντας τα εκκρεμή σήματα
Μπορούμε να εξετάσουμε ποια σήματα εκκρεμούν για παράδοση μέσω της 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; }

27 Ορισμός χειριστών σημάτων
Η συνάρτηση 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: ενδείξεις για τον χειρισμό του σήματος (επόμενη διαφάνεια)

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

29 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;

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

31 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;

32 Άρση αναστολής και αναμονή για σήμα
Δείτε το ακόλουθο πρόγραμμα [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 δεν τερματίζει

33 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; }

34 Επεκτάσεις 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 = υπερχείλιση σε πράξη κινητής υποδιαστολής κ.ο.κ.


Κατέβασμα ppt "Σήματα και χειρισμός σημάτων"

Παρόμοιες παρουσιάσεις


Διαφημίσεις Google