Конвейеры (программные каналы)


Конвейер (pipe – программный канал (связи), или, как его иногда называют, транспортер) является средством, с помощью которого можно производить об­мен данными между процессами. Принцип работы конвейера основан на меха­низме ввода/вывода, который используется для работы с файлами в UNIX, то есть задача, передающая информацию, действует так, как будто она записывает данные в файл, в то время как задача, для которой предназначается эта инфор­мация, читает её из этого файла. Операции записи и чтения осуществляются не записями, как это делается в обычных файлах, а потоком байтов, как это было принято в UNIX-системах. Таким образом, функции, с помощью которых вы­полняется запись в канал и чтение из него, являются теми же самыми, что и при работе с файлами. По сути, канал представляет собой поток данных между дву­мя (или более) процессами. Это упрощает программирование и избавляет про­граммистов от использования каких-то новых механизмов. На самом деле конвейеры не являются файлами на диске, а представляют собой буферную память, работающую по принципу FIFO, то есть по принципу обычной очереди. Однако не следует путать конвейеры с очередями сообщений; последние реализуются иначе и имеют другие возможности.

Конвейер имеет определенный размер1, который не может превышать 64 Кбайт, и работает циклически. Вспомните реализацию очереди на массивах, когда име­ются указатели начала и конца очереди, которые перемещаются циклически по массиву. Имеется некий массив и два указателя: один показывает на первый элемент (назовем его условно, head), а второй – на последний (назовем его tail).

В начальный момент оба указателя равны нулю. Добавление самого первого эле­мента в пустую очередь приводит к тому, что указатели head и tail принимают значение, равное 1 (в массиве появляется первый элемент). В последующем добавление нового элемента вызывает изменение значения второго указателя, поскольку он отмечает расположение именно последнего элемента очереди. Чтение (и удаление) элемента (читается и удаляется всегда первый элемент из создан­ной очереди) приводит к необходимости модифицировать значение указателя head. В результате операций записи (добавления) и чтения (удаления) элемен­тов в массиве, моделирующем очередь элементов, указатели будут перемещаться от начала массива к его концу. При достижении указателем значения индекса последнего элемента массива значение указателя вновь становится единичным (если при этом не произошло переполнение массива, то есть количество элемен­тов в очереди не стало больше числа элементов в массиве). Можно сказать, что мы как бы замыкаем массив в кольцо, организуя круговое перемещение указателей head и tail, которые отслеживают первый и последний элементы в очереди. Ска­занное проиллюстрировано на рис. 6.5. Именно так и функционирует конвейер.


Рис. 6.5. Организация очереди на массиве

Как информационная структура канал описывается идентификатором, размером и двумя указателями. Конвейеры представляют собой системный ресурс. Чтобы начать работу с конвейером, процесс сначала должен заказать его у операционной системы и получить в своё распоряжение. Процессы, знающие иденти­фикатор конвейера, могут через него обмениваться данными.

Теперь рассмотрим основные системные запросы для работы с ними. В качестве примера возьмем вызовы из API OS/2 (в следующем разделе мы ими воспользу­емся). Традиционные вызовы для работы с каналами (конвейерами) приведены в разделе, где описывается архитектура системы UNIX (см. главу 8). Итак:

¨ Функция создания конвейера:

DosCreatePipe (&ReadHand1e, &WriteHandle, PipeSize);

где ReadHandle – описатель для чтения из конвейера, WriteHandle – описатель для записи в конвейер, PipeSize – размер конвейера.

¨ Функция чтения из конвейера:

DosRead (&ReadHandle, (PVOID)&Inform, sizeof(Inform), &BytesRead);

где ReadHandle – описатель для чтения из конвейера, Inform – переменная лю­бого типа, sizeof( Inform) – размер переменной Inform, BytesRead – количество прочитанных байтов. Данная функция при обращении к пустому конвейеру будет ожидать, пока в конвейере не появится информация для чтения.

¨ Функция записи в конвейер:

DosWrite (&WriteHand1e, (PVOID)&Inform, s1zeof( Inform), &BytesWrite);

где WriteHandle – описатель для записи в конвейер, BytesWrite – количество записанных байтов.

Читать из конвейера может только тот процесс, который знает идентификатор соответствующего конвейера. При работе с конвейером данные непосредственно помещаются в него. Ещё раз отметим, что из-за ограничения на размер конвейе­ра программисты сталкиваются и с ограничениями на размеры передаваемых че­рез него сообщений.

Очереди сообщений

Очереди сообщений (Queue) являются более сложным методом связи между взаимодействующими процессами по сравнению с каналами. С помощью очере­дей также можно из одной или нескольких задач независимым образом посы­лать сообщения некоторой задаче-приёмнику. При этом только процесс-приём­ник может читать и удалять сообщения из очереди, а процессы-клиенты имеют право лишь помещать в очередь свои сообщения. Таким образом, очередь рабо­тает только в одном направлении. Если же необходима двухсторонняя связь, то можно создать две очереди.

Работа с очередями сообщений имеет много отличий от работы с конвейерами. Во-первых, очереди сообщений предоставляют возможность использовать несколько дисциплин обработки сообщений:

¨ FIFO – сообщение, записанное первым, будет первым и прочитано;

¨ LIFO – сообщение, записанное последним, будет прочитано первым;

¨ приоритетный – сообщения читаются с учётом их приоритетов;

¨ произвольный доступ, то есть можно читать любое сообщение, тогда как канал обеспечивает только дисциплину FIFO.

Во-вторых, если при чтении сообщения из канала (конвейера) оно удаляется из него, то при чтении сообщения из очереди этого не происходит, и сообщение при желании может быть прочитано несколько раз,

В третьих, в очередях присутствуют не непосредственно сами сообщения, а толь­ко их адреса в памяти и размер. Эта информация размещается системой в сегменте памяти, доступном для всех задач, общающихся с помощью данной очереди.

Каждый процесс, использующий очередь, должен предварительно получить раз­решение на использование общего сегмента памяти с помощью системных запросов API, ибо очередь – это системный механизм и для работы с ним требуются системные ресурсы и, соответственно, обращение к самой ОС. Во время чтения из очереди задача-приёмник пользуется следующей информацией:

¨ идентификатор процесса (PID – process ID), который передал сообщение;

¨ адрес и длина переданного сообщения;

¨ ждать или нет, если очередь пуста;

¨ приоритет переданного сообщения;

¨ номер освобождаемого семафора, когда сообщение передаётся в очередь.

Наконец, приведём перечень основных функций, управляющих работой очереди (без подробного описания передаваемых параметров), поскольку в различных ОС обращения к этим функциям могут существенно различаться:

¨ CreateQueue – создание новой очереди;

¨ OpenQueue – открытие существующей очереди;

¨ ReadQueue – чтение и удаление сообщения из очереди;

¨ PeekQueue – чтение сообщения без его последующего удаления из оче­реди;

¨ WriteQueue – добавление сообщения в очередь;

¨ CloseQueue – завершение использования очереди;

¨ PurgeQueue – удаление из очереди всех сообщений;

¨ QueryQueue – определение числа элементов в очереди.



Дата добавления: 2022-02-05; просмотров: 257;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.009 сек.