Программные каналы
9.6. Программные каналы
Программный канал по-английски называется pipe (труба), и это весьма удачное название. Канал действительно можно представить как трубопровод пневматической почты, проложенный между двумя процессами, как показано на Рисунок 9.1. По этому трубопроводу данные передаются от одного процесса к другому. Как и трубопровод, программный канал однонаправленный (хотя, например, в Unix одним системным вызовом создаются сразу два разнонаправленных канала). Как и трубопровод, программный канал имеет собственную емкость: данные записанные в канал, не обязательно должны немедленно выбираться не противоположном его конце, но могут накапливаться в канале, пока это позволяет его емкость. Как и трубопровод, канал работает по дисциплине FIFO: первый вошел - первый вышел.
Рисунок 9.1. Программные каналы
Из всех средств взаимодействия между процессами каналы (pipe) лучше всего вписываются в модель виртуальных коммуникационных портов. Канал для процесса практически аналогичен файлу. Специальные системные вызовы типа createPipe, openPipe используются для создания канала и получения доступа к каналу, а для работы с каналом используются те же вызовы read и write, что и для файлов, и даже закрытие канала выполняется файловым системным вызовом close. При создании канала для него создается дескриптор, как для открытого файла, что позволяет работать с ним далее, как с файлом. Канал, однако, представляет собой не внешние данные, а область памяти. Для канала выделяется память в системной области, что может ограничивать емкость канала.
Наиболее часто используются неименованные каналы, как средство связи между родителем и потомком. Операция создания неименованного канала возвращает два файловых манипулятора: для чтения и для записи. Процесс-предок передает эти манипуляторы процессу-потомку. Если связь между предком и потомком однонаправленная, то каждый из них закрывает канал по одному из манипуляторов. Например, если данные передаются только от предка к потомку, то предок после передачи манипуляторов закрывает канал на чтение, а потомок - на запись. Пример установления такой связи в ОС Unix приведен в главе 11, в фрагменте программы командного интерпретатора.
С точки зрения реализации канал представляет собой классический вариант задачи "производитель-потребитель": один процесс пишет в канал данные, другой - читает их из канала. Если при попытке записи данных в канал обнаруживается, что канал полон, пишущий процесс блокируется до освобождения места в канале, если при попытке чтения данных обнаруживается, что канал пуст - читающий процесс блокируется до появления в канале данных. Внутренним механизмом ОС, обеспечивающим синхронизацию в таких ситуациях, является, конечно же, семафор.
Именованные каналы представляют собой удобное средство клиент-серверных коммуникаций. Именованные каналы в некоторых ОС (например, OS/2) существенно отличаются от неименованных. Именованные каналы ориентированы в этих системах прежде всего на взаимодействие процессов в сетевой среде (или, точнее, для них прозрачно, находятся ли оба процесса на одном компьютере или в разных узлах сети). Такой канал двунаправленный, то есть, к нему возможны обращения и для чтения, и для записи. Данные в канале могут передаваться как потоком байт, так и сообщениями. В последнем случае каждое сообщение снабжается заголовком, в котором указывается его длина. К одному концу канала постоянно подключен процесс, его создавший - владелец или сервер, к другому концу могут подключаться различные процессы-клиенты, таким образом, обмен данными по именованному каналу между процессами, ни один из которых не является владельцем канала, невозможен. При создании канала (системный вызов createNamedPipe) указывается максимально возможное число клиентов, которые могут быть одновременно подключены к каналу (это число, впрочем, может и не ограничиваться). Когда владелец создает канал, канал находится в так называемом отключенном состоянии. Системным вызовом connectNamedPipe владелец переводит канал в ждущее состояние. Теперь процессы-клиенты могут подключаться к другому концу канала при помощи файловых системных вызовов openNamedPipe. Канал, открытый хотя бы одним клиентом, считается подключенным, сервер и подключенные клиенты могут работать с ним, используя вызовы файлового API. Клиенты отключаются от канала вызовом close, в распоряжении владельца есть вызов disconnectNamedPipe - разрыва канала. Помимо обычных вызовов файлового обмена, для работы с именованным каналом в составе API могут быть специальные системные вызовы, обеспечивающие выполнение сложных транзакций на именованном канале например: transactNamedPipe - взаимный обмен данными (вывод и ввод) за одну операцию, callNamedPipe - обеспечивает также открытие канала, взаимный обмен, закрытие канала. Кроме того, к именованному каналу или к нескольким именованным каналам может быть подключен семафор, который сбрасывается при изменении состояния канала (заполнен - не заполнен, пуст - не пуст).