Книга: Основы программирования в Linux

shmctl

shmctl

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

int shmctl(int shm_id, int command, struct shmid_ds *buf);

У структуры типа shmid_ds есть, как минимум, следующие элементы:

struct shmid_ds {
 uid_t shm_perm.uid;
 uid_t shm_perm.gid;
 mode_t shm_perm.mode;
}

Первый параметр shm_id — идентификатор, возвращаемый функцией shmget.

Второй параметр command содержит предпринимаемое действие. Он может принимать три значения, перечисленные в табл. 14.2.

Таблица 14.2

Значение Описание
IPC_STAT Задаёт данные в структуре shmid_ds, отображающие значения, связанные с совместно используемой памятью
IPC_SET Устанавливает значения, связанные с совместно используемой памятью в соответствии с данными из структуры типа shmid_ds, если у процесса есть право на это действие
IPC_RMID Удаляет сегмент совместно используемой памяти

Третий параметр buf — указатель на структуру, содержащую режимы и права доступа для совместно используемой памяти.

В случае успеха возвращает 0, в случае ошибки — -1. В стандарте X/Open не описано, что произойдет, если вы попытаетесь удалить присоединенный к процессу сегмент совместно используемой памяти. Обычно присоединенный, но удаленный сегмент совместно используемой памяти продолжает функционировать до тех пор, пока не будет отсоединен от последнего процесса. Но поскольку это поведение не задано в стандарте, на него лучше не рассчитывать.

Выполните упражнение 14.2.

Упражнение 14.2. Совместно используемая память

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

1. Сначала создайте общий заголовочный файл для описания совместно используемой памяти, которую вы хотите предоставить. Назовите его shm_com.h.

#define TEXT_SZ 2048
struct shared_use_st {
 int written_by_you;
 char some_text[TEXT_SZ];
};

В файле определена структура, которая будет применяться в обеих программах: потребителе и поставщике. Вы используете флаг written_by_you типа int для того, чтобы сообщить потребителю о том, что данные записаны в оставшуюся часть структуры, и произвольно решаете, что необходимо передать до 2 Кбайт текста.

2. Первая программа shm1.c — потребитель. После заголовочных файлов создается сегмент совместно используемой памяти (размер равен вашей структуре, описывающей совместно используемую память) с помощью вызова shmget с заданным битом IPC_CREAT.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"
int main() {
 int running = 1;
 void *shared_memory = (void *)0;
 struct shared_use_st *shared_stuff;
 int shmid;
 srand((unsigned int)getpid());
 shmid = shmget((key_t)1234, sizeof(struct shared_use_st),
  0666 | IPC_CREAT);
 if (shmid == -1) {
  fprintf(stderr, "shmget failedn");
  exit(EXIT_FAILURE);
 }

3. Теперь вы делаете совместно используемую память доступной программе.

 shared_memory = shmat(shmid, (void *)0, 0);
 if (shared memory == (void *)-1) {
  fprintf(stderr, "shmat failedn");
  exit(EXIT_FAILURE);
 }
 printf("Memory attached at %Xn", (int)shared_memory);

4. В следующем фрагменте программы сегмент shared_memory присваивается переменной shared_stuff, из которой затем выводится любой текст, содержащийся в some_text. Цикл продолжает выполняться до тех пор, пока не найдена строка end в элементе some_text. Вызов функции sleep заставляет программу-потребителя оставаться в своей критической секции, что вынуждает поставщика ждать.

 shared_stuff = (struct shared_use_st *)shared_memory;
 shared_stuff->written_by_you = 0;
 while (running) {
  if (shared_stuff->written_by_you) {
   printf("You wrote: %s", shared_stuff->some_text);
   sleep(rand() % 4);
   /* Заставляет другой процесс ждать нас! */
   shared_stuff->written_by_you = 0;
   if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
    running = 0;
   }
  }
 }

5. В заключение совместно используемая память отсоединяется и удаляется.

 if (shmdt(shared_memory) == -1) {
  fprintf(stderr, "shmdt failedn");
  exit(EXIT_FAILURE);
 }
 if (shmctl(shmid, IPC_RMID, 0) == -1) {
  fprintf(stderr, "shmctl(IPC_RMID) failedn");
  exit(EXIT_FAILURE);
 }
 exit(EXIT_SUCCESS);
}

6. Вторая программа shm2.c — поставщик; она позволяет вводить данные для потребителей. Программа очень похожа на shm1.c и выглядит следующим образом.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"
int main() {
 int running = 1;
 void *shared_memory = (void *)0;
 struct shared_use_st *shared_stuff;
 char buffer[BUFSIZ];
 int shmid;
 shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
 if (shmid == -1) {
  fprintf(stderr, "shmget failedn");
  exit(EXIT_FAILURE);
 }
 shared_memory = shmat(shmid, (void *)0, 0);
 if (shared_memory == (void *)-1) {
  fprintf(stderr, "shmat failedn");
  exit(EXIT_FAILURE);
 }
 printf("Memory attached at %Xn", (int)shared_memory);
 shared_stuff = (struct shared_use_st *)shared_memory;
 while (running) {
  while (shared_stuff->written_by_you == 1) {
   sleep(1);
   printf("waiting for client...n");
  }
  printf("Enter same text: ");
  fgets(buffer, BUFSIZ, stdin);
  strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
  shared_stuff->written_by_you = 1;
  if (strncmp(buffer, "end", 3) == 0) {
   running = 0;
  }
 }
 if (shmdt(shared_memory) == -1) {
  fprintf(stderr, "shmdt failedn");
  exit(EXIT_FAILURE);
 }
 exit(EXIT_SUCCESS);
}

Когда вы выполните эти программы, то получите образец вывода, подобный следующему:

$ ./shm1 &
[1] 294
Memory attached at 40017000
$ ./shm2
Memory attached at 40017000
Enter some text: hello
You wrote: hello
waiting for client...
waiting for client...
Enter some text: Linux!
You wrote: Linux!
waiting for client...
waiting for client...
waiting for client...
Enter some text: end
You wrote: end
$

Как это работает

Первая программа shm1 создает сегмент совместно используемой памяти и затем присоединяет его к своему адресному пространству. Вы накладываете структуру shared_use_st на начальную область совместно используемой памяти. У нее есть флаг written_by_you, который устанавливается, когда данные доступны. Если флаг установлен, программа считывает текст, выводит его и сбрасывает флаг, чтобы показать, что данные прочитаны. Для корректного выхода из цикла примените специальную строку end. Далее программа отсоединяет сегмент совместно используемой памяти и удаляет его.

Вторая программа shm2 получает и присоединяет тот же самый сегмент совместно используемой памяти, поскольку она применяет тот же ключ 1234. Затем она просит пользователя ввести текст. Если флаг written_by_you установлен, shm2 знает, что клиентский процесс еще не считал предыдущую порцию данных и ждет завершения чтения. Когда другой процесс очищает флаг, shm2 записывает новые данные и устанавливает флаг. Она также пользуется магической строкой end для завершения записи и отсоединения сегмента совместно используемой памяти.

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

Оглавление книги

Оглавление статьи/книги

Генерация: 1.594. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз