- •Часть 4. Локальное взаимодействие процессов
- •Глава 16. Блокирование записей 89
- •12.2. Процессы, потоки и общий доступ к информации
- •12.3. Живучесть объектов ipc
- •12.4. Пространства имен
- •12.5. Действие команд fork, exec и exit на объекты ipc
- •12.6. Комментарии к примерам ipc
- •12.7. Выводы по главе 12
- •12.8. Упражнения по главе 12
- •Глава 13. Именованные и неименованные каналы
- •13.1. Введение
- •13.2. Приложение типа клиент-сервер
- •13.3. Программные каналы
- •13.4. Функции popen и pclose
- •13.5. Именованные каналы (fifo)
- •13.6. Некоторые свойства именованных и неименованных каналов
- •13.7. Один сервер, несколько клиентов
- •13.8. Последовательные и параллельные серверы
- •13.9. Ограничения программных каналов и fifo
- •13.10. Выводы по главе 13
- •13.11. Упражнения по главе 13
- •Глава 14. Программные потоки
- •14.1. Введение
- •14.2. Концепция потоков
- •14.3. Идентификация потоков
- •14.4. Создание потока
- •14.5. Завершение потока
- •Функции управления процессами и потоками
- •14.6. Установка атрибутов потока
- •14.7. Реентерабельность
- •Альтернативные версии функций, безопасные в многопоточной среде
- •14.8. Локальные данные потоков
- •14.9. Принудительное завершение потоков
- •Некоторые точки выхода, определенные стандартом Posix.1
- •14.10. Потоки и сигналы
- •14.11. Выводы по главе 14
- •14.12. Упражнения по главе 14 Глава 15. Средства синхронизации потоков
- •15.1. Введение
- •15.2. Взаимные исключения: установка и снятие блокировки
- •15.2.1. Схема производитель-потребитель
- •15.2.2. Блокирование и опрос
- •15.2.3. Предотвращение тупиковых ситуаций
- •15.3. Условные переменные
- •15.3.1. Ожидание и сигнализация
- •15.3.2. Исключение состояния гонок
- •15.4. Блокировки чтения-записи
- •15.5. Атрибуты средств синхронизации потоков
- •15.5.1. Атрибуты взаимных исключений
- •Поведение взаимных исключений различных типов
- •15.5.2. Атрибуты условных переменных
- •15.5.3. Атрибуты блокировок чтения-записи
- •15.6. Выводы по главе 15
- •15.7. Упражнения по главе 15
- •Глава 16. Блокирование записей
- •16.1. Введение
- •16.2. Блокирование записей и файлов
- •16.3. Блокирование записей с помощью fcntl по стандарту Posix
- •16.4. Рекомендательная блокировка
- •16.5. Обязательная блокировка
- •16.6. Приоритет чтения и записи Выводы по главе 16
- •Упражнения по главе 16 Глава 17. System V ipc
- •17.1. Введение
- •17.2. Ключи типа key_t и функция ftok
- •17.3. Структура ipc_perm
- •17.4. Создание и открытие каналов ipc
- •17.5. Разрешения ipc
- •17.6. Программы ipcs и ipcrm
- •17.7. Ограничения ядра
- •17.8. Выводы по главе 17
- •17.9. Упражнения по главе 17
- •Глава 18. Очереди сообщений System V
- •18.1. Введение
- •18.2. Функция msgget
- •18.3. Функция msgsnd
- •18.4. Функция msgrcv
- •18.5. Функция msgctl
- •18.6. Пример программы клиент-сервер
- •18.7. Мультиплексирование сообщений
- •18.7.1. Пример: одна очередь на приложение
- •18.7.2. Пример: одна очередь для каждого клиента
- •18.8. Ограничения, накладываемые на очереди сообщений
- •18.9. Выводы по главе 18
- •18.10. Упражнения по главе 18
- •Глава 19. Семафоры System V
- •19.1. Введение
- •19.2. Функция semget
- •19.3. Функция semop
- •19.4. Функция semctl
- •19. . Ограничения семафоров System V
- •19. . Выводы по главе 19
- •19. . Упражнения по главе 19 Глава 20. Введение в разделяемую память
- •20.1. Введение
- •20.2. Функции mmap, munmap и msync
- •20.3. Увеличение счетчика в отображаемом в память файле
- •20.4. Неименованное отображение в память
- •20.5. Обращение к объектам, отображенным в память
- •20.6. Выводы по главе 20
- •20.7. Упражнения по главе 20
- •Глава 21. Разделяемая память System V
- •21.1. Введение
- •21.2. Функция shmget
- •21.3. Функция shmat
- •21.4. Функция shmdt
- •21.5. Функция shmctl
- •21.6. Ограничения, накладываемые на разделяемую память
- •21.7. Выводы по главе 21
- •21.8. Упражнения по главе 21
19.2. Функция semget
Функция semgetсоздает набор семафоров или обеспечивает доступ к существующему набору.
#include <sys/sem.h>
int semget (key_t key, int nsems, int oflag);
/* возвращает неотрицательный идентификатор в случае успешного завершения, -1 – в случае ошибки */
Эта функция возвращает целое значение, называемое идентификатором семафора, которое затем используется при вызове функций semopиsemctl.
Аргумент nsemsзадает количество семафоров в наборе. Если мы не создаем новый набор, а устанавливаем доступ к существующему, этот аргумент может быть нулевым. Количество семафоров в уже созданном наборе изменить нельзя.
Аргумент oflagпредставляет собой комбинацию константSEM_RиSEM_Aиз табл.16.3. ЗдесьRобозначает Read (чтение), аA– Alter (изменение). К этим константам можно логически прибавитьIPC_CREATилиIPC_CREAT | IPC_EXCL, о чем мы уже говорили в связи с рис.16.2.
При создании нового семафора инициализируются следующие поля структуры semid_ds:
поля uidиcuidструктурыsem_permустанавливаются равными действующему идентификатору пользователя процесса, а поляgidиcgidустанавливаются равными действующему идентификатору группы процесса;
биты разрешений чтения-записи аргумента oflagсохраняются вsem_perm.mode;
поле sem_otimeустанавливается в 0, а полеsem_ctimeустанавливается равным текущему времени;
значение sem_nsemsустанавливается равнымnsems;
структуры semдля каждого из семафоров набора не инициализируются. Это происходит лишь при вызовеsemctlс командамиSETVALилиSETALL.
Инициализация значения семафора. К сожалению, значения семафоров не инициализируются при вызове функцииsemget, а устанавливаются только при вызове функцииsemctl(описание этой функции приводится ниже) с командамиSETVAL(установка значения одного из семафоров набора) иSETALL(установка значений всех семафоров набора).
Необходимость вызова двух функций для создания (semget) и инициализации (semctl) набора семафоров является неисправимым недостатком семафоров System V. Эту проблему можно решить частично, указывая флагиIPC_CREAT | IPC_EXCLпри вызовеsemget, чтобы только один процесс, вызвавшийsemgetпервым, создавал семафор и инициализировал его. Другие процессы при вызовеsemgetполучат ошибкуEEXIST, так что им придется еще раз вызватьsemget, уже не указывая флаговIPC_CREATилиIPC_EXCL.
Однако ситуация гонок все еще не устранена. Предположим, что два процесса попытаются создать и инициализировать набор семафоров с одним элементом приблизительно в один и тот же момент времени, причем оба они будут выполнять один и тот же фрагмент кода:
1 oflag = IPC_CREAT | IPC_EXCL | SVSEM_MODE;
2 if ((semid = semget(key, 1, oflag)) >= 0)
3 { /* успешное завершение, этот процесс должен инициализировать семафор */
4 arg.val = 1;
5 semctl(semid, 0, SETVAL, arg);
6 }
7 else /* семафор уже существует, поэтому просто открываем его */
8 if (errno == EEXIST)
9 semid = semget(key, 1, SVSEM_MODE);
10 else
11 {
12 fprintf(stderr, "Ошибка вызова функции semget: %s\n", strerror(errno));
13 exit(1);
14 }
15 semop(semid, ...); /* уменьшаем значение семафора на 1 */
При этом может возникнуть следующая ситуация.
Первый процесс выполняет строки 1–4, а затем останавливается ядром.
Ядро запускает второй процесс, который выполняет строки 1, 2, 7, 8, 9 и 15.
Хотя первый процесс, создавший семафор, и будет единственным процессом, который проинициализирует семафор, ядро может переключиться на второй процесс в промежутке между созданием и инициализацией семафора, и тогда второй процесс сможет обратиться к семафору (строка 15), который еще не был проинициализирован. Значение семафора для второго процесса после выполнения строки 15 останется неопределенным.
К счастью, существует способ исключить в данном случае ситуацию гонок. Стандарт гарантирует, что при создании набора семафоров поле sem_otimeструктурыsemid_dsинициализируется нулем. Это поле устанавливается равным текущему времени только при успешном вызове функцииsemop. Следовательно, второй процесс в приведенном выше примере должен просто вызватьsemctlс командойIPC_STATпосле второго вызоваsemget(строка 9). Затем этот процесс должен ожидать изменения значения поляsem_otimeна ненулевое, после чего он может быть уверен в том, что семафор был успешно проинициализирован другим процессом. Это значит, что процесс, создавший семафор, должен проинициализировать его с помощью успешного вызова функцииsemop, прежде чем другие процессы смогут воспользоваться этим семафором. Мы используем этот метод влистингах 10.37 и 11.6.
