Назад

Содержание

Вперед


1.7.3.3. Разделяемая память.

    Рассмотрим наиболее мощный способ обмена данными - разделяемую память. Она позволяет двум и более процессам использовать одну и ту же область (сегмент) оперативной памяти. Это самое эффективное средство обмена, поскольку при его использовании не происходит копирования информации, и доступ к ней производится напрямую. Для синхронизации записи данных в общую память, как правило, используются семафоры.

    Для получения доступа к сегменту разделяемой памяти используется системный вызов shmget( ). Формат его следующий:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget (key_t key, int size, int flag);

    Здесь:

key - уникальный ключ разделяемого сегмента памяти;
size - размер сегмента; в некоторых версиях системы UNIX он ограничен 128 К байтами;
flag - задание режима создания разделяемой памяти; значение этого параметра то же, что и в msgget( ), только префикс "MSG" заменен на "SHM".

    При успешном создании или, если объект с заданным ключом key существует, функция возвращает идентификатор сегмента. В случае ошибки функция возвращает значение -1.

    После создания разделяемой памяти, надо получить ее адрес. Для этого используется вызов shmat( ):

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

void *shmat (int shmid, void *addr, int flag);

    Первый аргумент - идентификатор сегмента разделяемой памяти. Различные сочетания значений второго и третьего аргументов задают способ определения его адреса. Не приводя всех возможных вариантов, заметим, что простейшим вариантом из всех возможных является тот, при котором оба аргумента равны нулю. Возвращаемый системным вызовом shmat( ) адрес в дальнейшем используется процессом для прямого обращения к сегменту.

    Изменить режим доступа к сегменту разделяемой памяти или удалить его можно при помощи системного вызова shmctl( ). При этом следует заметить, что сегмент не может быть удален до тех пор, пока не сделано обращение к процедуре shmdt( ). Прототипы shmdt( ) и shmctl( ) следующие:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

void shmdt (void *ptr);
int shmctl (int shmid, int cmd, struct shmid_ds *buf);

    Здесь :

ptr - указатель на сегмент разделяемой памяти, полученный при вызове shmat( );
shmid - идентификатор сегмента, возвращаемый функцией shmget( );
cmd - выполняемая операция, основная из них IPC_RMID - удалить объект;
buf структура, используемая для передачи данных, необходимых для выполнения операции и/или получения ее результатов.

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

    Оба процесса используют файл-заголовок "message.h".

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <stdio.h>

#define SEM_ID	2001      /* ключ массива семафоров */
#define SHM_ID	2002      /* ключ разделяемой памяти */
#define PERMS	0666      /* права доступа */

/* коды сообщений */

#define MSG_TYPE_EMPTY  0 /* пустое сообщение */
#define MSG_TYPE_STRING 1 /* тип сообщения о том, что
                             передана непустая строка */
#define MSG_TYPE_FINISH 2 /* тип сообщения о том, что
                             пора завершать обмен */
#define MAX_STRING	120

/* структура сообщения, помещаемого в разделяемую память */
typedef struct
{
  int type;
  char string [MAX_STRING];
} message_t;

    Код программы-клиента:

#include <stdio.h>
#include <string.h>
#include "message.h"

void sys_err (char *msg)
{
  puts (msg);
  exit (1);
}

int main ()
{
  int semid;                    /* идентификатор семафора */
  int shmid;                    /* идентификатор разделяемой памяти */
  message_t *msg_p;             /* адрес сообщения в разделяемой
                                   памяти */
  char s[MAX_STRING];

  /* получение доступа к массиву семафоров */
  if ((semid = semget (SEM_ID, 1, 0)) < 0)
    sys_err ("client: can not get semaphore");

  /* получение доступа к сегменту разделяемой памяти */
  if ((shmid = shmget (SHM_ID, sizeof (message_t), 0)) < 0)
    sys_err ("client: can not get shared memory segment");

  /* получение адреса сегмента */
  if ((msg_p = (message_t *) shmat (shmid, 0, 0)) == NULL)
    sys_err ("client: shared memory attach error");

  while (1)
    {
      scanf ("%s", s);
      while (semctl (semid, 0, GETVAL, 0) || msg_p->type != MSG_TYPE_EMPTY)
        /*
         *   если сообщение не обработано или сегмент блокирован - ждать
         *                                                             */
      ;
      semctl (semid, 0, SETVAL, 1);     /* блокировать */
      if (strlen (s) != 1)
        {
          /* записать сообщение "печать строки" */
          msg_p->type = MSG_TYPE_STRING;
          strncpy (msg_p->string, s, MAX_STRING);
        }
      else
        {
          /* записать сообщение "завершение работы" */
          msg_p->type = MSG_TYPE_FINISH;
        };
      semctl (semid, 0, SETVAL, 0);     /* отменить блокировку */
      if (strlen (s) == 1)
        break;
    }
  shmdt (msg_p);                /* отсоединить сегмент разделяемой памяти */
  exit (0);
}

    Код программы-сервера:

#include <stdio.h>
#include <string.h>
#include "message.h"

void sys_err (char *msg)
{
  puts (msg);
  exit (1);
}

int main ()
{
  int semid;                    /* идентификатор семафора */
  int shmid;                    /* идентификатор разделяемой памяти */
  message_t *msg_p;             /* адрес сообщения в разделяемой
                                   памяти */
  char s[MAX_STRING];

  /* создание массива семафоров из одного элемента */
  if ((semid = semget (SEM_ID, 1, PERMS | IPC_CREAT)) < 0)
    sys_err ("server: can not create semaphore");

  /* создание сегмента разделяемой памяти */
  if ((shmid = shmget (SHM_ID, sizeof (message_t), PERMS | IPC_CREAT)) < 0)
    sys_err ("server: can not create shared memory segment");

  /* подключение сегмента к адресному пространству процесса */
  if ((msg_p = (message_t *) shmat (shmid, 0, 0)) == NULL)
    sys_err ("server: shared memory attach error");

  semctl (semid, 0, SETVAL, 0); /* установка семафора */
  msg_p->type = MSG_TYPE_EMPTY;

  while (1)
    {
      if (msg_p->type != MSG_TYPE_EMPTY)
        {
          if (semctl (semid, 0, GETVAL, 0))     /* блокировка - ждать */
            continue;

          semctl (semid, 0, SETVAL, 1); /* установить блокировку */

          /* обработка сообщения */
          if (msg_p->type == MSG_TYPE_STRING)
            printf ("%s\n", msg_p->string);
          if (msg_p->type == MSG_TYPE_FINISH)
            break;

          msg_p->type = MSG_TYPE_EMPTY; /* сообщение обработано */
          semctl (semid, 0, SETVAL, 0); /* снять блокировку */
        }
    }

  /* удаление массива семафоров */
  if (semctl (semid, 0, IPC_RMID, (struct semid_ds *) 0) < 0)
    sys_err ("server: semaphore remove error");

  /* удаление сегмента разделяемой памяти */
  shmdt (msg_p);
  if (shmctl (shmid, IPC_RMID, (struct shmid_ds *) 0) < 0)
    sys_err ("server: shared memory remove error");

  exit (0);
}