- •Анотація
- •1 Середовище програмування ос unix
- •1.1 Структура ос unix
- •1.2 Створення програми
- •1. Препроцесор
- •2. Транслятор
- •2.1 Компілятор
- •2.2 Асемблер
- •3. Редактор зв'язків(Компонувальником)
- •1.3 Формати об'єктних файлів
- •2 Використання файлової системи unix
- •2.1 Основні поняття
- •2.1.1 Типи та іменування файлів
- •2.1.2 Права доступу до файлів
- •2.1.3 Cистемные структури керування файлами
- •2.1.4 Системні виклики роботи з файлами
- •2.2 Створення та відкриття файлів
- •2.2.1 Системний виклик open
- •2.2.2 Системний виклик create
- •2.2.3 Системний виклик close
- •2.3 Читання та зипис в файл
- •2.3.1 Системний виклик read
- •2.3.2 Системний виклик write
- •2.4 Позіціонування у файлі
- •2.4.1 Системний виклик lseek
- •2.4.2 Системні виклики pread і pwrite
- •2.4.3 Системні виклики readv і writev
- •2.5 Контрольні операції над файлами
- •2.5.1 Системні виклики stat, fstat і lstat
- •2.5.2 Контроль прав доступу до файлу
- •2.5.3 Функція fcntl
- •2.5.4 Системні виклики truncate і ftruncate
- •2.6 Управління каталогами
- •2.6.1 Створення і видалення
- •2.6.2 Читання каталога
- •2.6.3 Зміна каталога
- •2.7 Управління посиланнями
- •2.7.1 Управління жорсткими посиланнями
- •2.7.2 Управління символічними посиланнями
- •2.7.3 Перейменування файлів і каталогів
- •3 Керування процесами ос unix
- •3.1 Процеси ос unix
- •3.2 Сисвизови створення процесів
- •3.3 Завершення процесу
- •3.4 Взаємодія процесів в ос unix
- •3.5 Сигнали
- •3.5.1 Поняття сигналу
- •3.5.2 Розробка оброблювачів сигналів
- •3.5.3 Генерація сигналів
- •3.5.4 Посилка сигналів іншим процесам
- •3.5.5 Блокування сигналів
- •3.5.6 Очікування сигналу
- •3.6 Трасування процесу.
- •3.7 Канали й fifo-Файли
- •3.7.1 Поняття
- •3.7.2 Створення каналу
- •3.7.3 Створення fifo-Файлів
- •3.8 Семафори
- •3.9 Повідомлення.
- •3.10 Поділювана пам'ять.
- •Література
- •Системне програмування Навчальний посібник
- •65082, Одеса, вул. Дворянська, 1/3
3.5.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;
}
}
Існує два способи вирішити цю проблему:
використання елементарного типу даних. Таким типом даних є sig_atomic_t. У загальному випадку, тип даних int і всі типи даних, розмірністю меншої, чим int є елементарними;
обробка даних тільки в тілі програми. При цьому оброблювач повинен тільки модифікувати прапор, використовуваний для синхронізації програми й оброблювача.