Описание других операций, выполняемых над совместно используемыми сегментами памяти, можно найти на
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 не гарантирует монопольный доступ к сегменту, даже если он был создан с указанием флага
IPC_PRIVATE
.
Кроме того, чтобы несколько процессов могли совместно работать с общим сегментом, они должны "договориться" о выборе одинакового ключа.
5.2. Семафоры для процессов
Как говорилось в предыдущем разделе, процессы должны координировать свои усилия при совместном доступе к памяти. Вспомните: в разделе 4.4.5, "Обычные потоковые семафоры", рассказывалось о семафорах, которые являются счетчиками, позволяющими синхронизировать работу потоков. В Linux имеется альтернативная реализация семафоров (иногда называемых семафорами System V), предназначенных для синхронизации процессов. Такие семафоры выделяются, используются и освобождаются подобно совместно используемым сегментам памяти. Для большинства случаев достаточно одного семафора, тем не менее они работают группами. В этом разделе мы опишем системные вызовы, позволяющие реализовать двоичный семафор.
5.2.1. Выделение и освобождение семафоров
Функции
semget
и
semctl
выделяют и освобождают семафоры, функционируя подобно функциям
shmget
и
shmctl
. Первым аргументом функции
semget
является ключ, идентифицирующий группу семафоров; второй аргумент — это число семафоров в группе; третий аргумент — флаги прав доступа, как в функции
shmget
. Функция
semget
возвращает идентификатор группы семафоров. Если задан ключ, принадлежащий существующей группе, будет возвращен ее идентификатор. В этом случае второй аргумент (число семафоров) может равняться нулю.
Семафоры продолжают существовать даже после того, как все работавшие с ними процессы завершились. Чтобы система не исчерпала лимит семафоров, последний процесс должен явно удалить группу семафоров. Для этого нужно вызвать функцию
semctl
, передав ей идентификатор группы, число семафоров в группе, флаг
IPC_RMID
и произвольное значение типа
union semun
(оно игнорируется). Значение EUID (эффективный идентификатор пользователя) процесса, вызвавшего функцию, должно совпадать с аналогичным значением процесса, создавшего группу семафоров (либо вызывающий процесс должен быть запущен пользователем
root
). В отличие от совместно используемых сегментов памяти, удаляемая группа семафоров немедленно освобождается.
В листинге 5.2 представлены функции, выделяющие и освобождающие двоичный семафор.
Листинг 5.2. (sem_all_deall.c) Выделение и освобождение двоичного семафора
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
/* Тип union semun необходимо определить самостоятельно. */