Учебники

Общая память

Общая память — это память, разделяемая между двумя или более процессами. Однако зачем нам делиться памятью или какими-то другими средствами связи?

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

Обычно взаимосвязь между процессами осуществляется с использованием каналов или именованных каналов. Связь между несвязанными процессами (скажем, одним процессом, выполняющимся в одном терминале, и другим процессом в другом терминале) может осуществляться с использованием именованных каналов или с помощью популярных методов IPC общей памяти и очередей сообщений.

Мы познакомились с методами IPC для каналов и именованных каналов, и теперь пришло время узнать об оставшихся методах IPC: общая память, очереди сообщений, семафоры, сигналы и отображение памяти.

В этой главе мы узнаем все об общей памяти.

Общая память

Мы знаем, что для связи между двумя или более процессами мы используем общую память, но прежде чем использовать общую память, что нужно сделать с помощью системных вызовов, давайте посмотрим на это —

  • Создайте сегмент общей памяти или используйте уже созданный сегмент общей памяти (shmget ())

  • Присоединить процесс к уже созданному сегменту разделяемой памяти (shmat ())

  • Отключить процесс от уже подключенного сегмента общей памяти (shmdt ())

  • Операции управления в сегменте разделяемой памяти (shmctl ())

Создайте сегмент общей памяти или используйте уже созданный сегмент общей памяти (shmget ())

Присоединить процесс к уже созданному сегменту разделяемой памяти (shmat ())

Отключить процесс от уже подключенного сегмента общей памяти (shmdt ())

Операции управления в сегменте разделяемой памяти (shmctl ())

Давайте посмотрим на некоторые детали системных вызовов, связанных с общей памятью.

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

int shmget(key_t key, size_t size, int shmflg)

Приведенный выше системный вызов создает или выделяет сегмент совместно используемой памяти System V. Аргументы, которые необходимо передать, следующие:

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

Второй аргумент, size, это размер сегмента разделяемой памяти, округленный до кратного PAGE_SIZE.

Третий аргумент, shmflg, указывает требуемые флаги общей памяти, такие как IPC_CREAT (создание нового сегмента) или IPC_EXCL (используется с IPC_CREAT для создания нового сегмента, и вызов не выполняется, если сегмент уже существует). Нужно также передать разрешения.

Примечание. Подробнее о разрешениях см. В предыдущих разделах.

Этот вызов вернул бы действительный идентификатор совместно используемой памяти (используемый для дальнейших вызовов совместно используемой памяти) в случае успеха и -1 в случае сбоя. Чтобы узнать причину ошибки, проверьте с помощью переменной errno или функции perror ().

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

void * shmat(int shmid, const void *shmaddr, int shmflg)

Вышеупомянутый системный вызов выполняет операцию совместно используемой памяти для сегмента совместно используемой памяти System V, то есть присоединение сегмента совместно используемой памяти к адресному пространству вызывающего процесса. Аргументы, которые необходимо передать, следующие:

Первый аргумент, shmid, является идентификатором сегмента разделяемой памяти. Этот идентификатор является идентификатором разделяемой памяти, который является возвращаемым значением системного вызова shmget ().

Второй аргумент, shmaddr, должен указать адрес присоединения. Если shmaddr равен NULL, система по умолчанию выбирает подходящий адрес для присоединения сегмента. Если shmaddr не равен NULL и SHM_RND указан в shmflg, присоединение будет равно адресу ближайшего кратного SHMLBA (адрес нижней границы). В противном случае shmaddr должен быть адресом с выравниванием страницы, по которому происходит / начинается вложение в общую память.

Третий аргумент, shmflg, указывает требуемые флаги общей памяти, такие как SHM_RND (адрес округления до SHMLBA) или SHM_EXEC (разрешает выполнение содержимого сегмента) или SHM_RDONLY (по умолчанию присоединяет сегмент только для чтения) это чтение-запись) или SHM_REMAP (заменяет существующее отображение в диапазоне, указанном shmaddr и продолжающемся до конца сегмента).

Этот вызов вернет адрес подключенного сегмента разделяемой памяти в случае успеха и -1 в случае сбоя. Чтобы узнать причину ошибки, проверьте с помощью переменной errno или функции perror ().

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

int shmdt(const void *shmaddr)

Вышеупомянутый системный вызов выполняет операцию совместно используемой памяти для сегмента совместно используемой памяти System V, отсоединяя сегмент совместно используемой памяти от адресного пространства вызывающего процесса. Аргумент, который необходимо передать —

Аргумент shmaddr — это адрес сегмента разделяемой памяти, который нужно отсоединить. Отключаемый сегмент должен быть адресом, возвращаемым системным вызовом shmat ().

Этот вызов вернет 0 в случае успеха и -1 в случае сбоя. Чтобы узнать причину ошибки, проверьте с помощью переменной errno или функции perror ().

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf)

Вышеупомянутый системный вызов выполняет операцию управления для сегмента совместно используемой памяти System V. Следующие аргументы должны быть переданы —

Первый аргумент, shmid, является идентификатором сегмента разделяемой памяти. Этот идентификатор является идентификатором разделяемой памяти, который является возвращаемым значением системного вызова shmget ().

Второй аргумент, cmd, является командой для выполнения требуемой операции управления в сегменте разделяемой памяти.

Допустимые значения для cmd —

  • IPC_STATкопирует информацию о текущих значениях каждого члена struct shmid_ds в переданную структуру, указанную в buf. Эта команда требует разрешения на чтение для сегмента общей памяти.

  • IPC_SET — устанавливает идентификатор пользователя, идентификатор группы владельца, разрешения и т. Д., На которые указывает структура buf.

  • IPC_RMID — отмечает сегмент, подлежащий уничтожению. Сегмент уничтожается только после того, как последний процесс отключил его.

  • IPC_INFO — возвращает информацию об ограничениях и параметрах разделяемой памяти в структуре, на которую указывает buf.

  • SHM_INFO — возвращает структуру shm_info, содержащую информацию об используемых системных ресурсах совместно используемой памятью.

IPC_STATкопирует информацию о текущих значениях каждого члена struct shmid_ds в переданную структуру, указанную в buf. Эта команда требует разрешения на чтение для сегмента общей памяти.

IPC_SET — устанавливает идентификатор пользователя, идентификатор группы владельца, разрешения и т. Д., На которые указывает структура buf.

IPC_RMID — отмечает сегмент, подлежащий уничтожению. Сегмент уничтожается только после того, как последний процесс отключил его.

IPC_INFO — возвращает информацию об ограничениях и параметрах разделяемой памяти в структуре, на которую указывает buf.

SHM_INFO — возвращает структуру shm_info, содержащую информацию об используемых системных ресурсах совместно используемой памятью.

Третий аргумент, buf, это указатель на структуру разделяемой памяти с именем struct shmid_ds. Значения этой структуры будут использоваться для набора или получения согласно cmd.

Этот вызов возвращает значение в зависимости от переданной команды. В случае успеха IPC_INFO и SHM_INFO или SHM_STAT возвращает индекс или идентификатор сегмента общей памяти или 0 для других операций и -1 в случае сбоя. Чтобы узнать причину ошибки, проверьте с помощью переменной errno или функции perror ().

Давайте рассмотрим следующий пример программы.

  • Создайте два процесса, один для записи в общую память (shm_write.c), а другой для чтения из общей памяти (shm_read.c)

  • Программа выполняет запись в общую память с помощью процесса записи (shm_write.c) и чтения из общей памяти с помощью процесса чтения (shm_read.c).

  • В общей памяти процесс записи создает общую память размером 1 КБ (и флаги) и присоединяет общую память

  • Процесс записи 5 раз записывает алфавиты от «A» до «E» каждый из 1023 байтов в общую память. Последний байт означает конец буфера

  • Процесс чтения будет читать из общей памяти и записывать в стандартный вывод

  • Действия чтения и записи выполняются одновременно

  • После завершения записи процесс записи обновляется, чтобы указать завершение записи в разделяемую память (с переменной complete в struct shmseg)

  • Процесс чтения выполняет чтение из общей памяти и отображает на выходе, пока не получит индикацию завершения процесса записи (переменная complete в struct shmseg)

  • Выполняет процесс чтения и записи несколько раз для упрощения, а также во избежание бесконечных циклов и усложнения программы

Создайте два процесса, один для записи в общую память (shm_write.c), а другой для чтения из общей памяти (shm_read.c)

Программа выполняет запись в общую память с помощью процесса записи (shm_write.c) и чтения из общей памяти с помощью процесса чтения (shm_read.c).

В общей памяти процесс записи создает общую память размером 1 КБ (и флаги) и присоединяет общую память

Процесс записи 5 раз записывает алфавиты от «A» до «E» каждый из 1023 байтов в общую память. Последний байт означает конец буфера

Процесс чтения будет читать из общей памяти и записывать в стандартный вывод

Действия чтения и записи выполняются одновременно

После завершения записи процесс записи обновляется, чтобы указать завершение записи в разделяемую память (с переменной complete в struct shmseg)

Процесс чтения выполняет чтение из общей памяти и отображает на выходе, пока не получит индикацию завершения процесса записи (переменная complete в struct shmseg)

Выполняет процесс чтения и записи несколько раз для упрощения, а также во избежание бесконечных циклов и усложнения программы

Ниже приведен код для процесса записи (запись в общую память — файл: shm_write.c)

/* Filename: shm_write.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

#define BUF_SIZE 1024
#define SHM_KEY 0x1234

struct shmseg {
   int cnt;
   int complete;
   char buf[BUF_SIZE];
};
int fill_buffer(char * bufptr, int size);

int main(int argc, char *argv[]) {
   int shmid, numtimes;
   struct shmseg *shmp;
   char *bufptr;
   int spaceavailable;
   shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
   if (shmid == -1) {
      perror("Shared memory");
      return 1;
   }
   
   // Attach to the segment to get a pointer to it.
   shmp = shmat(shmid, NULL, 0);
   if (shmp == (void *) -1) {
      perror("Shared memory attach");
      return 1;
   }
   
   /* Transfer blocks of data from buffer to shared memory */
   bufptr = shmp->buf;
   spaceavailable = BUF_SIZE;
   for (numtimes = 0; numtimes < 5; numtimes++) {
      shmp->cnt = fill_buffer(bufptr, spaceavailable);
      shmp->complete = 0;
      printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
      bufptr = shmp->buf;
      spaceavailable = BUF_SIZE;
      sleep(3);
   }
   printf("Writing Process: Wrote %d times\n", numtimes);
   shmp->complete = 1;

   if (shmdt(shmp) == -1) {
      perror("shmdt");
      return 1;
   }

   if (shmctl(shmid, IPC_RMID, 0) == -1) {
      perror("shmctl");
      return 1;
   }
   printf("Writing Process: Complete\n");
   return 0;
}

int fill_buffer(char * bufptr, int size) {
   static char ch = 'A';
   int filled_count;
   
   //printf("size is %d\n", size);
   memset(bufptr, ch, size - 1);
   bufptr[size-1] = '\0';
   if (ch > 122)
   ch = 65;
   if ( (ch >= 65) && (ch <= 122) ) {
      if ( (ch >= 91) && (ch <= 96) ) {
         ch = 65;
      }
   }
   filled_count = strlen(bufptr);
   
   //printf("buffer count is: %d\n", filled_count);
   //printf("buffer filled is:%s\n", bufptr);
   ch++;
   return filled_count;
}

Шаги компиляции и выполнения

Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Wrote 5 times
Writing Process: Complete

Ниже приведен код для процесса чтения (чтение из общей памяти и запись в стандартный вывод — File: shm_read.c)