Шаги в установлении связи через неименованный канал
Хотя канал создается одним процессом, он редко используется только этим процессом. Каналы обычно используются для связи между двумя процессами – родительским и дочерним, двумя дочерними процессами. На рис. 11.3 показаны шаги в установлении связи между двумя процессами B и C, являющимися потомками одного родительского процесса A, при помощи неименованного канала.
1. Процесс A создает канал и получает два дескриптора (рис. 11.3, а).
2. Процесс A создает два дочерних процесса B и C, которые наследуют оба дескриптора канала (рис. 11.3, б).
3. Каждый процесс закрывает те концы канала, которые ему не нужны (рис.11.3, в).
4. Процесс B может писать в канал, процесс C может читать из канала.
Ограничения неименованных каналов
1. Однонаправленный.
2. Процессы должны быть связаны.
3. Каналы временные, т.е. существуют, пока используются хотя бы одним процессом.
4. Чтение данных приводит к их удалению из канала, поэтому каналы не могут использоваться для широковещательной передачи.
5. Данные – поток байтов, поэтому границы сообщений неизвестны.
6. При наличии нескольких получателей нельзя направить данные определенному получателю. При наличии нескольких отправителей нельзя определить отправителя данных.
Ограничения 2 и 3 отсутствуют в именованных каналах.
Именованные каналы
Именованные каналы, или FIFO, схожи с неименованными, но могут использоваться несвязанными процессами и являются постоянными объектами, поскольку имеют имя в файловой системе (ФС). По завершении использования их необходимо удалять явно.
Использование именованных каналов
Один процесс создает FIFO. Затем процессы, которые будут FIFO использовать, должны открыть FIFO либо на запись либо на чтение, а после использования – закрыть. После закрытия FIFO последним из процессов информация в FIFO сбрасывается операционной системой.
Синхронизирующие свойства именованных каналов
Такие же, как и у неименованных каналов плюс дополнительно: открытие FIFO для чтения блокируется, пока какой-либо другой процесс не откроет его для записи, и наоборот.
Системные вызовы для работы с именованными каналами
Именованный канал создается при помощи функции mkfifo.
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
Функция mkfifo возвращает 0 при успешном выполнении, -1 – при возникновении ошибки. Здесь pathname – полное имя файла, которое и будет именем именованного канала. Аргумент mode указывает битовую маску разрешений доступа к файлу, аналогично второму аргументу функции open.
Функция mkfifo действует как функция open, вызванная с аргументом O_CREAT | O_EXCL. Это означает, что создается новый канал FIFO или возвращается ошибка EEXIST, в случае если канал с заданным полным именем уже существует.
После создания именованный канал должен быть открыт на чтение или запись с помощью либо функции open, либо одной из стандартных функций открытия файлов из библиотеки ввода-вывода (например, fopen). FIFO может быть открыт либо только на чтение, либо только на запись. Нельзя открывать канал на чтение и запись, поскольку именованные каналы могут быть только односторонними.
Очереди сообщений
Три механизма межпроцессного взаимодействия в UNIX (очереди сообщений, семафоры, разделяемая память) имеют общую особенность. Объект межпроцессного взаимодействия идентифицируется уникальным числовым ключом (можно провести аналогию с именем файла), операция xxget возвращает идентификатор объекта, с которым затем работают как с дескриптором файла (см. табл. 11.1).
Таблица 11.1
Системные вызовы для работы с очередями сообщений, семафорами и разделяемой памятью
Очереди сообщений | Семафоры | Разделяемая память | Назначение |
msgget | semget | shmget | Создать или получить доступ к объекту межпроцессного взаимодействия |
msgsnd msgrsv | semop | shmat shmdt | Основные операции |
msgctl | semctl | shmctl | Операции управления |
Время жизни всех трех перечисленных выше объектов совпадает со временем жизни ядра, т.е. они существуют до перезагрузки ОС. Информацию об этих объектах можно получить командой
ipcs -b
Очереди сообщений могут осуществлять двустороннюю связь между процессами, они обеспечивают границы между сообщениями. Очередь сообщений можно рассматривать как связный список сообщений. Одни процессы помещают сообщения в очередь, другие – извлекают их оттуда. Сообщения можно читать, и не извлекая из очереди. Каждое сообщение представляет собой запись. Сообщению присваивается тип (приоритет) его отправителем.
В отличие от каналов, куда нельзя произвести запись, пока не появится считывающий данные процесс, для записи сообщения в очередь не требуется наличия ожидающего его процесса. Очереди сообщений позволяют считывать собщения только определенного типа, т.е. позволяют осуществлять чтение, напрример, сообщений только от определенного клиента, или сообщений только определенного приоритета (типа).
Очереди сообщений обладают временем жизни ядра, т.е. существуют до перезагрузки системы. Следовательно, процесс может записать в очерель сообщения; затем они могут быть получены другим процессом в любое время, даже если первый процесс завершит свою работу.
Разделяемая память
Разделяемая память является наиболее быстрым средством межпроцессного взаимодействия. После отображения памяти в адресное пространство процессов, совместно ее использующих, для передачи данных между процессами больше не требуется участие ядра.
Ниже перечислены шаги для создания и использования разделяемой памяти.
1. Один процесс создает сегмент разделяемой памяти. Другие процессы получают идентификатор созданного сегмента.
2. Каждый процесс, используя полученный в шаге 1 идентификатор, добавляет сегмент разделяемой памяти в свое адресное пространство.
3. Теперь каждый процесс может обращаться к сегменту разделяемой памяти с операциями записи и чтения. Необходима синхронизировать доступ к разделяемой памяти для избежания гонок.
4. После работы с разделяемой памятью каждый процесс открепляет сегмент разделяемой памяти от своего адресного пространства.
В таблице 11.1 эти шаги проиллюстрированы на примере.
Таблица 11.1
Пример использования разделяемой памяти
№ шага | Процесс A | Процесс B |
key_t key1=123; int id1; char *ptr1; id1=shmget(key1,size,IPC_CREAT|0666); | key_t key2=123; int id2; char *ptr2; id2=shmget(key2,size,0); | |
ptr1=shmat(id1,0,0); | ptr2=shmat(id2,0,0); | |
ptr1='A'; /* запись символа */ | putchar(*ptr2); /* печать символа*/ | |
shmat(ptr1); | shmat(ptr2); |
Вопросы для самоконтроля
1. Перечислите механизмы межпроцессного взаимодействия в ОС UNIX.
2. Что такое неименованный канал?
3. Перечислите синхронизирующие свойства неименованного канала?
4. При помощи какого системного вызова создается неименованный канал?
5. Могут ли взаимодействовать при помощи неименованного канала два неродственных процесса?
6. Как при помощи неименованного канала осуществить двустороннюю передачу информации?
7. Могут ли писать в неименованный канал несколько процессов?
8. Когда неименованный канал прекращает свое существование?
9. Что происходит с данными после их чтения из неименованного канала?
10. Можно ли организовать широковещательную рассылку информации при помощи неименованного канала?
11. Как процесс, читающий из неименованного канала узнает, что пишущий процесс больше не будет помещать в канал данные?
12. Что такое именованный канал?
13. Каким системным вызовом создается именованный канал?
14. В чем отличия именованного канала от неименованного канала?
15. Перечислите синхронизирующие свойства именованных каналов.
16. Сколько времени хранится информация в именованном канале?
17. Что такое очередь сообщений?
18. Что такое разделяемая память?
19. Перечислите системные вызовы для работы с разделяемой памятью.
Тема 12. Передача сообщений