7.3.6 Сигналы реального времени |
Предыдущая Содержание Следующая |
Сигналы расширения POSIX 1003.1b играют очень важную роль в приложениях реального времени. Они используются для уведомления процессов о возникновении асинхронных событий, таких как завершение работы таймера высокого разрешения, завершение асинхронного ввода/вывода, приём сообщения в пустой очереди сообщений POSIX, и так далее. Некоторые преимущества, которыми обладают сигналы реального времени по отношению к нативным сигналам, перечислены в Таблице 7.7. Эти преимущества делают их пригодными для приложений реального времени. Сигнальные интерфейсы реального времени POSIX.1b перечислены в Таблице 7.8.
Таблица 7.7 Сравнение сигналов реального времени и нативных сигналов
Таблица 7.8 Функции сигналов реального времени POSIX.1b
Объясним вышеописанные интерфейсы на примере. В этом примере родительский процесс посылает сигналы реального времени дочернему процессу, а затем они обрабатываются. Пример разделён на две части, как показано на Рисунке 7.4.
Рисунок 7.4 Сигналы реального времени: пример приложения.
Основное приложение
#include <signal.h> #include <sys/types.h> #include <unistd.h> #include <errno.h>
int child(void); int parent(pid_t); /* Обработчик сигнала */ void rt_handler(int signum, siginfo_t *siginfo, void * extra); int main(){ pid_t cpid; if ((cpid = fork()) == 0) child(); else parent(cpid); }
Родительский процесс
Родительский процесс использует для отправки дочернему процессу SIGRTMIN и SIGRTMIN + 1 функцию sigqueue. Последний аргумент функции используется, чтобы передавать вместе с сигналом дополнительную информацию.
int parent(pid_t cpid){ union sigval value;
/* -------- НАЧАЛО 1-ой ЧАСТИ ----------- */
/* Спим, пока запускается дочерний процесс */ sleep(3); /* Дополнительная информация для SIGRTMIN */ value.sival_int = 1; /* Отправка дочернему процессу SIGRTMIN */ sigqueue(cpid, SIGRTMIN, value); /* Отправка дочернему процессу SIGRTMIN+1 */ sleep(3); value.sival_int = 2; sigqueue(cpid, SIGRTMIN+1, value);
/* -------- НАЧАЛО 2-ой ЧАСТИ ----------- */
/* Наконец, посылаем SIGRTMIN ещё раз */ sleep(3); value.sival_int = 3; sigqueue(cpid, SIGRTMIN, value);
/* --------- КОНЕЦ 2-ой ЧАСТИ ----------- */ }
Дочерний процесс
int child(void){ sigset_t mask, oldmask; siginfo_t siginfo; struct sigaction action; struct timespec tv; int count = 0, recv_sig;
Для хранения сигналов, которые необходимо блокировать, мы определяем маску типа sigset_t. Прежде чем продолжить, очищаем маску, вызывая sigemptyset. Эта функция инициализирует маску, чтобы исключить все сигналы. Затем вызывается sigaddset, чтобы добавить к заблокированному набору сигналы SIGRTMIN и SIGRTMIN + 1. Наконец, для блокировки доставки сигнала вызывается sigprocmask. (Мы блокируем эти сигналы в процессе их доставки с помощью функции ожидания сигнала, вместо того, чтобы использовать обработчик сигнала.)
/* -------- НАЧАЛО 1-ой ЧАСТИ ----------- */
/* Очищаем маску */ sigemptyset(&mask); /* Добавляем к маске SIGRTMIN */ sigaddset(&mask, SIGRTMIN); /* Добавляем к маске SIGRTMIN+1 */ sigaddset(&mask, SIGRTMIN+1);
/* * Блокируем доставку сигналов SIGRTMIN, SIGRTMIN+1. * После возвращения предыдущее значение * заблокированной сигнальной маски хранится в oldmask */ sigprocmask(SIG_BLOCK, &mask, &oldmask);
Теперь дочерний процесс ждёт доставки SIGRTMIN и SIGRTMIN + 1. Для ожидания блокированных сигналов используются функция sigwaitinfo или sigtimedwait. Набором сигналов для ожидания является первый аргумент. Второй аргумент заполняется любой дополнительной информацией, полученной вместе с сигналом (подробнее об siginfo_t позже). Последним аргументом для sigtimedwait является время ожидания.
/* Задаём время ожидания 1 с */ tv.tv_sec = 1; tv.tv_nsec = 0;
/* * Ждём доставки сигнала. Мы ожидаем 2 сигнала, * SIGRTMIN и SIGRTMIN+1. Цикл завершится, когда * будут приняты оба сигнала */ while(count < 2){ if ((recv_sig = sigtimedwait(&mask, &siginfo, &tv)) == -1){ if (errno == EAGAIN){ printf("Timed out\n"); continue; }else{ perror("sigtimedwait"); return -1; } }else{ printf("signal %d received\n", recv_sig); count++; } } /* --------- КОНЕЦ 1-ой ЧАСТИ ----------- */
Другим методом для обработки сигналов является регистрация обработчика сигналов. В этом случае процесс не должен блокировать сигнал. Зарегистрированный обработчик сигнала вызывается, когда сигнал доставляется процессу. Обработчик сигнала регистрирует функция sigaction. Это второй аргумент структуры sigaction, определённой как
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; }
Полями этой структуры являются:
▪sa_handler: функция регистрации обработчика сигналов для сигналов не реального времени. ▪sa_sigaction: функция регистрации обработчика сигналов для сигналов реального времени. ▪sa_mask: маска сигналов, которые должны быть заблокированы, когда выполняется обработчик сигналов. ▪sa_flags: для сигналов реального времени используется SA_SIGINFO. При получении сигнала, который имеет установленный SA_SIGINFO, вместо sa_handler вызывается функция sa_sigaction.
Теперь для обработки сигнала реального времени SIGRTMIN дочерний процесс регистрирует обработчик сигналов rt_handler. Эта функция будет вызываться, когда дочернему процессу доставляется SIGRTMIN.
/* -------- НАЧАЛО 2-ой ЧАСТИ ----------- */
/* Устанавливаем SA_SIGINFO */ action.sa_flags = SA_SIGINFO; /* Очищаем маску */ sigemptyset(&action.sa_mask);
/* * Регистрируем обработчик сигнала для SIGRTMIN. * Обратите внимание, что для регистрации * обработчика мы используем интерфейс * action.sa_sigaction */ action.sa_sigaction = rt_handler; if (sigaction(SIGRTMIN, &action, NULL) == -1){ perror("sigaction"); return 0; }
Затем дочерний процесс ждёт доставки сигнала. sigsuspend временно заменяет текущую маску сигнала маской, указанной в её аргументе. Затем она ожидает доставки разблокированного сигнала. После доставки сигнала sigsuspend восстанавливает исходную маску и возвращается после выполнения обработчика сигнала. Если обработчик сигнала вызывает прекращение процесса, sigsuspend не возвращается.
/* Wait from SIGRTMIN */ sigsuspend(&oldmask);
/* --------- КОНЕЦ 2-ой ЧАСТИ ----------- */ }
Таким образом, дочерний процесс успешно обработал сигналы, используя и функции ожидания сигнала, и обработчик сигналов. Наконец, обработчик сигнала:
/* Обработчик сигнала для SIGRTMIN */ void rt_handler(int signum, siginfo_t *siginfo, void * extra){ printf("signal %d received. code = %d, value = %d\n", siginfo->si_signo, siginfo->si_code, siginfo->si_int); }
Второй аргумент обработчика сигнала имеет тип siginfo_t и содержит всю информацию о полученном сигнале. Эта структура определяется так:
siginfo_t { int si_signo; // Номер сигнала int si_errno; // Ошибка сигнала int si_code; // Код сигнала union { ... // Сигналы POSIX .1b struct { pid_t pid; uid_t uid; sigval_t sigval; }_rt; ... }_sifields }
Чтобы узнать о всех полях этой структуры, обратитесь, пожалуйста, к /usr/include/bits/siginfo.h. Кроме si_signo, si_errno и si_code все остальные поля входят в объединение. Так что следует читать только те поля, которые имеют смысл в данном контексте. Например, поля, приведённые выше, действительны только для сигналов POSIX.1b.
▪si_signo является номером сигнала принятого сигнала. Это то же самое, что и первый аргумент обработчика сигналов. ▪si_code передаёт источник сигнала. Важные значения si_code для сигналов реального времени перечислены в Таблице 7.9. ▪si_value это дополнительная информация, передаваемая отправителем. ▪pid и uid являются идентификатором процесса и идентификатором пользователя отправителя, соответственно.
Таблица 7.9 Коды сигналов
Обращения к вышеописанным поля должны осуществляться с помощью макросов, которые определены в siginfo.h:
#define si_value _sifields._rt._sigval #define si_int _sifields._rt._sigval.sival_int #define si_ptr _sifields._rt._sigval.sival_ptr #define si_pid _sifields._kill._pid #define si_uid _sifields._kill._uid
Таким образом, для доступа к дополнительной информации, передаваемой отправителем, можно использовать siginfo->si_int и siginfo->si_ptr.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||
Предыдущая Содержание Следующая |