Каждый совместно используемый сегмент должен явно освобождаться с помощью функции shmctl(), чтобы случайно не был превышен системный лимит на общее число таких сегментов. Функции exit()и exec()отключают сегменты, но не освобождают их.
Описание других операций, выполняемых над совместно используемыми сегментами памяти, можно найти на man-странице функции shmctl().
Программа, приведенная в листинге 5.1, иллюстрирует методику совместного использования памяти.
Листинг 5.1. ( shm.c ) Пример совместного использования памяти
#include
#include
#include
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;
}
Команда 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
Благодаря совместному использованию памяти можно организовать быстрое двустороннее взаимодействие произвольного числа процессов. Любой пользователь сможет получать доступ к сегментам памяти для чтения/записи, но для этого программа должна следовать определенным правилам, позволяющим избегать конкуренции (чтобы, например, информация не оказалась перезаписанной до того, как будет прочитана). К сожалению, 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). В отличие от совместно используемых сегментов памяти, удаляемая группа семафоров немедленно освобождается.
Читать дальше