Выбрать главу

Отметьте, что в двух приведенных выше примерах я говорил: «мы предполагаем». В коде функции timer_settime() нет никаких проверок на правильность аргументов! Вы должны самостоятельно доопределить, является таймер абсолютным или относительным. Что до ядра, то оно будет просто счастливо запланировать какое-нибудь событие на 31.3 года вперед.

И еще один пример:

it_value.tv_sec = 1;

it_value.tv_nsec = 0;

it_interval.tv_sec = 0;

it_interval.tv_nsec = 500000000;

Если предположить, что это относительный таймер, он сработает через одну секунду и далее каждые полсекунды. Не существует никаких требований какого бы то ни было подобия значений интервала перезагрузки значениям задержки однократного срабатывания.

Сервер с периодическими импульсами

Первое, что следует рассмотреть, — это сервер, который желает получать периодические сообщения. Типовыми применениями такой схемы являются:

• поддерживаемые сервером тайм-ауты клиентских запросов;

• внутренние периодические события серверов.

Конечно, есть и другие, специализированные, применения для таких вещей — например, периодические подтверждения готовности узлов сети («я жив»), запросы на повторную передачу, и т.п.

Поддерживаемые сервером тайм-ауты

В таком сценарии сервер предоставляет клиенту некоторую услугу, и клиент способен задать тайм-аут. Это может использоваться в самых разнообразных приложениях. Например, вы можете сказать серверу «выдай мне данные за 15 секунд» или «дай мне знать, когда истекут 10 секунд», или «жди прихода данных, но в течение не более чем 2 секунд».

Все это — примеры поддерживаемых сервером тайм-аутов. Клиент посылает сообщение серверу и блокируется. Сервер принимает периодические сообщения от таймера (раз в секунду, реже или чаще) и подсчитывает, сколько этих сообщений он получил. Когда число сообщений о тайм-аутах превышает время ожидания, указанное клиентом, сервер отвечает клиенту с сообщением о тайм-ауте или, возможно, с данными, которые он успел накопить на данный момент — это зависит от того, как структурированы отношения клиента и сервера.

Ниже приведен полный пример сервера, который принимает одно из двух сообщений от клиентуры и сообщения о тайм-ауте в виде импульса. Первое клиентское сообщение говорит серверу: «Дай мне знать, есть ли для меня данные, но не блокируй меня более чем на 5 секунд». Второе клиентское сообщение говорит: «Вот, возьми данные». Сервер должен позволить нескольким клиентам блокироваться на себе в ожидании данных, и поэтому обязан сопоставить клиентам тайм-ауты. Тут-то и нужен импульс; он информирует сервер: «Истекла одна секунда».

Чтобы программа не выглядела излишне громоздкой, перед каждым из основных разделов я прерываю исходный текст небольшими пояснениями. Скачать эту программу вы можете на FTP-сайте компании PARSE (ftp://ftp.parseftp.parse.com/pub/book_v3.tar.gz), файл называется time1.с.

Декларации

В первом разделе программы определяются различные именованные константы и структуры данных. В нем также подключаются все необходимые заголовочные файлы. Оставим это без комментариев. :-)

/*

 * time1.c

 *

 * Пример сервера, получающего периодические сообщения

 * от таймера

 * и обычные сообщения от клиента.

 *

 * Иллюстрирует использование функций таймера с импульсами.

*/

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <signal.h>

#include <errno.h>

#include <unistd.h>

#include <sys/siginfo.h>

#include <sys/neutrino.h>

// Получаемые сообщения

// Сообщения

#define MT_WAIT_DATA 2 // Сообщение от клиента

#define MT_SEND_DATA 3 // Сообщение от клиента

// Импульсы

#define CODE_TIMER 1 // Импульс от таймера

// Отправляемые сообщения

#define MT_OK       0 // Сообщение клиенту

#define MT_TIMEDOUT 1 // Сообщение клиенту

// Структура сообщения

typedef struct {

 int messageType; // Содержит сообщение от клиента и

                  // клиенту

 int messageData; // Опциональные данные, зависят от

                  // сообщения

} ClientMessageT;

typedef union {

 ClientMessageT msg;  // Сообщение может быть

                      // либо обычным,

 struct _pulse pulse; // либо импульсом

} MessageT;

// Таблица клиентов

#define MAX_CLIENT 16 // Максимум клиентов

                      // одновременно

struct {

 int in_use;  // Элемент используется?

 int rcvid;   // Идентификатор

              // отправителя клиента

 int timeout; // Оставшийся клиенту

              //тайм-аут

} clients[MAX_CLIENT]; // Таблица клиентов

int chid; // Идентификатор канала

          // (глобальный)

int debug = 1; // Режим отладки, 1 ==

               // вкл, 0 == выкл

char *progname = "time1.c";

// Предопределенные прототипы

static void setupPulseAndTimer(void);

static void gotAPulse(void);

static void gotAMessage(int rcvid, ClientMessageT *msg);

main()

Следующий раздел кода является основным и отвечает за следующее:

• создание канала с помощью функции ChannelCreate();

• вызов подпрограммы setupPulseAndTimer() (для настройки периодического таймера, срабатывающего раз в секунду и использующего импульс в качестве способа доставки события;

• и, наконец, бесконечный цикл ожидания импульсов и сообщений и их обработки.

Обратите внимание на проверку значения, возвращаемого MsgReceive() — нуль указывает, что был принят импульс (здесь мы не делаем никакой дополнительной проверки, наш ли это импульс), ненулевое значение говорит о том, что было принято сообщение.

Обработка импульсов и сообщений выполняется функциями gotAPulse() и gotAMessage().

int main void) // Игнорировать аргументы

               // командной строки

{

 int rcvid; // PID отправителя

 MessageT msg; // Само сообщение

 if ((chid = ChannelCreate(0)) == -1) {

  fprintf(stderr, "%s: не удалось создать канал!\n",