создает новый совместно используемый сегмент памяти (или возвращает идентификатор существующего, если значение
shm_key
уже зарегистрировано в системе), доступный для чтения/записи только его владельцу:
int segment_id = shmget(shm_key, getpagesize,
IPC_CREAT | S_IRUSR | S_IWUSR);
В случае успешного завершения функция возвращает идентификатор сегмента. Если сегмент уже существует, проверяются нрава доступа к нему.
5.1.4. Подключение и отключение сегментов
Чтобы сделать сегмент памяти общедоступным, процесс должен
подключить его с помощью функции
shmat
. В первом ее аргументе передается идентификатор сегмента, возвращенный функцией
shmget
. Второй аргумент — это указатель, определяющий, где в адресном пространстве процесса необходимо создать привязку на совместно используемую область памяти. Если задать значение
NULL
, ОС Linux выберет первый доступный адрес. Третий аргумент может содержать следующие флаги.
■
SHM_RND
. Указывает на то, что адрес, заданный во втором параметре, должен быть округлен, чтобы стать кратным размеру страницы. Если этот флаг не указан, необходимо самостоятельно позаботиться о выравнивании сегмента по границе страницы.
■
SHM_RDONLY
. Указывает на то. что сегмент доступен только для чтения, но не для записи.
В случае успешного завершения функция возвращает адрес подключенного сегмента. Дочерний процесс, созданный функцией
fork
, унаследует этот адрес и в случае необходимости сможет отключить сегмент.
По завершении работы с сегментом его необходимо отключить с помощью функции
shmdt
. Ей следует передать адрес, возвращаемый функцией
shmat
. Если текущий процесс был последним, кто ссылался на сегмент, сегмент удаляется из памяти. Функции
exit
и
exec
автоматически отключают сегменты.
5.1.5. Контроль и освобождение совместно используемой памяти
Функция
shmctl
возвращает информацию о совместно используемом сегменте и способна модифицировать его. Первым параметром является идентификатор сегмента.
Чтобы получить информацию о сегменте, укажите в качестве второго параметра константу
IPC_STAT
, а в третьем параметре передайте указатель на структуру
shmid_ds
.
Чтобы удалить сегмент, передайте во втором параметре константу
IPC_RMID
, а в третьем параметре —
NULL
. Сегмент удаляется, когда последний подключивший его процесс отключает сегмент.
Каждый совместно используемый сегмент должен явно освобождаться с помощью функции
shmctl
, чтобы случайно не был превышен системный лимит на общее число таких сегментов. Функции
exit
и
exec
отключают сегменты, но не освобождают их.
Описание других операций, выполняемых над совместно используемыми сегментами памяти, можно найти на
man
– странице функции
shmctl
.
5.1.6. Пример программы
Программа, приведенная в листинге 5.1, иллюстрирует методику совместного использования памяти.
Листинг 5.1. (shm.c) Пример совместного использования памяти
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main {
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
const int shared_segment_size = 0x6400;
/*
Выделение совместно используемого сегмента. */
segment_id =
shmget(IPC_PRIVATE, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
/* Подключение сегмента. */
shared_memory = (char*)shmat(segment_id, 0, 0);
printf("shared memory attached at address %p\n",
shared_memory);
/* Определение размера сегмента. */
shmctl(segment_id, IPC_STAT, &shmbuffer);
segment_size = shmbuffer.shm_segsz;
printf("segment size: %d\n", segment_size);
/* Запись строки в сегмент. */
sprintf(shared_memory, "Hello, world.");
/* Отключение сегмента. */
shmdt(shared_memory);
/* Повторное подключение сегмента, но по другому адресу! */
shared_memory =
(char*)shmat(segment_id, (void*) 0x5000000, 0);
printf("shared memory reattached at address %p\n",
shared_memory);
/* Отображение строки, хранящейся в совместно используемой
памяти. */
printf("%s\n", shared_memory);
/* Отключение сегмента. */
shmdt(shared_memory);
/* Освобождение сегмента. */
shmctl(segment_id, IPC_RMID, 0);
return 0;
}
5.1.7. Отладка
Команда
ipcs
выдает информацию о взаимодействии процессов, включая сведения о совместно используемых сегментах (для этого следует задать флаг
– m
). Например, в показанном ниже случае сообщается о том, что используется один такой сегмент, с номером 1627649:
% ipcs -m
– ------- Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 1627649 user 640 25600 0
Если этот сегмент был по ошибке "забыт" какой-то программой, его можно удалить с помощью команды
ipcrm
:
% ipcrm shm 1627649
5.1.8. Проблема выбора
Благодаря совместному использованию памяти можно организовать быстрое двустороннее взаимодействие произвольного числа процессов. Любой пользователь сможет получать доступ к сегментам памяти для чтения/записи, но для этого программа должна следовать определенным правилам, позволяющим избегать конкуренции (чтобы, например, информация не оказалась перезаписанной до того, как будет прочитана). К сожалению, Linux не гарантирует монопольный доступ к сегменту, даже если он был создан с указанием флага