Блог
Анатолия Борисова

Как повысить свою долю в экспорте программного
обеспечения из России до 1 000 000 рублей в год?

Подарок

для каждого подписавшегося
на нашу рассылку

Пример межпроцессорного обмена сообщениями по стандарту System V

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

  1. Станислав
    Программирование изучаю для саморазвития, просто оно меня всегда интересовало, но учился я на юриста и сейчас работаю по специальности. Так что ваш пример я с удовольствием изучил на досуге. Все что вы описали, удалось реализовать. Так что, примите мои благодарности за мой очередной, небольшой опыт в данной области.
  2. Анатолий,я вижу вы отличный программист. По более обучающих статей пишите.
    • Анатолий Борисов
      Игорь, спасибо за ваш комментарий
      • Анатолий,да я вроде ничего существенного не написал,что вижу то и озвучил.

Оставить комментарий