Выбрать главу

 /* Определение размера сегмента. */

 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>