Πώς να χρησιμοποιήσετε τους χειριστές σήματος στη γλώσσα C;

How Use Signal Handlers C Language



Σε αυτό το άρθρο θα σας δείξουμε πώς να χρησιμοποιείτε τους χειριστές σημάτων στο Linux χρησιμοποιώντας τη γλώσσα C. Αλλά πρώτα θα συζητήσουμε τι είναι σήμα, πώς θα δημιουργήσει κάποια κοινά σήματα που μπορείτε να χρησιμοποιήσετε στο πρόγραμμά σας και στη συνέχεια θα δούμε πώς μπορούν να χειριστούν διάφορα σήματα ένα πρόγραμμα ενώ εκτελείται το πρόγραμμα. Λοιπόν, ας ξεκινήσουμε.

Σήμα

Ένα σήμα είναι ένα γεγονός που δημιουργείται για να ειδοποιήσει μια διαδικασία ή ένα νήμα ότι έχει φτάσει κάποια σημαντική κατάσταση. Όταν μια διαδικασία ή νήμα έχει λάβει ένα σήμα, η διαδικασία ή το νήμα θα σταματήσει αυτό που κάνει και θα λάβει κάποια ενέργεια. Το σήμα μπορεί να είναι χρήσιμο για επικοινωνία μεταξύ διαδικασιών.







Τυπικά σήματα

Τα σήματα ορίζονται στο αρχείο κεφαλίδας σήμα.η ως μακρο σταθερά. Το όνομα σήματος ξεκινά με ένα SIG και ακολουθείται από μια σύντομη περιγραφή του σήματος. Έτσι, κάθε σήμα έχει μια μοναδική αριθμητική τιμή. Το πρόγραμμά σας πρέπει πάντα να χρησιμοποιεί το όνομα των σημάτων και όχι τον αριθμό των σημάτων. Ο λόγος είναι ότι ο αριθμός σήματος μπορεί να διαφέρει ανάλογα με το σύστημα, αλλά η έννοια των ονομάτων θα είναι τυπική.



Η μακροεντολή NSIG είναι ο συνολικός αριθμός καθορισμένου σήματος. Η αξία του NSIG είναι ένα μεγαλύτερο από το συνολικό αριθμό σήματος που ορίζεται (Όλοι οι αριθμοί σήματος κατανέμονται διαδοχικά).



Ακολουθούν τα τυπικά σήματα:





Όνομα σήματος Περιγραφή
ΟΡΑΜΑ Κλείστε τη διαδικασία. Το σήμα SIGHUP χρησιμοποιείται για την αναφορά αποσύνδεσης του τερματικού του χρήστη, πιθανώς επειδή έχει χαθεί ή κλείσει μια απομακρυσμένη σύνδεση.
ΕΓΓΡΑΦΗ Διακόψτε τη διαδικασία. Όταν ο χρήστης πληκτρολογεί τον χαρακτήρα INTR (συνήθως Ctrl + C) αποστέλλεται το σήμα SIGINT.
SIGQUIT Κλείστε τη διαδικασία. Όταν ο χρήστης πληκτρολογεί τον χαρακτήρα QUIT (συνήθως Ctrl + ), αποστέλλεται το σήμα SIGQUIT.
ΣΦΡΑΓΙΔΑ Παράνομη οδηγία. Όταν γίνεται προσπάθεια εκτέλεσης σκουπιδιών ή προνομιακών οδηγιών, δημιουργείται το σήμα SIGILL. Επίσης, το SIGILL μπορεί να δημιουργηθεί όταν η στοίβα υπερχειλίσει ή όταν το σύστημα έχει πρόβλημα να εκτελέσει ένα χειριστή σήματος.
SIGTRAP Trace trap. Μια εντολή διακοπής και άλλη εντολή παγίδας θα παράγουν το σήμα SIGTRAP. Ο εντοπιστής σφαλμάτων χρησιμοποιεί αυτό το σήμα.
SIGABRT Κάνω αποβολή. Το σήμα SIGABRT δημιουργείται όταν καλείται η συνάρτηση abort (). Αυτό το σήμα υποδεικνύει ένα σφάλμα που ανιχνεύεται από το ίδιο το πρόγραμμα και αναφέρεται από την κλήση συνάρτησης abort ().
SIGFPE Εξαίρεση κυμαινόμενου σημείου. Όταν συνέβη ένα μοιραίο αριθμητικό σφάλμα, δημιουργείται το σήμα SIGFPE.
SIGUSR1 και SIGUSR2 Τα σήματα SIGUSR1 και SIGUSR2 μπορούν να χρησιμοποιηθούν όπως θέλετε. Είναι χρήσιμο να γράψετε έναν χειριστή σήματος για αυτούς στο πρόγραμμα που λαμβάνει το σήμα για απλή επικοινωνία μεταξύ διεργασιών.

Προεπιλεγμένη ενέργεια σημάτων

Κάθε σήμα έχει μια προεπιλεγμένη ενέργεια, μία από τις ακόλουθες:

Ορος: Η διαδικασία θα τερματιστεί.
Πυρήνας: Η διαδικασία θα τερματιστεί και θα δημιουργηθεί ένα βασικό αρχείο χωματερή.
Ign: Η διαδικασία θα αγνοήσει το σήμα.
Να σταματήσει: Η διαδικασία θα σταματήσει.
Λογαριασμός: Η διαδικασία θα συνεχίσει να σταματά.



Η προεπιλεγμένη ενέργεια μπορεί να αλλάξει χρησιμοποιώντας τη λειτουργία χειριστή. Η προεπιλεγμένη ενέργεια κάποιου σήματος δεν μπορεί να αλλάξει. ΣΙΓΚΙΛ και SIGABRT η προεπιλεγμένη ενέργεια του σήματος δεν μπορεί να αλλάξει ή να αγνοηθεί.

Χειρισμός σήματος

Εάν μια διαδικασία λάβει ένα σήμα, η διαδικασία έχει μια επιλογή ενέργειας για αυτό το είδος σήματος. Η διαδικασία μπορεί να αγνοήσει το σήμα, να καθορίσει μια λειτουργία χειρισμού ή να αποδεχτεί την προεπιλεγμένη ενέργεια για αυτό το είδος σήματος.

  • Εάν αγνοηθεί η καθορισμένη ενέργεια για το σήμα, τότε το σήμα απορρίπτεται αμέσως.
  • Το πρόγραμμα μπορεί να καταχωρήσει μια λειτουργία χειρισμού χρησιμοποιώντας συνάρτηση όπως π.χ. σήμα ή αντιδράσεις Το Αυτό ονομάζεται χειριστής πιάνει το σήμα.
  • Εάν το σήμα δεν χειριστεί ούτε αγνοηθεί, λαμβάνει χώρα η προεπιλεγμένη του ενέργεια.

Μπορούμε να χειριστούμε το σήμα χρησιμοποιώντας σήμα ή αντιδράσεις λειτουργία. Εδώ βλέπουμε το πιο απλό σήμα() η λειτουργία χρησιμοποιείται για το χειρισμό σημάτων.

intσήμα() (intσημάδι, κενός (*λειτουργία)(int))

ο σήμα() θα καλέσει το λειτουργία λειτουργία εάν η διαδικασία λάβει σήμα σημάδι Το ο σήμα() επιστρέφει έναν δείκτη στη λειτουργία λειτουργία εάν είναι επιτυχής ή επιστρέφει σφάλμα στο errno και -1 αλλιώς.

ο λειτουργία ο δείκτης μπορεί να έχει τρεις τιμές:

  1. SIG_DFL : Είναι δείκτης της προεπιλεγμένης λειτουργίας του συστήματος SIG_DFL () , δηλώθηκε στο η αρχείο κεφαλίδας. Χρησιμοποιείται για τη λήψη της προεπιλεγμένης ενέργειας του σήματος.
  2. SIG_IGN : Είναι ένας δείκτης για τη λειτουργία παράβλεψης συστήματος SIG_IGN () , δηλώθηκε στο η αρχείο κεφαλίδας.
  3. Δείκτης λειτουργίας που καθορίζεται από το χρήστη : Ο τύπος συνάρτησης χειριστή που ορίζεται από τον χρήστη είναι κενό (*) (int) , σημαίνει ότι ο τύπος επιστροφής είναι άκυρος και ένα όρισμα τύπου int.

Παράδειγμα χειρισμού βασικού σήματος

#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
κενόςsig_handler(intσημάδι){

// Ο τύπος επιστροφής της συνάρτησης χειριστή πρέπει να είναι άκυρος
printf (' nΛειτουργία εσωτερικού χειριστή n')?
}

intκύριος(){
σήμα(ΕΓΓΡΑΦΗ,sig_handler)? // Εγγραφή χειριστή σήματος
Για(intΕγώ=1;;Εγώ++){ // Άπειρος βρόχος
printf ('%d: Μέσα στην κύρια λειτουργία n',Εγώ)?
ύπνος(1)? // Καθυστέρηση για 1 δευτερόλεπτο
}
ΕΠΙΣΤΡΟΦΗ 0?
}

Στο στιγμιότυπο οθόνης της εξόδου του Παραδείγματος1.γ, μπορούμε να δούμε ότι στην κύρια λειτουργία εκτελείται άπειρος βρόχος. Όταν ο χρήστης πληκτρολογεί Ctrl+C, η κύρια λειτουργία σταματά και η λειτουργία χειρισμού του σήματος καλείται. Μετά την ολοκλήρωση της λειτουργίας χειριστή, η εκτέλεση της κύριας λειτουργίας συνεχίστηκε. Όταν ο χρήστης πληκτρολογήσει Ctrl+, η διαδικασία τερματίζεται.

Παράβλεψη παραδειγμάτων σημάτων

#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
intκύριος(){
σήμα(ΕΓΓΡΑΦΗ,SIG_IGN)? // Εγγραφή χειριστή σήματος για παράβλεψη του σήματος

Για(intΕγώ=1;;Εγώ++){ // Άπειρος βρόχος
printf ('%d: Μέσα στην κύρια λειτουργία n',Εγώ)?
ύπνος(1)? // Καθυστέρηση για 1 δευτερόλεπτο
}
ΕΠΙΣΤΡΟΦΗ 0?
}

Εδώ η λειτουργία χειριστή είναι εγγραφή σε SIG_IGN () λειτουργία για την παράβλεψη της ενέργειας σήματος. Έτσι, όταν ο χρήστης πληκτρολόγησε Ctrl+C, ΕΓΓΡΑΦΗ παράγει σήμα αλλά η ενέργεια αγνοείται.

Παράδειγμα χειριστή εγγραφής σήματος

#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω

κενόςsig_handler(intσημάδι){
printf (' nΛειτουργία εσωτερικού χειριστή n')?
σήμα(ΕΓΓΡΑΦΗ,SIG_DFL)? // Re Εγγραφή χειριστή σήματος για προεπιλεγμένη ενέργεια
}

intκύριος(){
σήμα(ΕΓΓΡΑΦΗ,sig_handler)? // Εγγραφή χειριστή σήματος
Για(intΕγώ=1;;Εγώ++){ // Άπειρος βρόχος
printf ('%d: Μέσα στην κύρια λειτουργία n',Εγώ)?
ύπνος(1)? // Καθυστέρηση για 1 δευτερόλεπτο
}
ΕΠΙΣΤΡΟΦΗ 0?
}

Στο στιγμιότυπο οθόνης της εξόδου του Παραδείγματος3.γ, μπορούμε να δούμε ότι όταν ο χρήστης πληκτρολόγησε για πρώτη φορά Ctrl+C, η λειτουργία χειρισμού κλήθηκε. Στη λειτουργία χειριστή, ο χειριστής σήματος καταχωρείται εκ νέου σε SIG_DFL για προεπιλεγμένη ενέργεια του σήματος. Όταν ο χρήστης πληκτρολογεί Ctrl+C για δεύτερη φορά, η διαδικασία τερματίζεται, η οποία είναι η προεπιλεγμένη ενέργεια ΕΓΓΡΑΦΗ σήμα.

Αποστολή σημάτων:

Μια διαδικασία μπορεί επίσης να στέλνει ρητά σήματα στον εαυτό της ή σε άλλη διαδικασία. η λειτουργία αύξησης () και kill () μπορεί να χρησιμοποιηθεί για την αποστολή σημάτων. Και οι δύο συναρτήσεις δηλώνονται στο αρχείο κεφαλίδας signal.h.

int υψώνω (intσημάδι)

Η λειτουργία αύξησης () χρησιμοποιείται για την αποστολή σήματος σημάδι στη διαδικασία κλήσης (η ίδια). Επιστρέφει μηδέν εάν είναι επιτυχές και μη μηδενική τιμή αν αποτύχει.

intσκοτώνω(pid_t pid, intσημάδι)

Η λειτουργία θανάτωσης που χρησιμοποιείται για την αποστολή σήματος σημάδι σε μια διαδικασία ή ομάδα διαδικασίας που καθορίζεται από pid Το

Παράδειγμα χειριστή σημάτων SIGUSR1

#περιλαμβάνω
#περιλαμβάνω

κενόςsig_handler(intσημάδι){
printf («Λειτουργία εσωτερικού χειριστή n')?
}

intκύριος(){
σήμα(SIGUSR1,sig_handler)? // Εγγραφή χειριστή σήματος
printf («Μέσα στην κύρια λειτουργία n')?
υψώνω (SIGUSR1)?
printf («Μέσα στην κύρια λειτουργία n')?
ΕΠΙΣΤΡΟΦΗ 0?
}

Εδώ, η διαδικασία στέλνει σήμα SIGUSR1 στον εαυτό της χρησιμοποιώντας τη συνάρτηση raise ().

Πρόγραμμα αύξησης με παράδειγμα δολοφονίας

#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
κενόςsig_handler(intσημάδι){
printf («Λειτουργία εσωτερικού χειριστή n')?
}

intκύριος(){
pid_t pid?
σήμα(SIGUSR1,sig_handler)? // Εγγραφή χειριστή σήματος
printf («Μέσα στην κύρια λειτουργία n')?
pid=χάλια()? // Αναγνωριστικό διαδικασίας από μόνο του
σκοτώνω(pid,SIGUSR1)? // Αποστολή του SIGUSR1 στον εαυτό του
printf («Μέσα στην κύρια λειτουργία n')?
ΕΠΙΣΤΡΟΦΗ 0?
}

Εδώ, η διαδικασία αποστολής SIGUSR1 σήμα στον εαυτό του χρησιμοποιώντας σκοτώνω() λειτουργία. getpid () χρησιμοποιείται για να λάβει το ίδιο το αναγνωριστικό διεργασίας.

Στο επόμενο παράδειγμα θα δούμε πώς επικοινωνούν οι διαδικασίες γονέων και παιδιών (Inter Process Communication) σκοτώνω() και λειτουργία σήματος.

Επικοινωνία γονιού παιδιού με σήματα

#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
κενόςsig_handler_parent(intσημάδι){
printf («Γονέας: Έλαβε ένα σήμα απόκρισης από το παιδί n')?
}

κενόςsig_handler_child(intσημάδι){
printf («Παιδί: Έλαβε ένα σήμα από τον γονέα n')?
ύπνος(1)?
σκοτώνω(χορταίνω(),SIGUSR1)?
}

intκύριος(){
pid_t pid?
αν((pid=πιρούνι())<0){
printf ('Το πιρούνι απέτυχε n')?
έξοδος (1)?
}
/ * Διαδικασία για παιδιά */
αλλού αν(pid==0){
σήμα(SIGUSR1,sig_handler_child)? // Εγγραφή χειριστή σήματος
printf («Παιδί: περιμένει σήμα n')?
παύση()?
}
/ * Γονική διαδικασία */
αλλού{
σήμα(SIGUSR1,sig_handler_parent)? // Εγγραφή χειριστή σήματος
ύπνος(1)?
printf («Γονέας: στέλνοντας σήμα στο Παιδί n')?
σκοτώνω(pid,SIGUSR1)?
printf («Γονέας: περιμένω απάντηση n')?
παύση()?
}
ΕΠΙΣΤΡΟΦΗ 0?
}

Εδώ, πιρούνι() η συνάρτηση δημιουργεί διαδικασία παιδιών και επιστρέφει το μηδέν στη διαδικασία παιδιού και το αναγνωριστικό διαδικασίας παιδιού στη διαδικασία γονέα. Έτσι, το pid έχει ελεγχθεί για να αποφασίσει τη διαδικασία γονέα και παιδιού. Στη διαδικασία γονέα, κοιμάται για 1 δευτερόλεπτο, έτσι ώστε η διαδικασία παιδιού να καταχωρήσει τη λειτουργία χειρισμού σήματος και να περιμένει το σήμα από τον γονέα. Μετά από 1 δευτερόλεπτο διαδικασία αποστολής γονέα SIGUSR1 σήμα στο παιδί και να περιμένει το σήμα απόκρισης από το παιδί. Στη διαδικασία για παιδιά, πρώτα περιμένει σήμα από τον γονέα και όταν ληφθεί το σήμα, καλείται η λειτουργία χειριστή. Από τη λειτουργία χειριστή, η θυγατρική διαδικασία στέλνει μια άλλη SIGUSR1 σήμα προς τον γονέα. Εδώ getppid () Η συνάρτηση χρησιμοποιείται για τη λήψη αναγνωριστικού διεργασίας γονέα.

συμπέρασμα

Το σήμα στο Linux είναι ένα μεγάλο θέμα. Σε αυτό το άρθρο έχουμε δει πώς να χειρίζεστε το σήμα από τα πολύ βασικά και επίσης να γνωρίζετε πώς δημιουργείται το σήμα, πώς μια διαδικασία μπορεί να στείλει σήμα στον εαυτό της και άλλη διαδικασία, πώς μπορεί να χρησιμοποιηθεί το σήμα για επικοινωνία μεταξύ διεργασιών.