Отметьте, что в двух приведенных выше примерах я говорил: «мы предполагаем». В коде функции 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);
Следующий раздел кода является основным и отвечает за следующее:
• создание канала с помощью функции ChannelCreate();
• вызов подпрограммы setupPulseAndTimer() (для настройки периодического таймера, срабатывающего раз в секунду и использующего импульс в качестве способа доставки события;
• и, наконец, бесконечный цикл ожидания импульсов и сообщений и их обработки.
Обратите внимание на проверку значения, возвращаемого MsgReceive() — нуль указывает, что был принят импульс (здесь мы не делаем никакой дополнительной проверки, наш ли это импульс), ненулевое значение говорит о том, что было принято сообщение.
Обработка импульсов и сообщений выполняется функциями gotAPulse() и gotAMessage().
int main void) // Игнорировать аргументы
// командной строки
{
int rcvid; // PID отправителя
MessageT msg; // Само сообщение
if ((chid = ChannelCreate(0)) == -1) {
fprintf(stderr, "%s: не удалось создать канал!\n",