Функции стандарта System V были разработаны еще для мэйнфреймов, но и сейчас их можно с легкостью применять на x86 системах. Предположим, что у вас есть демон (сервис, служба) который работает в режиме поллинга (англ. Polling), это может быть например брандмауэр. Вам потребовалось посредством графической оболочки или утилиты командной строки уведомить демон о добавлении нового параметра фильтрации или каких-либо изменениях в старых. Давайте рассмотрим как это можно сделать с помощью очереди сообщений System V.Очереди сообщений обладают живучестью ядра и это факт очень удобен для режима поллинга демона, в работу которого нужно вмешаться. Объявим структур передаваемого сообщения:
typedef struct _mymsg{
long mtype;
int cmd;
int data;
} MsgBuf;
Пусть параметр cmd будет позволять добавлять новый IP для фильтрации, его смену и менять категорию к которой он относиться.
enum Cmd{
NEW_CLIENT = 0, // новый IP
UPDATE_CATEGORIES = 1, // изменение категории
UPDATE_IP = 2 // обновление IP адреса клиента
};
СЕРВЕРНАЯ ЧАСТЬ
Эту часть кода следуют встроить в цикл поллинга демона. Создаем ключ для очереди и сохраняем его для клиента в файл .key
#define KEYPATH "/"
#define KEYID 133
#define KEYPATHOUT "/tmp/.key"
key = ftok(KEYPATH, KEYID);
if (key == -1)
{
fprintf(stderr, "error: unable to get key for queuen");
return 0;
}
else
{
f = fopen(KEYPATHOUT, "w+");
if (f)
{
fprintf(f, "%d", key);
fflush(f);
fclose(f);
}
else
{
fprintf(stderr, "error: unable to open /tmp/.dnsn");
return 0;
}
}
Создаем или подключаемся к уже существующей очереди.
mqid = msgget(key, 0666 | IPC_CREAT | IPC_EXCL);
if (mqid == -1)
{
if (errno == EEXIST)
{
printf("queue is existn");
mqid = msgget(key, 0);
}
else
{
printf("error: can't create queuen");
return 1;
}
}
printf("create/connect to queue N:%d n", mqid);
Извлекаем сообщения из очереди.
int len = 0;
errno = 0;
for (;;)
{
n = msgrcv(mqid,
&rcvmsg,
sizeof(MsgBuf) - sizeof(long),
rcvmsg.mtype,
IPC_NOWAIT);
if (n > 0)
{
printf("Get msg: cmd %d, data %d, size %dn",
rcvmsg.cmd, rcvmsg.data, n);
msg_count++;
}
else
{
switch (errno)
{
case ENOMSG:
printf("errno = ENOMSGn");
sleep(10);
break;
case EIDRM:
printf("Message queue N:%d not found", mqid);
break;
case E2BIG:
printf("error: too big msg!n");
break;
default:
break;
}
}
if (msg_count < 4)
break;
}
КЛИЕНТСКАЯ ЧАСТЬ
Получение ключа очереди.
f = fopen(KEYPATHOUT, "r");
if (f)
fscanf(f, "%d", &key);
else
{
printf("error: can't open file %sn", KEYPATHOUT);
return 1;
}
fclose(f);
if (!key)
{
printf("error: can't get key");
return 1;
}
Подключение к очереди аналогично серверной части. Отправка сообщения в очередь.
len = 2 * sizeof(int);
for (i = 0; i < MAX_PARAM; i++)
{
if (param[i])
{
switch (i)
{
case NEW_CLIENT:
sndmsg.cmd = NEW_CLIENT;
sndmsg.data = param[NEW_CLIENT];
break;
case UPDATE_CATEGORIES:
sndmsg.cmd = UPDATE_CATEGORIES;
sndmsg.data = param[UPDATE_CATEGORIES];
break;
case UPDATE_IP:
sndmsg.cmd = UPDATE_IP;
sndmsg.data = param[UPDATE_IP];
break;
default:
printf("unknown commandn");
flag = 1;
} // switch(i)
err = msgsnd(mqid, &sndmsg, len, 0);
if (!err)
{
printf("send msgn");
}
else
{
switch (errno)
{
case EAGAIN:
err = msgsnd(mqid, &sndmsg, len, 0);
printf("send msgn");
break;
case EIDRM:
printf("Message queue N:%d not foundn", mqid);
break;
default:
if (!err)
flag = 1;
}
} // if (!err) ... else
} // if (param[i])
Несколько слов про ограничение очередей сообщения System V. Во-первых эти ограничения зависят от реализации. Так Ubuntu Server 10.04 LTS выдает следующие значения:
user@host:~$ ipcs -l ------ Сообщения: Пределы -------- максимум очередей для всей системы = 1626 максимальный размер сообщения (байт) = 8192 максимальный по умолчанию размер сообщения (байт) = 16384
Во-вторых их можно менять редактируя файл /etc/sysctl.conf. Если этот файл не существует, его нужно создать и обновить переменные ядра командой: user@host:~$ sysctl -p
1 У. Стивенс - UNIX: разработка сетевых приложений. - СПб.: Питер, 2003 - 1088 с.: ил. - (Серия «Мастер-класс»)




4 ответов