
- •Часть 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
16.2. Блокирование записей и файлов
Ядро Unix никак не интерпретирует содержимое файла, оставляя всю обработку записей приложениям, работающим с этим файлом. Тем не менее, для описания предоставляемых возможностей используется термин “блокировка записей”. В действительности приложение указывает диапазон байтов файла для блокирования или разблокирования. Сколько логических записей помещается в этот диапазон – значения не имеет.
16.3. Блокирование записей с помощью fcntl по стандарту Posix
Согласно стандарту Posix, для блокировки записей следует использовать функцию fcntl, с которой мы уже встречались в разделе 3.13. Для удобства изложения приведем определение этой функции еще раз.
#include <fcntl.h>
int fcntl (int filedes, int cmd, .../* struct flock *arg */);
/* возвращаемое значение зависит от аргумента cmd (см. ниже) в случае успеха, -1 – в случае ошибки */
Для блокировки записей используются три различных значения аргумента cmd. При передаче любого из этих значений функции необходимо, чтобы третий аргумент являлся указателем на структуруflock, основные поля которой суть следующие:
struct flock
{
short l_type; /* тип блокировки: F_RDLCK, F_WRLCK или F_UNLCK */
short l_whence; /* начало диапазона: SEEK_SET, SEEK_CUR или SEEK_END */
off_t l_start; /* относительное смещение в байтах */
off_t l_len; /* число байтов для блокировки; 0 означает до конца файла */
pid_t l_pid; /* идентификатор блокирующего процесса, возвращаемый F_GETLK */
};
Вот три возможные команды (значения аргумента cmd), используемые при блокировании записей:
F_SETLK – установка блокировки (l_type принимает значение либо F_RDLCK, либо F_WRLCK) или снятие блокировки (l_type принимает значение F_UNLCK), свойства которых определяются структурой flock, адрес которой содержит arg. Если процесс не может установить блокировку, то происходит немедленный возврат из вызова функции с ошибкой EACCES или EAGAIN;
F_SETLKW – эта команда идентична предыдущей. Однако при невозможности блокирования файла процесс приостанавливается до тех пор, пока блокировка не будет установлена (W в конце команды означает “wait”);
F_GETLK – проверка состояния блокировки. Если в данный момент блокировка не установлена, поле l_type структуры flock, на которую указывает arg, будет иметь значение F_UNLCK. В противном случае в структуре flock, адрес которой содержит arg, возвращается информация об установленной блокировке, включая идентификатор процесса, заблокировавшего файл.
Обратите внимание, что последовательный вызов F_GETLKиF_SETLKне является атомарной операцией. Если мы вызвалиF_GETLKи она вернула значениеF_UNLCKв полеl_type, это не означает, что немедленный вызовF_SETLKбудет успешным. Между этими двумя вызовами другой процесс мог уже заблокировать файл.
Причина, по которой была введена команда F_GETLK, – необходимость получения информации о блокировке в том случае, когдаF_SETLKвозвращает ошибку. Мы можем узнать, кто и каким образом заблокировал файл (на чтение или на запись). Но и в этом случае мы должны быть готовы к тому, чтоF_GETLKвозвратит результатF_UNLCK, поскольку между двумя вызовами функции другой процесс мог снять блокировку с файла.
Структура flockописывает тип блокировки (чтение или запись) и блокируемый диапазон. Подобно функцииlseek, начальное смещение, хранимое в полеl_start, может быть смещением относительно начала файла, текущего положения или конца файла, и интерпретируется в зависимости от значения поляl_whence(SEEK_SET,SEEK_CURилиSEEK_ENDсоответственно).
Поле l_lenуказывает размер блокируемого диапазона. Значение 0 соответствует блокированию отl_startдо конца файла. Проще всего заблокировать файл целиком, указав следующие значения:l_whence=SEEK_SET,l_start=0иl_len=0.
Блокировка может быть установлена либо на чтение, либо на запись, и для конкретного байта файла может быть задан только один тип блокировки. Более того, на конкретный байт может быть установлено несколько блокировок на чтение, но только одна блокировка на запись. Это соответствует тому, что говорилось о блокировках чтения-записи в предыдущем разделе. Естественно, при попытке установить блокировку на чтение для файла, открытого только на запись, будет возвращена ошибка.
Все блокировки, установленные конкретным процессом, снимаются при закрытии дескриптора файла этим процессом или по завершении его работы. Блокировки не наследуются дочерним процессом, порождаемым при вызове функции fork.
Блокировка записей не должна использоваться со стандартной библиотекой ввода-вывода, поскольку функции из этой библиотеки осуществляют внутреннюю буферизацию. С заблокированными файлами следует использовать функции readиwrite, чтобы не возникало неожиданных проблем.