- •Часть 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
13.6. Некоторые свойства именованных и неименованных каналов
Некоторые свойства именованных и неименованных каналов, относящиеся к их открытию, а также чтению и записи данных, заслуживают более пристального внимания. Прежде всего, перевести дескриптор в неблокируемый режим можно двумя способами.
При вызове функции open указать флаг O_NONBLOCK. Например, первый вызов open в листинге 13.5 мог бы выглядеть так:
if ((writefd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0)) < 0)
Если дескриптор уже открыт, можно использовать функцию fcntl для включения флага O_NONBLOCK. Этот прием нужно применять для программных каналов, поскольку для их открытия функцию open не вызывают и нет возможности указать флаг O_NONBLOCK при ее вызове. Используя fcntl, мы сначала получаем текущий набор установленных флагов файла с помощью операции F_GETFL, затем добавляем к нему с помощью побитового логического или (OR) флаг O_NONBLOCK и записываем новый набор флагов с помощью операции F_SETFL:
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
{
fprintf(stderr, "Ошибка вызова функции fcntl, операция F_GETFL:
%s\n", strerror(errno));
exit(1);
}
flags |= O_NONBLOCK;
if ((flags = fcntl(fd, F_SETFL, flags)) < 0)
{
fprintf(stderr, "Ошибка вызова функции fcntl, операция F_SETFL:
%s\n", strerror(errno));
exit(1);
}
Будьте аккуратны с программами, которые просто устанавливают требуемый флаг, поскольку при этом сбрасываются все прочие флаги состояния:
/* неправильное отключение блокировки */
if ((flags = fcntl(fd, F_SETFL, O_NONBLOCK)) < 0)
{
fprintf(stderr, "Ошибка вызова функции fcntl, операция F_SETFL:
%s\n", strerror(errno));
exit(1);
}
Таблица 13.1 иллюстрирует действие флага, отключающего блокировку, при открытии каналов, а также при попытках чтения данных из пустых программных каналов или FIFO.
Таблица 13.1
Действие флага O_NONBLOCKна именованные и неименованные каналы
|
Операция |
Наличие открытых каналов |
Блокировка включена (по умолчанию) |
Флаг O_NONBLOCK установлен |
|
Открытие (open) FIFO только для чтения |
FIFO открыт для записи |
Возвращается код успешного завершения операции |
Возвращается код успешного завершения операции |
|
Открытие (open) FIFO только для чтения |
FIFO не открыт для записи |
Процесс (поток) блокируется, пока FIFO не будет открыт для записи |
Возвращается код успешного завершения операции |
|
Открытие (open) FIFO только для записи |
FIFO открыт для чтения |
Возвращается код успешного завершения операции |
Возвращается код успешного завершения операции |
|
Открытие (open) FIFO только для записи |
FIFO не открыт для чтения |
Процесс (поток) блокируется, пока FIFO не будет открыт для чтения |
Возвращается ошибка с кодом ENXIO |
|
Чтение (read) из пустого программного канала или FIFO |
Программный канал или FIFO открыт для записи |
Процесс (поток) блокируется, пока в программный канал или FIFO не будут помещены данные или они не будут закрыты всеми процессами, которыми они были открыты для записи |
Возвращается ошибка с кодом EAGAIN |
|
Чтение (read) из программного канала или FIFO |
Программный канал или FIFO не открыт для записи |
Функция readвозвращает 0 (признак конца файла) |
Функция readвозвращает 0 (признак конца файла) |
|
Запись (write) в программный канал или FIFO |
Программный канал или FIFO открыт для чтения |
(См. в тексте) |
(См. в тексте) |
|
Запись (write) в программный канал или FIFO |
Программный канал или FIFO не открыт для чтения |
Процессу (потоку) посылается сигнал SIGPIPE |
Процессу (потоку) посылается сигнал SIGPIPE |
Запомните несколько дополнительных правил, действующих при чтении и записи данных в программные каналы или FIFO.
При попытке считать больше данных, чем в данный момент содержится в программном канале или FIFO, возвращается только имеющийся объем данных. Нужно предусмотреть обработку ситуации, в которой функция read возвращает меньший объем данных, чем тот, который был запрошен.
Если количество байтов, переданных для записи функции write, не превышает значения PIPE_BUF (ограничение, устанавливаемое стандартом Posix, о котором более подробно рассказывается в разделе 13.9), то ядро гарантирует атомарность операции записи. Это означает, что если два процесса запишут данные в программный канал или FIFO приблизительно одновременно, то в буфер будут помещены сначала все данные от первого процесса, а затем – от второго, или наоборот. Данные от двух процессов при этом не будут смешиваться. Однако если количество записываемых байтов превысит значение PIPE_BUF, то атомарность операции записи не гарантируется.
Установка флага O_NONBLOCK не влияет на атомарность операции записи в программный канал или FIFO – она определяется исключительно объемом записываемых данных в сравнении с величиной PIPE_BUF. Однако если для программного канала или FIFO блокировка отключена, то значение, возвращаемое функцией write, зависит от количества записываемых байтов и наличия свободного места в программном канале или FIFO. Если количество записываемых байтов не превышает величины PIPE_BUF, то:
если в программном канале или FIFO достаточно места для записи требуемого количества данных, то они будут переданы все сразу;
если в программном канале или FIFO недостаточно места для записи требуемого объема данных, происходит немедленное завершение работы функции write с возвратом ошибки с кодом EAGAIN. Поскольку флаг O_NONBLOCK установлен, процесс не может быть заблокирован, но в то же время ядро не может принять лишь часть данных, так как при этом невозможно гарантировать атомарность операции записи. Поэтому ядро возвращает ошибку, сообщающую процессу о необходимости попытаться произвести запись еще раз.
Если количество записываемых байтов превышает значение PIPE_BUF, то:
если в программном канале или FIFO есть место хотя бы для одного байта, ядро передает в буфер ровно столько данных, сколько туда может поместиться, и это переданное количество байтов возвращается функцией write;
если в программном канале или FIFO свободное место отсутствует, то функция write немедленно завершает работу, возвращая ошибку с кодом EAGAIN.
При записи в программный канал или FIFO, не открытый для чтения, ядро посылает сигнал SIGPIPE, при этом:
если процесс не перехватывает и не игнорирует сигнал SIGPIPE, выполняется действие по умолчанию – завершение работы процесса;
если процесс игнорирует сигнал SIGPIPE или перехватывает его и возвращается из подпрограммы-обработчика, функция write возвращает ошибку с кодом EPIPE.
