Роб Кёртен - Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
- Название:Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
- Автор:
- Жанр:
- Издательство:Петрополис
- Год:2001
- Город:Санкт-Петербург
- ISBN:5-94656-025-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роб Кёртен - Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform краткое содержание
Книга "Введение в QNX/Neutrino 2» откроет перед вами в мельчайших подробностях все секреты ОСРВ нового поколения от компании QNX Software Systems Ltd (QSSL) — QNX/Neutrino 2. Книга написана в непринужденной манере, легким для чтения и понимания стилем, и поможет любому, от начинающих программистов до опытных системотехников, получить необходимые начальные знания для проектирования надежных систем реального времени, от встраиваемых управляющих приложений до распределенных сетевых вычислительных систем
В книге подробно описаны основные составляющие ОС QNX/Neutrino и их взаимосвязи. В частности, уделено особое внимание следующим темам:
• обмен сообщениями: принципы функционирования и основы применения;
• процессы и потоки: базовые концепции, предостережения и рекомендации;
• таймеры: организация периодических событий в программах;
• администраторы ресурсов: все, что относится к программированию драйверов устройств;
• прерывания: рекомендации по эффективной обработке.
В книге представлено множество проверенных примеров кода, подробных разъяснений и рисунков, которые помогут вам детально вникнуть в и излагаемый материал. Примеры кода и обновления к ним также можно найти на веб-сайте автора данной книги, www.parse.com.
Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Последний параметр в инициализации структуры события — это приоритет импульса; здесь мы выбрали SIGEV_PULSE_PRIO_INHERIT (константа, равная -1). Это предписывает ядру не изменять приоритет принимающего импульс потока.
В конце описания функции мы вызываем timer_create() для создания таймера в ядре, после чего настраиваем его на срабатывание через одну секунду (поле it_value ) и на периодическую перезагрузку односекундными интервалами (поле it_interval ). Отметим, что таймер включается только по вызову timer_settime() , а не при его создании.
Схема уведомления по типу SIGEV_PULSE — расширение, свойственное только QNX/Neutrino. Концепция импульсов в POSIX отсутствует.
/*
* setupPulseAndTimer
*
* Эта подпрограмма отвечает за настройку импульса, чтобы
* тот отправлял сообщение с кодом MT_TIMER.
* Затем устанавливается
* периодический таймер с периодом в одну секунду.
*/
void setupPulseAndTimer(void) {
timer_t timerid; // Идентификатор таймера
struct sigevent event; // Генерируемое событие
struct itimerspec timer; // Структура данных
// таймера
int coid; // Будем соединяться с
// собой
// Создать канал к себе
coid = ConnectAttach(0, 0, chid, 0, 0);
if (coid == -1) {
fprintf(stderr, "%s: ошибка ConnectAttach!\n", progname);
perror(NULL);
exit(EXIT_FAILURE);
}
// Установить, какое событие мы хотим сгенерировать
// - импульс
SIGEV_PULSE_INIT(&event, coid, SIGEV_PULSE_PRIO_INHERIT,
CODE_TIMER, 0);
// Создать таймер и привязать к событию
if (timer_create(CLOCK_REALTIME, &event, &timerid) ==
-1) {
fprintf(stderr,
"%s: не удалось создать таймер, errno %d\n",
progname, errno);
perror(NULL);
exit(EXIT_FAILURE);
}
// Настроить таймер (задержка 1 с, перезагрузка через
// 1 с) ...
timer.it_value.tv_sec = 1;
timer.it_value.tv_nsec = 0;
timer.it_interval.tv_sec = 1;
timer.it_interval.tv_nsec = 0;
// ...и запустить его!
timer_settime(timerid, 0, &timer, NULL);
}
В функции gotAPulse() вы можете видеть, как мы реализовали способность сервера обеспечивать тайм-ауты для клиентов. Мы последовательно просматриваем список клиентуры и, поскольку мы знаем, что импульс выдается один раз в секунду, просто уменьшаем число секунд, которое остается клиенту до тайм-аута. Если эта величина достигает нулевого значения, мы отвечаем этому клиенту сообщением «Извините, тайм-аут» (тип сообщения MT_TIMEDOUT). Обратите внимание, что мы подготавливаем это сообщение заранее (вне цикла for
), а затем посылаем его по мере необходимости. Этот прием — по существу вопрос стиля: если вы предполагаете отвечать часто, возможно, имело бы смысл выполнить настройку однажды и загодя. Если же множество ответов не ожидается, то имело бы больший смысл делать настройки по мере необходимости.
Если значение оставшегося времени еще не достигло нуля, мы не делаем ничего — клиент по-прежнему заблокирован в ожидании сообщения.
/*
* gotAPulse
*
* Эта подпрограмма отвечает за обработку тайм-аутов.
* Она проверяет список клиентов на предмет тайм-аута
* и отвечает соответствующим сообщением тем клиентам,
* у которых тайм-аут произошел.
*/
void gotAPulse(void) {
ClientMessageT msg;
int i;
if (debug) {
time_t now;
time(&now);
printf("Получен импульс, время %s", ctime(&now));
}
// Подготовить ответное сообщение
msg.messageType = MT_TIMEDOUT;
// Просмотреть список клиентов
for (i = 0; i < MAX_CLIENT; i++) {
// Элемент используется?
if (clients[i].in_use) {
// Тайм-аут?
if (--clients[i].timeout == 0) {
// Ответить
MsgReply(clients[i].rcvid, EOK, &msg, sizeof(msg));
// Освободить элемент
clients[i].in_use = 0;
}
}
}
}
В функции gotAMessage() вы видите другую половину заданной функциональности, где мы добавляем клиента в список клиентуры, ожидающей данные (если получено сообщение типа MT_WAIT_DATA), или сопоставляем клиента с сообщением, которое было только что получено (если это сообщение типа MT_SEND_DATA). Заметьте, что для простоты мы здесь не реализуем очередь клиентов, находящихся в ожидании передачи данных, получатель для которых еще не доступен — это вопрос управления очередями, оставьте его для себя в качестве упражнения.
/*
* gotAMessage
*
* Эта подпрограмма вызывается при каждом приеме
* сообщения. Проверяем тип
* сообщения (либо «жду данных», либо «вот данные»),
* и действуем
* соответственно. Для простоты предположим, что данные
* никогда не ждут.
* Более подробно об этом см. в тексте.
*/
void gotAMessage(int rcvid, ClientMessageT *msg) {
int i;
// Определить тип сообщения
switch (msg->messageType) {
// Клиент хочет ждать данных
case MT_WAIT_DATA:
// Посмотрим, есть ли пустое место в таблице клиентов
for (i = 0; i < MAX_CLIENT; i++) {
if (!clients[i].in_use) {
// Нашли место - пометить как занятое,
// сохранить rcvid
// и установить тайм-аут
clients[i].in_use = 1;
clients[i].rcvid = rcvid;
clients[i].timeout = 5;
return;
}
}
fprintf(stderr,
"Таблица переполнена, сообщение от rcvid %d"
" игнорировано, клиент заблокирован\n", rcvid);
break;
// Клиент с данными
case MT_SEND_DATA:
// Посмотрим, есть ли другой клиент, которому можно ответить
// данными от этого клиента
for (i = 0; i < MAX CLIENT; i++) {
if (clients[i].in_use) {
// Нашли - использовать полученное сообщение
// в качестве ответного
msg->messageType = MT_OK;
// Ответить ОБОИМ КЛИЕНТАМ!
MsgReply(clients[i].rcvid, EOK, msg, sizeof(*msg));
MsgReply(rcvid, EOK, msg, sizeof(*msg));
clients[i].in_use = 0;
return;
}
}
fprintf(stderr,
"Таблица пуста, сообщение от rcvid %d игнорировано,"
" клиент заблокирован\n", rcvid);
break;
}
}
Несколько общих замечаний по тексту программы:
• Если сообщение с данными прибывает, когда либо никто не ждет, либо список ожидающих клиентов переполнен, в стандартный поток ошибок выводится сообщение, но клиенту при этом мы не отвечаем ничего. Это означает, что ряд клиентов может оказаться в REPLY-блокированном состоянии навсегда — идентификаторы отправителей мы потеряли, а значит, и ответ им дать не можем.
Это сделано намеренно. Вы можете изменить это, добавив соответственно сообщения MT_NO_WAITERS и MT_NO_SPACE, которыми можно было бы отвечать всякий раз при обнаружении ошибок данного типа.
Читать дальшеИнтервал:
Закладка: