Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
PR_СП_лекции_укр.doc
Скачиваний:
6
Добавлен:
22.04.2019
Размер:
697.34 Кб
Скачать

3.5.2 Розробка оброблювачів сигналів

Існують дві базові стратегії, використовувані при розробці функцій-оброблювачів:

  1. при надходженні сигналу функція виконує деяку дію й повертає керування в програму;

  2. функція перериває виконання програми або виправляє ситуацію, що виникла в результаті помилки.

Варто звернути особливу увагу на те, що оброблювач може бути викликаний асинхронно, з будь-якої крапки програми. При дуже короткому інтервалі часу видачі сигналу два й більше процеси оброблювачів можуть виконуватися одночасно.

Оброблювачі першого типу використовуються для сигналів 'SIGALRM', сигналів, що надходять від пристроїв вводу-виводу. Оброблювач повинен змінювати деяку глобальну змінну в знак того, що оброблювач одержав керування. Тип даних цієї змінної повинен бути sig_atomic_t.

Приклад оброблювача сигналу:

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

volatile sig_atomic_t keep_going = 1;

void catch_alarm (int sig)

{

keep_going = 0;

signal (sig, catch_alarm);

}

Void do_stuff (void)

{

puts ("Doing stuff while waiting for alarm....");

}

Int main (void)

{

signal (SIGALRM, catch_alarm);

alarm (2);

while (keep_going)

do_stuff ();

return EXIT_SUCCESS;

}

Оброблювачі другого типу звичайно використовуються для виконання деяких завершальних дій при критичних помилках. Найкращий шлях для припинення виконання процесу - викликати той же сигнал з певною дією за замовчуванням. Наприклад,

volatile sig_atomic_t fatal_error_in_progress = 0;

void fatal_error_signal (int sig)

{

if (fatal_error_in_progress)

raise (sig);

fatal_error_in_progress = 1;

...

signal (sig, SIG_DFL);

raise (sig);

}

Якщо запущено оброблювача для конкретного сигналу, то цей сигнал блокується доти, поки робота оброблювача не буде завершена. Проте, робота оброблювача може бути перервана приходом якого-небудь іншого сигналу. Для того, щоб уникнути цього потрібно використовувати поле sa_mask структури sigaction, щоб указати які сигнали будуть заблоковані в процесі виконання оброблювача.

При написанні оброблювачів сигналів варто прагнути до мінімізації його коду й скороченню часу виконання. Критичним місцем можуть стати структури даних, з якими працює оброблювач. Необхідно дотримуватися наступних принципів:

  • якщо оброблювачеві необхідно працювати із глобальної змінної, певної в програмі, то вона повинна бути визначена як volatile, що говорить компіляторові про можливість зміни її асинхронно;

  • якщо в оброблювачі викликається яка-небудь функція, переконаєтеся, що вона є повторно входимой або в тім, що сигнал не може перервати виконання цієї функції.

При написанні оброблювачів існує проблема звертання до неелементарним даних (об'єктам у пам'яті, доступ до яких може бути здійснений лише при виконанні декількох операцій з даними). У випадку надходження сигналу в момент модифікації вмісту об'єкта, його значення може стати непередбаченим. Наприклад,

#include <signal.h>

#include <stdio.h>

struct two_words { int a, b; } memory;

void handler(int signum)

{

printf ("%d,%d\n", memory.a, memory.b);

alarm (1);

}

Int main (void)

{

static struct two_words zeros = { 0, 0 }, ones = { 1, 1 };

signal (SIGALRM, handler);

memory = zeros;

alarm (1);

while (1)

{

memory = zeros;

memory = ones;

}

}

Існує два способи вирішити цю проблему:

  1. використання елементарного типу даних. Таким типом даних є sig_atomic_t. У загальному випадку, тип даних int і всі типи даних, розмірністю меншої, чим int є елементарними;

  2. обробка даних тільки в тілі програми. При цьому оброблювач повинен тільки модифікувати прапор, використовуваний для синхронізації програми й оброблювача.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]