Как да използвам манипулатори на сигнали на език C?

How Use Signal Handlers C Language



В тази статия ще ви покажем как да използвате манипулатори на сигнали в Linux, използвайки език C. Но първо ще обсъдим какво е сигнал, как той ще генерира някои общи сигнали, които можете да използвате във вашата програма, а след това ще разгледаме как различните сигнали могат да бъдат обработени от програма, докато програмата се изпълнява. И така, нека започнем.

Сигнал

Сигналът е събитие, което се генерира, за да уведоми процес или нишка, че е настъпила някаква важна ситуация. Когато процес или нишка е получил сигнал, процесът или нишката ще спрат това, което правят и ще предприемат някои действия. Сигналът може да бъде полезен за междупроцесна комуникация.







Стандартни сигнали

Сигналите са дефинирани в заглавния файл сигнал.ч като макроконстанта. Името на сигнала е започнало със SIG и е последвано от кратко описание на сигнала. Така че всеки сигнал има уникална цифрова стойност. Вашата програма винаги трябва да използва името на сигналите, а не номера на сигналите. Причината е, че номерът на сигнала може да се различава в зависимост от системата, но значението на имената ще бъде стандартно.



Макросът NSIG е общият брой на дефинирания сигнал. Стойността на NSIG е с един по -голям от общия брой на дефинирания сигнал (Всички номера на сигнала се разпределят последователно).



Следните са стандартните сигнали:





Име на сигнала Описание
ЗАБЕЛЕЖКА Закачете процеса. Сигналът SIGHUP се използва за съобщаване за прекъсване на терминала на потребителя, вероятно защото отдалечената връзка се губи или затваря.
ПОДПИСАНЕ Прекъснете процеса. Когато потребителят въведе символа INTR (обикновено Ctrl + C), сигналът SIGINT се изпраща.
SIGQUIT Излезте от процеса. Когато потребителят въведе символа QUIT (обикновено Ctrl + ), сигналът SIGQUIT се изпраща.
ТЮЛЕН Незаконно наставление. Когато се прави опит за изпълнение на боклук или привилегирована инструкция, се генерира сигналът SIGILL. Също така, SIGILL може да се генерира, когато стекът препълни, или когато системата има проблеми с изпълнението на манипулатор на сигнал.
SIGTRAP Капан за проследяване. Инструкция за точка на прекъсване и друга инструкция за улавяне ще генерират сигнала SIGTRAP. Дебъгерът използва този сигнал.
SIGABRT Прекъсване. Сигналът SIGABRT се генерира при извикване на функцията abort (). Този сигнал показва грешка, която е открита от самата програма и докладвана от извикването на функцията abort ().
SIGFPE Изключение с плаваща запетая. При възникване на фатална аритметична грешка се генерира сигнал SIGFPE.
SIGUSR1 и SIGUSR2 Сигналите SIGUSR1 и SIGUSR2 могат да се използват по ваше желание. Полезно е да им напишете манипулатор на сигнал в програмата, която приема сигнала за проста комуникация между процесите.

Действие на сигналите по подразбиране

Всеки сигнал има действие по подразбиране, едно от следните:

Срок: Процесът ще приключи.
Ядро: Процесът ще прекрати и ще произведе основен дамп файл.
Запалване: Процесът ще игнорира сигнала.
Спри се: Процесът ще спре.
Сметка: Процесът ще продължи от спиране.



Действието по подразбиране може да бъде променено с помощта на функция за обработка. Действието по подразбиране на някои сигнали не може да бъде променено. SIGKILL и SIGABRT Действието по подразбиране на сигнала не може да бъде променено или игнорирано.

Обработка на сигнали

Ако процесът получава сигнал, процесът има избор на действие за този вид сигнал. Процесът може да игнорира сигнала, може да посочи функция манипулатор или да приеме действието по подразбиране за този вид сигнал.

  • Ако посоченото действие за сигнала се игнорира, тогава сигналът се изхвърля незабавно.
  • Програмата може да регистрира функция за обработка, като използва функция като сигнал или сигакция . Това се нарича манипулатор улавя сигнала.
  • Ако сигналът не е нито обработен, нито игнориран, се извършва действието му по подразбиране.

Можем да обработим сигнала с помощта сигнал или сигакция функция. Тук виждаме как най -простият сигнал () функцията се използва за обработка на сигнали.

intсигнал() (intзнак, невалиден (*функция)(int))

The сигнал () ще се обади на функция функция, ако процесът получи сигнал знак . The сигнал () връща показалец към функция функция ако е успешен или връща грешка на errno и -1 в противен случай.

The функция показалецът може да има три стойности:

  1. SIG_DFL : Това е указател към функция по подразбиране на системата SIG_DFL () , деклариран в з заглавен файл. Използва се за предприемане на действие по подразбиране на сигнала.
  2. SIG_IGN : Това е указател към функция за игнориране на системата SIG_IGN () , деклариран в з заглавен файл.
  3. Потребителски указател на функция на манипулатор : Потребителският тип функция на манипулатора е void (*) (int) , означава, че типът на връщане е невалиден и един аргумент от тип int.

Пример за основен манипулатор на сигнали

#включва
#включва
#включва
невалиденsig_handler(intзнак){

// Типът на връщане на манипулаторната функция трябва да бъде невалиден
printf ('нВътрешна функция на манипулаторан');
}

intглавен(){
сигнал(ПОДПИСАНЕ,sig_handler); // Регистрирайте манипулатора на сигнали
за(inti=1;;i++){ //Безкраен цикъл
printf ('%d: Вътрешна основна функциян',i);
сън(1); // Забавяне за 1 секунда
}
връщане 0;
}

На екранната снимка на изхода на Пример1.в, можем да видим, че в основната функция се изпълнява безкраен цикъл. Когато потребителят въведе Ctrl+C, изпълнението на основната функция спира и функцията манипулатор на сигнала се извиква. След завършване на манипулаторната функция изпълнението на основната функция се възобновява. Когато потребителят въведе Ctrl+, процесът се прекратява.

Пример за игнориране на сигнали

#включва
#включва
#включва
intглавен(){
сигнал(ПОДПИСАНЕ,SIG_IGN); // Регистрирайте манипулатора на сигнал за игнориране на сигнала

за(inti=1;;i++){ //Безкраен цикъл
printf ('%d: Вътрешна основна функциян',i);
сън(1); // Забавяне за 1 секунда
}
връщане 0;
}

Тук функцията манипулатор се регистрира в SIG_IGN () функция за игнориране на действието на сигнала. Така че, когато потребителят въведе Ctrl+C, ПОДПИСАНЕ сигналът се генерира, но действието се игнорира.

Пререгистрирайте Примера за обработка на сигнали

#включва
#включва
#включва

невалиденsig_handler(intзнак){
printf ('нВътрешна функция на манипулаторан');
сигнал(ПОДПИСАНЕ,SIG_DFL); // Пререгистрирайте манипулатора на сигнала за действие по подразбиране
}

intглавен(){
сигнал(ПОДПИСАНЕ,sig_handler); // Регистрирайте манипулатора на сигнали
за(inti=1;;i++){ //Безкраен цикъл
printf ('%d: Вътрешна основна функциян',i);
сън(1); // Забавяне за 1 секунда
}
връщане 0;
}

На екранната снимка на изхода на Example3.c можем да видим, че когато потребителят за първи път въведе Ctrl+C, функцията манипулатор се извиква. Във функцията манипулатор манипулаторът на сигнали се регистрира отново в SIG_DFL за действие по подразбиране на сигнала. Когато потребителят въведе Ctrl+C за втори път, процесът се прекратява, което е действието по подразбиране на ПОДПИСАНЕ сигнал.

Изпращане на сигнали:

Процесът също може изрично да изпраща сигнали към себе си или към друг процес. Функцията raise () и kill () могат да се използват за изпращане на сигнали. И двете функции са декларирани в заглавен файл signal.h.

int повишаване (intзнак)

Функцията raise (), използвана за изпращане на сигнал знак към извикващия процес (себе си). Връща нула, ако е успешна, и ненулева стойност, ако се провали.

intубивам(pid_t pid, intзнак)

Функцията за убиване, използвана за изпращане на сигнал знак към процес или група от процеси, посочени от пид .

Пример за обработка на сигнали SIGUSR1

#включва
#включва

невалиденsig_handler(intзнак){
printf („Вътрешна функция на манипулаторан');
}

intглавен(){
сигнал(SIGUSR1,sig_handler); // Регистрирайте манипулатора на сигнали
printf („Вътрешна основна функциян');
повишаване (SIGUSR1);
printf („Вътрешна основна функциян');
връщане 0;
}

Тук процесът изпраща сигнал SIGUSR1 към себе си, като използва функцията raise ().

Повишаване с Примерна програма за убиване

#включва
#включва
#включва
невалиденsig_handler(intзнак){
printf („Вътрешна функция на манипулаторан');
}

intглавен(){
pid_t pid;
сигнал(SIGUSR1,sig_handler); // Регистрирайте манипулатора на сигнали
printf („Вътрешна основна функциян');
пид=избухвам(); // Идентификационен номер на процеса
убивам(пид,SIGUSR1); // Изпратете SIGUSR1 към себе си
printf („Вътрешна основна функциян');
връщане 0;
}

Тук процесът изпраща SIGUSR1 сигнал към себе си, използвайки убий () функция. getpid () се използва за получаване на идентификационния номер на процеса.

В следващия пример ще видим как родителските и дъщерните процеси комуникират (междупроцесна комуникация), използвайки убий () и сигнална функция.

Комуникация на родителско дете със сигнали

#включва
#включва
#включва
#включва
невалиденsig_handler_parent(intзнак){
printf („Родител: Получен сигнал за отговор от детен');
}

невалиденsig_handler_child(intзнак){
printf („Дете: Получен сигнал от родителн');
сън(1);
убивам(getppid(),SIGUSR1);
}

intглавен(){
pid_t pid;
ако((пид=вилица())<0){
printf („Вилицата е неуспешнан');
изход (1);
}
/ * Детски процес */
иначе ако(пид==0){
сигнал(SIGUSR1,sig_handler_child); // Регистрирайте манипулатора на сигнали
printf („Дете: чака сигналн');
пауза();
}
/ * Родителски процес */
иначе{
сигнал(SIGUSR1,sig_handler_parent); // Регистрирайте манипулатора на сигнали
сън(1);
printf („Родител: изпращане на сигнал до детен');
убивам(пид,SIGUSR1);
printf („Родител: чака отговорн');
пауза();
}
връщане 0;
}

Тук, вилица () функцията създава дъщерния процес и връща нула на дъщерния процес и идентификатора на дъщерния процес на родителския процес. И така, pid е проверен, за да реши процеса родител и дете. В родителския процес той спи за 1 секунда, така че дочерният процес може да регистрира функцията за обработка на сигнала и да изчака сигнала от родителя. След 1 секунда родителски процес изпраща SIGUSR1 сигнал към дете процес и изчакайте сигнала за отговор от детето. В дъщерния процес първо се чака сигнал от родителя и когато се получи сигнал, се извиква функция манипулатор. От функцията манипулатор дъщерният процес изпраща друг SIGUSR1 сигнал към родителя. Тук getppid () функцията се използва за получаване на идентификатор на родителски процес.

Заключение

Signal в Linux е голяма тема. В тази статия видяхме как да боравим със сигнала от самото основно, а също така научихме как генерира сигнала, как процес може да изпраща сигнал към себе си и друг процес, как сигналът може да се използва за междупроцесна комуникация.