Функции стандарта 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 ответов