QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович
Традиционная обработка сигнала
В этой части изложения мы рассмотрим традиционные модели перехвата сигналов и установки для них собственных обработчиков (в том числе и игнорирование или восстановление стандартной обработки по умолчанию). Термин «традиционный» здесь означает, что мы бегло рассмотрим обработку сигналов применительно к процессам и стандартным сигналам UNIX (не сигналам реального времени), то есть в том изложении, как она традиционно рассматривается в литературе по UNIX (и здесь сигнал воспринимается, конечно же, единственным потоком приложения, а не процессом, но в этом случае различие не принципиально). Позже мы рассмотрим модель обработки сигналов реального времени и расширим ее на многопоточные приложения.
«Старая» модель обработки сигнала
В ранних версиях UNIX была принята единственная модель обработки сигналов, основанная на функции signal(), которая подразумевает семантику так называемых «ненадежных сигналов», принятую в этих ОС. Позже эта модель была подвержена радикальной критике, вскрывшей ее «ненадежность». Данная модель сохранена для совместимости с ранее разработанным программным обеспечением. Она обладает существенными недостатками, основными из которых являются:
• процесс не может заблокировать сигнал, то есть отложить получение сигнала на период выполнения критических участков кода;
• каждый раз при получении сигнала его диспозиция устанавливается на действие по умолчанию, и при необходимости продолжить обработку поступающих сигналов требуется повторно восстанавливать требуемый обработчик.
Вот пример ( файл s2.cc) использования этой модели в коде, который уже стал иллюстративным образцом и кочует из одного источника в другой:
Ненадежная модель реакции на сигнал#include <iostream.h>
#include <signal.h>
#include <unistd.h>
// обработчик сигнала SIGINT
static void handler(int signo) {
// восстановить обработчик:
signal(SIGINT, handler);
cout << "Получен сигнал SYSINT" << endl;
}
int main() {
// устанавливаются диспозиции сигналов:
signal(SIGINT, handler);
signal(SIGSEGV, SIG_DFL);
signal(SIGTERM, SIG_IGN);
while(true) pause();
}
ПримечаниеМакросы SIG_DFLи SIG_IGNопределяются так:
#define SIG_ERR (( void(*)(_SIG_ARGS))-1 )
#define SIG_DFL (( void(*)(_SIG_ARGS))0)
#define SIG_IGN (( void(*)(_SIG_ARGS))1)
#define SIG_HOLD (( void(*)(_SIG_ARGS))2)
где _SIG_ARGS— это фактически тип int. SIG_DFLи SIG_IGNустанавливают диспозиции сигнала «по умолчанию» и «игнорировать» соответственно, а о SIG_HOLDмы будем отдельно говорить позже.
Выполнение этой программы вам будет не так просто прекратить: на комбинацию завершения [Ctrl+C] она отвечает сообщением о получении сигнала... и все. Воспользуемся для этого посылкой программе опять же сигнала, но из другого процесса (другого экземпляра командного интерпретатора). Смотрим PID запущенного процесса:
# pidin
...
220//86 1 /s2 10 r STOPPED
...
И посылаем процессу сигнал завершения:
# kill -9 2207786или kill -SIGKILL 2207786
Таким же образом, как показано командой kill, мы будем посылать сигналы процессам «извне» и в описываемых далее тестах, не останавливаясь подробно, как это происходит, в том числе и для сигналов реального времени (41…56).
Предыдущий пример можно переписать ( файл s4.cc) для обеспечения часто требуемой на практике защиты от немедленного прерывания выполнения по [Ctrl+C], чтобы дать программе возможность выполнить все требуемые операции по завершению (сбросить буферы данных на диск, закрыть файлы, сокеты и другие используемые объекты):
#include <stdlib.h>
#include <iostream.h>
#include <signal.h>
#include <unistd.h>
static void handler(int signo) {
cout << "Saving data ... wait.r" << flush;
sleep(2); // здесь выполняются все завершающие действия!
cout << " " << flush;
exit(EXIT_SUCCESS);
}
int main() {
signal(SIGINT, handler);
signal(SIGSEGV, SIG_DFL);
signal(SIGTERM, SIG_IGN);
while (true) pause();
}
Оператор ожидания pause()при поступлении сигналов завершается с возвратом -1, а переменная errnoустанавливается в EINTR. Этот оператор дает нам еще один способ (файл s3.cc) неявного (без явной установки обработчиков) использования сигналов:
#include <stream.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
alarm(5);
cout << "Waiting to die in 5 seconds ..." << endl;
pause();
return EXIT_SUCCESS;
Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович, относящееся к жанру Интернет. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.


