Протокол rpc. Удаленные процедуры: вызов удаленных процедур, определение и особенности
Многие пользователи компьютерных систем слышали о таких понятиях, как удаленные процедуры, вызов удаленных процедур или RPC. Вот только далеко не все представляют себе, что это за технологии, как они работают и для чего нужны. А ведь многие из тех, кто отключил данную службу в Windows-системах, зачастую могут получать и ошибки, относящиеся к критическим сбоям. Об этом и многом другом далее пойдет речь.
Удаленный вызов процедур: что это?
Начать стоит с некоторых теоретических сведений. Как считается, удаленные процедуры (вызов удаленных процедур) - это механизм, позволяющий запускать или использовать какие-либо функции компьютерных систем в отличном от используемого терминала адресном пространстве. Проще говоря, это способ доступа к удаленному компьютеру, например, через локальную сеть или интернет-подключение.
Однако удаленные процедуры (вызов удаленных процедур), обозначаемые как RPC (сокращение от английского Remote Procedure Call), можно отнести не только к удаленным компьютерам. На локальном уровне такие технологии тоже применяются. В качестве простейшего примера можно привести вызов определенной функции одной программы из другого приложения посредством взаимодействия через специальные библиотеки.
Кроме того, абсолютно во всех версиях Windows имеется такая служба, а при ее отключении или отказе работоспособности модификация XP не работает вообще.
Принцип действия
Как правило, служба «Удаленный вызов процедур RPC» для работы в режиме клиент-сервер требует наличия как минимум двух основных компонентов: сетевого протокола для обмена данными и языка сериализации (перевода какого-то процесса или информационной структуры данных в битовую последовательность).
Архитектуры могут быть совершенно разными и отличающимися по своим возможностям. Но для обмена данными на так называемом транспортном уровне чаще всего применяются протоколы UDP и TCP, реже - HTTP.
Чтобы не вдаваться в технические аспекты, самым простым объяснением принципа работы таких технологий может стать следующие пример: клиентский процесс формирует запрос серверу с описанием выбранной процедуры с указанными параметрами и отправляет его, после чего сервер выполняет требуемую директиву и отправляет клиенту ответ, который отображается на клиентской машине. Однако сам серверный обработчик находится, так сказать, в режиме ожидания и активируется только в моменты получения клиентских запросов. При этом совсем не обязательно, чтобы выполнение схемы «запрос-ответ» производилось немедленно.
При этом максимальный эффект производительности достигается при обмене относительно малыми объемами данных и небольшим временем отклика компонентов, между которыми установлена интерактивная связь.
Удаленные процедуры (вызов удаленных процедур): характерные черты и реализации
Таким образом, можно выделить две главные особенности названных технологий:
- асимметричность (инициация выполнения удаленной процедуры только одной из сторон);
- синхронность (приостановка вызывающей процедуры с момента инициации запроса и возобновление после отправки ответа).
Что же касается реализаций, удаленные процедуры (вызов удаленных процедур) сегодня используют несколько базовых технологий, среди которых наиболее широко применяются следующие:
- DCE/RPC - бинарный протокол на основе TCP/IP, SMB/SIFC и т. д.;
- DCOM - объектно-ориентированное дополнение с возможностью передачи ссылок на объекты и вызовом методов их обработки;
- JSON-RPC - текстовый протокол, основанный на HTTP;
- .NET Remoting - бинарный протокол на основе UDP, TCP и HTTP;
- JAVA RMI;
- SOAP;
- XML RPC;
- SUN RPC;
- ZeroC ICE;
- Routix.RPC и др.
Проблемы и задачи
Теперь несколько слов о недостатках. Самая главная проблема, а соответственно, и задача реализации, состоит в том, что одна и та же операция вызова удаленной процедуры через узел службы «Удаленный вызов процедур» должна одновременно выполняться на разных машинах, причем зачастую с различными операционными системами, адресными пространствами и архитектурой. В процессе данные параметров должны копироваться с одного терминала на другой. Для этого используется не только транспортный протокол, но и сериализация, позволяющая преобразовать в байтовую последовательность разные типы данных.
Второй момент связан с тем, что удаленные процедуры (вызов удаленных процедур) используют не один процесс, как на локальном уровне, а два (на клиентской машине и на сервере). Посему аварийное завершение программы на одном из терминалов может вызвать такую же реакцию и на другом.
Наконец, одной из основных называется проблема совместимости ввиду неоднородности некоторых языков программирования, несмотря даже на установленные единые стандарты.
Основные типы подсистем
Удаленный вызов процедур Windows 10 или любой другой системы рангом ниже подразумевает использование специальных подсистем:
- транспортная подсистема, предназначенная для управления исходящими и входящими подключениями с гарантированной доставкой пакетов данных;
- пул-протоколов - концепция выполнения процедуры на вызываемом терминале;
- сериализация (маршалинг) - преобразование потоков данных в стандартные байтовые коды, независящие от архитектуры;
- шифрование отправляемых и принимаемых пакетов с наложением на них цифровой подписи;
- система аутентификации и авторизации.
Какие типы программ требуют выполнения RPC?
Если же говорить о том, какие программные модули операционных систем требуют держать службу RPC включенной, все их перечислить просто невозможно.
Но среди всем известных компонентов Windows-систем можно отметить службу факсов, службы криптографии, регистрацию ошибок, справку и поддержку, доступ к устройствам HID, службу сообщений (Messenger), администрирование дисков и логических разделов, управление съемными накопителями, аудиосистему, установщик Windows и еще бог весть что.
Думается, этого списка достаточно, чтобы понять, насколько многие компоненты системы, да и сам пользователь, зависимы от этой службы.
На что влияет RPC
Вообще, исходя из предыдущего описания, можно оценить влияние RPC. Так, например, известно немало случаев, когда при отключении этой службы полностью пропадал звук, невозможным оказывалось восстановление системы после критических сбоев или инициированное пользователем, «слетали» настройки беспроводной сети.
Но самое печальное состоит в том, что, если отключить удаленный вызов процедур RPC, иногда бывает невозможно даже получить доступ к основным настройкам системы, будь пользователь хоть трижды администратором на собственном терминале.
Можно ли отключить эту службу
По большому счету, многие пытались (и пытаются) службу «Удаленный вызов процедур» отключить. Делать это категорически запрещается. В общем-то, и сама система при осуществлении такой попытки сделать этого не даст, выдав соответствующее уведомление.
Но не все знают, что в разделе службы (services.msc) есть еще такая штука, как «Локатор удаленного вызова процедур RPC». Вот его-то как раз и можно отключить безболезненно для системы. Приложения, которые могут его использовать в своей работе, самостоятельно вызовут сервис при необходимости.
Устранение сбоев и ошибок
Наконец, посмотрим, что можно сделать, если выдается ошибка при удаленном вызове процедуры. В самом простом случае можно попытаться включить службу заново (если, конечно, получится).
Для этого в соответствующем разделе, где находится искомая служба, двойным кликом вызывается меню редактирования параметров, нажимается кнопка включения, а сам тип включения устанавливается на автоматический. Если проделать такую процедуру при стандартной загрузке системы не представляется возможным, можно попытаться произвести аналогичные действия в безопасном режиме. Некоторые специалисты советуют заодно на время проведения действий на всякий случай отключить антивирусное ПО.
Если это не поможет, но под рукой имеется установочный или восстановительный диск системы, можно запустить командную консоль с правами администратора (загружаться с диска не нужно) и прописать в ней такие команды:
- cd z:\i386 (Z - литера оптического привода) ;
- expand explorer.ex_ %TEMP%\explorer.exe;
- expand svchost.ex_ %TEMP%\svchost.exe.
После этого запускаем «Диспетчер задач» (Ctrl + Del + Alt или taskmgr в меню «Выполнить») и завершаем процесс Explorer.exe.
В «Диспетчере» останавливаем все процессы svhost.exe, после чего в течение 60 секунд нужно успеть в командой строке ввести строку copy %TEMP%\svchost.exe %systemroot%\system32 /y.
Наконец, если есть доступ к редактору системного реестра (regedit) восстановлен, нужно пройти по ветке HKCC через разделы SYSTEM и CurrentControlSet и добраться до параметра CSConfigFlags, изменив его значение на ноль.
Это далеко не все методы исправления ошибок, связанных с RPC. Дело в том, что, если эта служба повлекла за собой нарущения в работе других сервисов, возможно, сначала придется устранять проблемы с их работоспособностью, а только потом предпринимать какие-то действия в отношении RPC. И не всегда к вышеописанным параметрам и настройкам можно получить полный доступ. Если уж совсем ничего не получится, как это ни плачевно звучит, придется полностью переустанавливать операционную систему, хотя хочется надеяться, что до этого дело не дойдет.
Заключение
Вот кратко и все, что касается технологии и службы RPC. На самом деле все это выглядит намного сложнее, нежели было представлено в данном описании, и для полного понимания вопроса необходимо обладать хотя бы начальными знаниями. Но для того, чтобы иметь об RPC общее представление, этого пока достаточно.
Что же касается отключения, не вздумайте заниматься подобными вещами, иначе вся система откажет. Приведенные решение по исправлению сбоев обычно помогают, но полной гарантии все равно дать нельзя, ведь деактивация службы могла вызвать сбои и в других компонентах.
Лекция 4
4.1 Концепция удаленного вызова процедур
Идея вызова удаленных процедур (Remote Procedure Call - RPC) состоит в расширении хорошо известного и понятного механизма передачи управления и данных внутри программы, выполняющейся на одной машине, на передачу управления и данных через сеть. Средства удаленного вызова процедур предназначены для облегчения организации распределенных вычислений. Наибольшая эффективность использования RPC достигается в тех приложениях, в которых существует интерактивная связь между удаленными компонентами с небольшим временем ответов и относительно малым количеством передаваемых данных. Такие приложения называются RPC-ориентированными.
Характерными чертами вызова локальных процедур являются: асимметричность, то есть одна из взаимодействующих сторон является инициатором; синхронность, то есть выполнение вызывающей процедуры при останавливается с момента выдачи запроса и возобновляется только после возврата из вызываемой процедуры.
Реализация удаленных вызовов существенно сложнее реализации вызовов локальных процедур. Начнем с того, что поскольку вызывающая и вызываемая процедуры выполняются на разных машинах, то они имеют разные адресные пространства, и это создает проблемы при передаче параметров и результатов, особенно если машины не идентичны. Так как RPC не может рассчитывать на разделяемую память, то это означает, что параметры RPC не должны содержать указателей на ячейки нестековой памяти и что значения параметров должны копироваться с одного компьютера на другой. Следующим отличием RPC от локального вызова является то, что он обязательно использует нижележащую систему связи, однако это не должно быть явно видно ни в определении процедур, ни в самих процедурах. Удаленность вносит дополнительные проблемы. Выполнение вызывающей программы и вызываемой локальной процедуры в одной машине реализуется в рамках единого процесса. Но в реализации RPC участвуют как минимум два процесса - по одному в каждой машине. В случае, если один из них аварийно завершится, могут возникнуть следующие ситуации: при аварии вызывающей процедуры удаленно вызванные процедуры станут "осиротевшими", а при аварийном завершении удаленных процедур станут "обездоленными родителями" вызывающие процедуры, которые будут безрезультатно ожидать ответа от удаленных процедур.
Кроме того, существует ряд проблем, связанных с неоднородностью языков программирования и операционных сред: структуры данных и структуры вызова процедур, поддерживаемые в каком-либо одном языке программирования, не поддерживаются точно так же во всех других языках.
Эти и некоторые другие проблемы решает широко распространенная технология RPC, лежащая в основе многих распределенных операционных систем.
Базовые операции RPC
Чтобы понять работу RPC, рассмотрим вначале выполнение вызова локальной процедуры в обычной машине, работающей автономно. Пусть это, например, будет системный вызов
count=read (fd,buf,nbytes);
где fd – целое число;
buf – массив символов;
nbytes – целое число.
Чтобы осуществить вызов, вызывающая процедура заталкивает параметры в стек в обратном порядке. После того, как вызов read выполнен, он помещает возвращаемое значение в регистр, перемещает адрес возврата и возвращает управление вызывающей процедуре, которая выбирает параметры из стека, возвращая его в исходное состояние. Заметим, что в языке С параметры могут вызываться или по ссылке (by name), или по значению (by value). По отношению к вызываемой процедуре параметры-значения являются инициализируемыми локальными переменными. Вызываемая процедура может изменить их, и это не повлияет на значение оригиналов этих переменных в вызывающей процедуре.
Если в вызываемую процедуру передается указатель на переменную, то изменение значения этой переменной вызываемой процедурой влечет изменение значения этой переменной и для вызывающей процедуры. Этот факт весьма существенен для RPC.
Существует также другой механизм передачи параметров, который не используется в языке С. Он называется call-by-copy/restore и состоит в необходимости копирования вызывающей программой переменных в стек в виде значений, а затем копирования назад после выполнения вызова поверх оригинальных значений вызывающей процедуры.
Решение о том, какой механизм передачи параметров использовать, принимается разработчиками языка. Иногда это зависит от типа передаваемых данных. В языке С, например, целые и другие скалярные данные всегда передаются по значению, а массивы - по ссылке.
Идея, положенная в основу RPC, состоит в том, чтобы сделать вызов удаленной процедуры выглядящим по возможности также, как и вызов локальной процедуры. Другими словами - сделать RPC прозрачным: вызывающей процедуре не требуется знать, что вызываемая процедура находится на другой машине, и наоборот.
RPC достигает прозрачности следующим путем. Когда вызываемая процедура действительно является удаленной, в библиотеку помещается вместо локальной процедуры другая версия процедуры, называемая клиентским стабом (stub - заглушка). Подобно оригинальной процедуре, стаб вызывается с использованием вызывающей последовательности, так же происходит прерывание при обращении к ядру. Только в отличие от оригинальной процедуры он не помещает параметры в регистры и не запрашивает у ядра данные, вместо этого он формирует сообщение для отправки ядру удаленной машины.
Этапы выполнения RPC
Взаимодействие программных компонентов при выполнении удаленного вызова процедуры иллюстрируется рисунком 2.
Рисунок 2. Remote Procedure Call
После того, как клиентский стаб был вызван программой-клиентом, его первой задачей является заполнение буфера отправляемым сообщением. В некоторых системах клиентский стаб имеет единственный буфер фиксированной длины, заполняемый каждый раз с самого начала при поступлении каждого нового запроса. В других системах буфер сообщения представляет собой пул буферов для отдельных полей сообщения, причем некоторые из этих буферов уже заполнены. Этот метод особенно подходит для тех случаев, когда пакет имеет формат, состоящий из большого числа полей, но значения многих из этих полей не меняются от вызова к вызову.
Затем параметры должны быть преобразованы в соответствующий формат и вставлены в буфер сообщения. К этому моменту сообщение готово к передаче, поэтому выполняется прерывание по вызову ядра.
Когда ядро получает управление, оно переключает контексты, сохраняет регистры процессора и карту памяти (дескрипторы страниц), устанавливает новую карту памяти, которая будет использоваться для работы в режиме ядра. Поскольку контексты ядра и пользователя различаются, ядро должно точно скопировать сообщение в свое собственное адресное пространство, так, чтобы иметь к нему доступ, запомнить адрес назначения (а, возможно, и другие поля заголовка), а также оно должно передать его сетевому интерфейсу. На этом завершается работа на клиентской стороне. Включается таймер передачи, и ядро может либо выполнять циклический опрос наличия ответа, либо передать управление планировщику, который выберет какой-либо другой процесс на выполнение. В первом случае ускоряется выполнение запроса, но отсутствует мультипрограммирование.
На стороне сервера поступающие биты помещаются принимающей аппаратурой либо во встроенный буфер, либо в оперативную память. Когда вся информация будет получена, генерируется прерывание. Обработчик прерывания проверяет правильность данных пакета и определяет, какому стабу следует их передать. Если ни один из стабов не ожидает этот пакет, обработчик должен, либо поместить его в буфер, либо вообще отказаться от него. Если имеется ожидающий стаб, то сообщение копируется ему. Наконец, выполняется переключение контекстов, в результате чего восстанавливаются регистры и карта памяти, принимая те значения, которые они имели в момент, когда стаб сделал вызов receive.
Теперь начинает работу серверный стаб. Он распаковывает параметры и помещает их соответствующим образом в стек. Когда все готово, выполняется вызов сервера. После выполнения процедуры сервер передает результаты клиенту. Для этого выполняются все описанные выше этапы, только в обратном порядке.
Рисунок 3 показывает последовательность команд, которую необходимо выполнить для каждого RPC-вызова.
Рисунок 3. Этапы выполнения процедуры RPC
Цель данной статьи - обсудить терминологию. Статья - не о том, как и для чего, а только исключительно об использовании терминологии. Статья отражает мнение автора и не претендует на научность.
Вступление
Если вы работаете в области программирования распределенных систем или в интеграции систем , то большая часть изложенного здесь вам не в новинку.
Проблема возникает, когда встречаются люди, использующие разные технологии, и когда эти люди начинают технические разговоры. При этом часто возникает взаимное недопонимание, обусловленное терминологией. Я здесь попытаюсь свести воедино терминологии, используемые в разных контекстах.
Терминология
Четкой терминологии и классификации в этой области нет. Используемая ниже терминология является отражением модели, сложившейся у автора, то есть она строго субъективна. Любая критика и любые обсуждения приветствуются.
Я разделил терминологию на три области: RPC (Remote Procedure Call), Messaging и REST. Эти области имеют под собою исторические корни.
RPC
RPC технологии - наиболее старые технологии. Наиболее яркие представители RPC, это - CORBA и DCOM .
В те времена в основном приходилось связывать системы в быстрых и относительно надежных локальных сетях. Главная идея RPC была в том, чтобы сделать вызов удаленных систем очень похожим на вызов функций внутри программы. Вся механика удаленных вызовов пряталась от программиста. По крайней мере её пытались спрятать. Программисты во многих случаях вынуждены были работать на более глубоком уровне, где появлялись термины маршалинг (marshalling ) и unmarshalling (как это по-русски?), что по сути означало сериализацию. Обычные вызовы функций внутри процессов обрабатывались на вызывающей стороне в Proxy , а на стороне системы, выполняющей функцию, в Dispatcher . В идеале ни вызывающая система, ни обрабатывающая система не занимались тонкостями передачи данных между системами. Все эти тонкости сосредотачивались в связке Proxy - Dispatcher, код которых генерировался автоматически.
Поэтому вы не заметите, не должны заметить, никакой разницы между вызовом локальной функции и вызовом удаленной функции.
Сейчас наблюдается своеобразный ренесанс RPC, наиболее яркие представители которого: Google ProtoBuf, Thrift, Avro.
Messaging
С течением времени выяснилось, что попытка оградить программиста от того, что вызываемая функция все же отличается от локальной, не привела к желаемому результату. Детали реализации и принципиальные отличия распределенных систем были слишком велики, чтобы решаться с помощью автоматически генерируемого кода Proxy. Постепенно пришло понимание, что факт того, что системы связывает ненадежная, медленная, низкоскоростная среда, должен быть явно отражен в коде программы.
Появились технологии веб-сервисов . Мы стали говорить ABC: Address, Binding, Contract . Не совсем понятно, почему появились контракты, которые по сути являются Envelope (конвертами) для входных аргументов. Контракты чаще усложняют всю модель, чем упрощают ее. Но… неважно.
Теперь программист явным образом создавал сервис (Service ) или клиента (Client ), вызывающего сервис. Сервис представлял из себя набор операций (Operation ), каждая из которых на входе принимала запрос (Request ) и выдавала ответ (Response ). Клиент явным образом посылал (Sent ) запрос, сервис явным образом получал (Receive ) его и отвечал (Sent), высылая ответ. Клиент получал (Receive) ответ и на этом вызов завершался.
Так же, как и в RPC, где-то здесь работали Proxy и Dispatcher. И как прежде их код генерировался автоматически и программисту не надо было в нем разбираться. Разве только что, клиент явным образом использовал классы из Proxy.
Запросы и ответы явным образом преобразуются к формату, предназначенному для передачи по проводам. Чаще всего это массив байт. Преобразование называется Serialization
и Deserialization
и иногда прячется в коде Proxy.
Кульминация messaging проявилась в появлении парадигмы ESB (Enterprise Service Bus)
. Никто толком не может сформулировать, что это такое, но все сходятся на том, что данные по ESB движутся в виде сообщений.
REST
В постоянной борьбе со сложностью кода, программисты сделали очередной шаг и создали REST .
Основной принцип REST в том, что операции-функции резко ограничили и оставили только набор операций CRUD: Create - Read - Update - Delete . В этой модели все операции всегда применяются к некоторым данным. Имеющихся в CRUD операций достаточно для большей части приложений. Так как REST технологии в большинстве случаев подразумевают использование протокола HTTP, то команды CRUD отразились на команды HTTP (Post - Get - Put - Delete ) . Постоянно утверждается, что REST не обязательно привязан к HTTP. Но на практике повсеместно используется отражение сигнатур операций на синтаксис HTTP команд. К примеру, вызов функции
EntityAddress ReadEntityAddress(string param1, string param2)
Выразится в таком виде:
GET: entityAddress?param1=value1¶m2=value2
Заключение
Прежде, чем начинать дискуссию по распределенным системам или по интеграции, определитесь с терминологией. Если Proxy всегда будет означать одно и то же в разных контекстах, то, к примеру, request мало что будет значить в терминах RPC, а marshalling вызовет недоумение при обсуждении REST технологий.
Программы, общающиеся через сеть, нуждаются в механизме связи. На нижнем уровне по поступлении пакетов подается сигнал, обрабатываемый сетевой программой обработки сигналов. На верхнем уровне работает механизм rendezvous (рандеву), принятый в языке Ада. В NFS используется механизм вызова удаленных процедур (RPC), в котором клиент взаимодействует с сервером (см. Рисунок 1). В соответствии с этим процессом клиент сначала обращается к процедуре, посылающей запрос на сервер. По прибытии пакета с запросом сервер вызывает процедуру его вскрытия, выполняет запрашиваемую услугу, посылает ответ, и управление возвращается клиенту.
Интерфейс RPC можно представить состоящим из трех уровней:
Верхний уровень полностью "прозрачен". Программа этого уровня может, например, содержать обращение к процедуре rnusers(), возвращающей число пользователей на удаленной машине. Вам не нужно знать об использовании механизма RPC, поскольку вы делаете обращение в программе.
Средний уровень предназначен для наиболее общих приложений. RPC-вызовами на этом уровне занимаются подпрограммы registerrpc() и callrpc(): registerrpc() получает общесис темный код, а callrpc() исполняет вызов удаленной процедуры. Вызов rnusers() реализуется с помощью этих двух подпрограмм.
Нижний уровень используется для более сложных задач, изменяющих умолчания на значения параметров процедур. На этом уровне вы можете явно манипулировать гнездами, используемыми для передачи RPC-сообщений.
Как правило, вам следует пользоваться верхним уровнем и избегать использования нижних уровней без особой необходимости.
Несмотря на то, что в данном руководстве мы рассматриваем интерфейс только на Си, обращение к удаленным процедурам может быть сделано из любого языка. Работа механизма RPC для организации взаимодействия между процессами на разных машинах не отличается от его работы на одной машине.
RPC (Remote Procedure Call, Сервис вызова удаленных процедур) представляет собой интерфейс между удаленными пользователями и определенными программами хоста, которые запускаются по запросам этих пользователей. Сервис RPC какого-либо хоста, как правило, предоставляет клиентам комплекс программ. Каждая из таких программ состоит, в свою очередь, из одной или нескольких удаленных процедур. Например, сервис удаленной файловой системы NFS, который построен на вызовах RPC, может состоять только из двух программ: например, одна программа взаимодействует с высокоуровневыми пользовательскими интерфейсами, а другая - с низкоуровневыми функциями ввода-вывода.
В каждом вызове удаленной процедуры участвуют две стороны: активный клиент, который отправляет запрос вызова процедуры на сервер, и сервер, который отправляет клиенту ответ.
Примечание. Следует иметь в виду, что термины "клиент" и "сервер" в данном случае относятся к определенной транзакции Конкретный хост или программное обеспечение (процесс или программа) могут работать как в роли клиента, так и в роли сервера. Например, программа, которая обеспечивает работу сервиса удаленных процедур, в то же время может быть клиентом в работе с сетевой файловой системой.
Протокол RPC построен на модели вызовов удаленных процедур, подобному механизму вызовов локальных процедур. При вызове локальной процедуры вы помещаете аргументы в определенное место памяти, в стек или переменные окружения и передаете управление процессом по определенному адресу. После завершения работы вы читаете результаты по конкретному адресу и продолжаете свой процесс.
В случае работы с удаленной процедурой, основное отличие состоит в том, что вызов удаленной функции обслуживают два процесса: клиентский процесс и серверный процесс.
Процесс клиента отправляет серверу сообщение, в которое включены параметры вызываемой процедуры и ожидает ответного сообщения с результатами ее работы. При получении ответа результат считывается, и процесс продолжает работу. Со стороны сервера процесс-обработчик вызовов находится в состоянии ожидания, и, при поступлении сообщения, считывает параметры процедуры, выполняет ее, отправляет ответ и становится в состояние ожидания следующего вызова.
RPC-протокол не накладывает каких-либо требований на дополнительные связи между процессами и не требует синхронности выполняемых функций, т. е. вызовы могут быть асинхронными и взамонезависимыми, так что клиент во время ожидания ответа может выполнять другие процедуры. Сервер RPC может выделять для каждой функции отдельный процесс или виртуальную машину, поэтому, не дожидаясь окончания работы предыдущих запросов, сразу же может принимать следующие.
Однако между вызовами локальных и удаленных процедур есть несколько важных отличий:
1. Обработка ошибок. Клиент в любом случае должен получать уведомление об ошибках, возникающих при вызовах удаленных процедур на сервере или в сети.
2. Глобальные переменные. Поскольку сервер не имеет доступа к адресному пространству клиента, при вызовах удаленных процедур нельзя использовать скрытые параметры в виде глобальных переменных.
3. Производительность. Скорость выполнения удаленных процедур, как правило на один или два порядка ниже скорости выполнения аналогичных локальных процедур.
4. Аутентификация. Поскольку вызовы удаленных процедур происходят по сети, необходимо использовать механизмы аутентификации клиента.
Принципы построения протокола.
Протокол RPC может использовать несколько различных транспортных протоколов. В обязанности RPC-протокола входит только обеспечение стандартов и интерпретация передачи сообщений. Достоверность и надежность передачи сообщений целиком обеспечивается транспортным уровнем.
Однако RPC может контролировать выбор и некоторые функции транспортного протокола. В качестве примера взаимодействия между RPC и транспортным протоколом рассмотрим процедуру назначения RPC-порта работы прикладного процесса через RPC - Portmapper .
Эта функция динамически (по запросу) назначает соединению RPC определенный порт. Функция Portmapper используется довольно часто, поскольку набор зарезервированных для RPC транспортных портов ограничен, а количество процессов, которые потенциально могут одновременно работать очень высоко. Portmapper , например, вызывается при выборе портов взаимодействия клиента и сервера системы NFS.
Сервис Portmapper использует механизм широковещательных сообщений RPC на определенный порт - III. На этот порт клиент отправляет широковещательное сообщение запроса порта определенного сервиса RPC. Сервис Portmapper обрабатывает таксе сообщение, определяет адрес локального сервиса RPC и отправляет клиенту ответ. Сервис RPC Portmapper может работать как с TCP, так и с UDP-протоколами.
RPC может работать с различными транспортными протоколами, но никогда не дублирует их функции, т. е. если RPC работает поверх TCP, все заботы о надежности и достоверности соединения RPC возлагает на TCP. Однако, если протокол RPC установлен поверх UDP, он может обеспечивать дополнительные собственные функции обеспечения гарантированной доставки сообщений.
Примечание. Прикладные задачи могут рассматривать RPC-протокол как определенную процедуру вызова функции по сети JSR (Jump Subroutine Instruction).
Для работы RPC-протокола необходимо выполнение следующих условий:
1. Уникальная идентификации всех удаленно вызываемых процедур на данном хосте. RPC-запросы содержат три поля идентификаторов - номер удаленной программы (сервиса), номер версии удаленной программы и номер удаленной процедуры указанной программы. Номер программы назначается производителем сервиса, номер процедуры указывает на конкретную функцию данного сервиса
2. Идентификация версии RPC-протокола. RPC-сообщения содержат поле версии RPC-протокола. Она используется для согласования форматов передаваемых параметров при работе клиента с различными версиями RPC.
3. Предоставление механизмов аутентификации клиента на сервере. RPC-протокол обеспечивает процедуру аутентификации клиента в сервисе, и, в случае необходимости, при каждом запросе или отправке ответа клиенту. Кроме того, RPC позволяет использовать различные дополнительные механизмы безопасности.
RPC может использовать четыре типа механизмов аутентификации:
AUTH_NULL - без использования аутентификации
AUTH_UNIX - аутентификация по стандарту UNIX
AUTH_SHORT - аутентификация по стандарту UNIX с собственной структурой кодирования
AUTH_DES - аутентификация по стандарту DES
4. Идентификация сообщений ответа на соответствующие запросы. Ответные сообщения RPC содержат идентификатор запроса, на основании которого они были построены. Этот идентификатор можно назвать идентификатором транзакции вызова RPC. Данный механизм особенно необходим при работе в асинхронном режиме и при выполнении последовательности из нескольких RPC-вызовов.
5. Идентификация ошибок работы протокола. Все сетевые или серверные ошибки имеют уникальные идентификаторы, по которым каждый из участников соединения может определить причину сбоя в работе.
Структуры сообщений протокола
При передаче RPC-сообщений поверх транспортного протокола, несколько RPC-сообщений могут располагаться внутри одного транспортного пакета. Для того чтобы отделять одно сообщение от другого, используется маркер записи (RM - Record Marker). Каждое RPC-сообщение "маркируется" ровно одним RM.
RPC-сообщение может состоять из нескольких фрагментов. Каждый фрагмент состоит из четырех байт заголовка и (от 0 до 2**31-1) данных. Первый бит заголовка указывает, является ли данный фрагмент последним, а остальные 31 бит указывают длину пакета данных.
Структура RPC формально описана на языке описания и представления форматов данных - XDR с дополнениями, касающимися описания процедур. Можно даже сказать, что язык описания RPC является расширением XDR, дополненным работой с процедурами.
Структура RPC-пакета выглядит следующим образом:
struct rpc_msg {
unsigned int xid;
union switch (msg_type mtype) {
call_body cbody;
reply body rbody;
где xid - идентификатор текущей транзакции, call_body - пакет запроса, reply_body - пакет ответа. Структура запроса выглядит примерно так:
struct call body {
unsigned int rpcvers;
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque_auth cred;
opaque_auth verf;
/* procedure parameters */
Структура ответа (reply_body) может содержать либо структуру, передаваемую в случае ошибки (тогда она содержит код ошибки), либо структуру успешной обработки запроса (тогда она содержит возвращаемые данные).
Программный интерфейс высокого уровня.
Использование подпрограмм в программе - традиционный способ структурировать задачу, сделать ее более ясной. Наиболее часто используемые подпрограммы собираются в библиотеки, где могут использоваться различными программами. В данном случае речь идет о локальном (местном) вызове, т. е. и вызывающий, и вызываемый объекты работают в рамках одной программы на одном компьютере.
В случае удаленного вызова процесс, выполняющийся на одном компьютере, запускает процесс на удаленном компьютере (т. е. фактически запускает код процедуры на удаленном компьютере). Очевидно, что удаленный вызов процедуры существенным образом отличается от традиционного локального, однако с точки зрения программиста такие отличия практически отсутствуют, т. е. архитектура удаленного вызова процедуры позволяет сымитировать вызов локальной.
Однако если в случае локального вызова программа передает параметры в вызываемую процедуру и получает результат работы через стек или общие области памяти, то в случае удаленного вызова передача параметров превращается в передачу запроса по сети, а результат работы находится в пришедшем отклике.
Данный подход является возможной основой создания распределенных приложений, и хотя многие современные системы не используют этот механизм, основные концепции и термины во многих случаях сохраняются. При описании механизма RPC мы будем традиционно называть вызывающий процесс - клиентом, а удаленный процесс, реализующий процедуру, - сервером.
Удаленный вызов процедуры включает следующие шаги:
1. Программа-клиент производит локальный вызов процедуры, называемой заглушкой (stub). При этом клиенту "кажется", что, вызывая заглушку, он производит собственно вызов процедуры-сервера. И действительно, клиент передает заглушке необходимые параметры, а она возвращает результат. Однако дело обстоит не совсем так, как это себе представляет клиент. Задача заглушки - принять аргументы, предназначаемые удаленной процедуре, возможно, преобразовать их в некий стандартный формат и сформировать сетевой запрос. Упаковка аргументов и создание сетевого запроса называется сборкой (marshalling).
2. Сетевой запрос пересылается по сети на удаленную систему. Для этого в заглушке используются соответствующие вызовы, например, рассмотренные в предыдущих разделах. Заметим, что при этом могут быть использованы различные транспортные протоколы, причем не только семейства TCP/IP.
3. На удаленном хосте все происходит в обратном порядке. Заглушка сервера ожидает запрос и при получении извлекает параметры - аргументы вызова процедуры. Извлечение (unmarshalling) может включать необходимые преобразования (например, изменения порядка расположения байтов).
4. Заглушка выполняет вызов настоящей процедуры-сервера, которой адресован запрос клиента, передавая ей полученные по сети аргументы.
5. После выполнения процедуры управление возвращается в заглушку сервера, передавая ей требуемые параметры. Как и заглушка клиента; заглушка сервера преобразует возвращенные процедурой значения, формируя сетевое сообщение-отклик, который передается по сети системе, от которой пришел запрос.
6. Операционная система передает полученное сообщение заглушке клиента, которая, после необходимого преобразования, передает значения (являющиеся значениями, возвращенными удаленной процедурой) клиенту, воспринимающему это как нормальный возврат из процедуры.
Таким образом, с точки зрения клиента, он производит вызов удаленной процедуры, как он это сделал бы для локальной. То же самое можно сказать и о сервере: вызов процедуры происходит стандартным образом, некий объект (заглушка сервера) производит вызов локальной процедуры и получает возвращенные ею значения. Клиент воспринимает заглушку как вызываемую процедуру-сервер, а сервер принимает собственную заглушку за клиента.
Таким образом, заглушки составляют ядро системы RPC, отвечая за все аспекты формирования и передачи сообщений между клиентом и удаленным сервером (процедурой), хотя и клиент и сервер считают, что вызовы происходят локально. В этом-то и состоит основная концепция RPC - полностью спрятать распределенный (сетевой) характер взаимодействия в коде заглушек. Преимущества такого подхода очевидны: и клиент и сервер являются независимыми от сетевой реализации, оба они работают в рамках некой распределенной виртуальной машины, и вызовы процедур имеют стандартный интерфейс.
Передача параметров
Передача параметров-значений не вызывает особых трудностей. В этом случае заглушка клиента размещает значение параметра в сетевом запросе возможно, выполняя преобразования к стандартному виду (например, изменяя порядок следования байтов). Гораздо сложнее обстоит дело с передачей указателей, когда параметр представляет собой адрес данных, а не их значение. Передача в запросе адреса лишена смысла, так как удаленная процедура выполняется в совершенно другом адресном пространстве. Самым простым решением, применяемым в RPC, является запрет клиентам передавать параметры иначе, как по значению, хотя это, безусловно, накладывает серьезные ограничения.
Связывание (binding)
Прежде чем клиент сможет вызвать удаленную процедуру, необходимо связать его с удаленной системой, располагающей требуемым сервером. Таким образом, задача связывания распадается на две:
Нахождение удаленного хоста с требуемым сервером
Нахождение требуемого серверного процесса на данном хосте
Для нахождения хоста могут использоваться различные подходы. Возможный вариант - создание некоего централизованного справочника, в котором хосты анонсируют свои серверы, и где клиент при желании может выбрать подходящие для него хост и адрес процедуры.
Каждая процедура RPC однозначно определяется номером программы и процедуры. Номер программы определяет группу удаленных процедур, каждая из которых имеет собственный номер. Каждой программе также присваивается номер версии, так что при внесении в программу незначительных изменений (например, при добавлении процедуры) отсутствует необходимость менять ее номер. Обычно несколько функционально сход-ных процедур реализуются в одном программном модуле, который при запуске становится сервером этих процедур, и который идентифицируется номером программы.
Таким образом, когда клиент хочет вызвать удаленную процедуру, ему необходимо знать номера программы, версии и процедуры, предоставляющей требуемый сервис.
Для передачи запроса клиенту также необходимо знать сетевой адрес хоста и номер порта, связанный с программой-сервером, обеспечивающей требуемые процедуры. Для этого используется демон portmap(IM) (в некоторых системах он называется rpcbind(IM)). Демон запускается на хосте, который предоставляет сервис удаленных процедур, и использует общеизвестный номер порта. При инициализации процесса-сервера он регистрирует в portmap(IM) свои процедуры и номера портов. Теперь, когда клиенту требуется знать номер порта для вызова конкретной процедуры, он посылает запрос на сервер portmap(IM), который, в свою очередь, либо возвращает номер порта, либо перенаправляет запрос непосредственно серверу удаленной процедуры и после ее выполнения возвращает клиенту отклик. В любом случае, если требуемая процедура существует, клиент получает от сервера portmap(IM) номер порта процедуры, и дальнейшие запросы может делать уже непосредственно на этот порт.
Обработка особых ситуаций (exception)
Обработка особых ситуаций при вызове локальных процедур не представляет особой проблемы. UNIX обеспечивает обработку ошибок процессов таких как деление на ноль, обращение к недопустимой области памяти и т. д. В случае вызова удаленной процедуры вероятность возникновения ошибочных ситуаций увеличивается. К ошибкам сервера и заглушек добавляются ошибки, связанные, например, с получением ошибочного сетевого сообщения.
Например, при использовании UDP в качестве транспортного протокола производится повторная передача сообщений после определенного тайм-аута. Клиенту возвращается ошибка, если, спустя определенное число попыток, отклик от сервера так и не был получен. В случае, когда используется протокол TCP, клиенту возвращается ошибка, если сервер оборвал TCP-соединение.
Семантика вызова
Вызов локальной процедуры однозначно приводит к ее выполнению после чего управление возвращается в головную программу. Иначе дело обстоит при вызове удаленной процедуры. Невозможно установить, когда конкретно будет выполняться процедура, будет ли она выполнена вообще, а если будет, то какое число раз? Например, если запрос будет получен удаленной системой после аварийного завершения программы сервера, процедура не будет выполнена вообще. Если клиент при неполучении отклика после определенного промежутка времени (тайм-аута) повторно посылает запрос, то может создаться ситуация, когда отклик уже передается по сети, а повторный запрос вновь принимается на обработку удаленной процедурой. В этом случае процедура будет выполнена несколько раз.
Таким образом, выполнение удаленной процедуры можно характеризовать следующей семантикой:
- Один и только один раз. Данного поведения (в некоторых случаях наиболее желательного) трудно требовать ввиду возможных аварий сервера.
- Максимум раз. Это означает, что процедура либо вообще не была выполнена, либо была выполнена только один раз. Подобное утверждение можно сделать при получении ошибки вместо нормального отклика.
- Хотя бы раз. Процедура наверняка была выполнена один раз, но возможно и больше. Для нормальной работы в такой ситуации удаленная процедура должна обладать свойством идемпотентности (от англ. idemponent). Этим свойством обладает процедура, многократное выполнение которой не вызывает кумулятивных изменений. Например, чтение файла идемпотентно, а добавление текста в файл - нет.
Представление данных
Когда клиент и сервер выполняются в одной системе на одном компьютере, проблем с несовместимостью данных не возникает. И для клиента и для сервера данные в двоичном виде представляются одинаково. В случае удаленного вызова дело осложняется тем, что клиент и сервер могут выполняться на системах с различной архитектурой, имеющих различное представление данных (например, представление значения с плавающей точкой, порядок следования байтов и т. д.)
Большинство реализаций системы RPC определяют некоторые стандартные виды представления данных, к которым должны быть преобразованы все значения, передаваемые в запросах и откликах.
Например, формат представления данных в RPC фирмы Sun Microsystems следующий:
Порядок следования байтов - Старший - последний
Представление значений с плавающей точкой - IEEE
Представление символа - ASCII
По своей функциональности система RPC занимает промежуточное место между уровнем приложения и транспортным уровнем. В соответствии с моделью OSI этому положению соответствуют уровни представления и сеанса. Таким образом, RPC теоретически независим от реализации сети, в частности, от сетевых протоколов транспортного уровня.
Программные реализации системы, как правило, поддерживают один или два протокола. Например, система RPC разработки фирмы Sun Microsystems поддерживает передачу сообщений с использованием протоколов TCP и UDP. Выбор того или иного протокола зависит от требований приложения. Выбор протокола UDP оправдан для приложений, обладающих следующими характеристиками:
Вызываемые процедуры идемпотентны
Размер передаваемых аргументов и возвращаемого результата меньше размера пакета UDP - 8 Кбайт.
Сервер обеспечивает работу с несколькими сотнями клиентов. Поскольку при работе с протоколами TCP сервер вынужден поддерживать соединение с каждым из активных клиентов, это занимает значительную часть его ресурсов. Протокол UDP в этом отношении является менее ресурсоемким
С другой стороны, TCP обеспечивает эффективную работу приложений со следующими характеристиками:
Приложению требуется надежный протокол передачи
Вызываемые процедуры неидемпонентны
Размер аргументов или возвращаемого результата превышает 8 Кбайт
Выбор протокола обычно остается за клиентом, и система по-разному организует формирование и передачу сообщений. Так, при использовании протокола TCP, для которого передаваемые данные представляют собой поток байтов, необходимо отделить сообщения друг от друга. Для этого например, применяется протокол маркировки записей, описанный в RFC1057 "RPC: Remote Procedure Call Protocol specification version 2", при котором в начале каждого сообщения помещается 32-разрядное целое число, определяющее размер сообщения в байтах.
По-разному обстоит дело и с семантикой вызова. Например, если RPC выполняется с использованием ненадежного транспортного протокола (UDP), система выполняет повторную передачу сообщения через короткие промежутки времени (тайм-ауты). Если приложение-клиент не получает отклик, то с уверенностью можно сказать, что процедура была выполнена ноль или большее число раз. Если отклик был получен, приложение может сделать вывод, что процедура была выполнена хотя бы однажды. При использовании надежного транспортного протокола (TCP) в случае получения отклика можно сказать, что процедура была выполнена один раз. Если же отклик не получен, определенно сказать, что процедура выполнена не была, нельзя3.
Как это работает?
По существу, собственно система RPC является встроенной в программу-клиент и программу-сервер. Отрадно, что при разработке распределенных приложений, не придется вникать в подробности протокола RPC или программировать обработку сообщений. Система предполагает существование соответствующей среды разработки, которая значительно облегчает жизнь создателям прикладного программного обеспечения. Одним из ключевых моментов в RPC является то, что разработка распределенного приложения начинается с определения интерфейса объекта - формального описания функций сервера, сделанного на специальном языке. На основании этого интерфейса затем автоматически создаются заглушки клиента и сервера. Единственное, что необходимо сделать после этого, - написать фактический код процедуры.
В качестве примера рассмотрим RPC фирмы Sun Microsystems. Система состоит из трех основных частей:
Rpcgen(1) - RPC-компилятор, который на основании описания интерфейса удаленной процедуры генерирует заглушки клиента и сервера в виде программ на языке С.
Библиотека XDR (eXternal Data Representation), которая содержит функции для преобразования различных типов данных в машинно-независимый вид, позволяющий производить обмен информацией между разнородными системами.
Библиотека модулей, обеспечивающих работу системы в целом.
Рассмотрим пример простейшего распределенного приложения для ведения журнала событий. Клиент при запуске вызывает удаленную процедуру записи сообщения в файл журнала удаленного компьютера.
Для этого придется создать как минимум три файла: спецификацию интерфейсов удаленных процедур log.x (на языке описания интерфейса), собственно текст удаленных процедур log.c и текст головной программы клиента main () - client.c (на языке С) .
Компилятор rpcgen(l) на основании спецификации log.x создает три файла: текст заглушек клиента и сервера на языке С (log clnt.c и log svc.c) и файл описаний log.h, используемый обеими заглушками.
Итак, рассмотрим исходные тексты программ.
В этом файле указываются регистрационные параметры удаленной процедуры - номера программы, версии и процедуры, а также определяется интерфейс вызова - входные аргументы и возвращаемые значения. Таким образом, определена процедура RLOG, в качестве аргумента принимающая строку (которая будет записана в журнал), а возвращаемое значение стандартно указывает на успешное или неудачное выполнение заказанной операции.
program LOG_PROG {
version LOG_VER {
int RLOG (string) = 1;
} = 0х31234567;
Компилятор rpcgen(l) создает файл заголовков log.h, где, в частности, определены процедуры:
log.h
* Please do not edit this file.
* It was generated using rpcgen.
#ifndef _LOG_H_RPCGEN
#define _LOG_H_RPCGEN
#include
/* Номер программы*/
#define LOG_PROG ((unsigned long) (0х31234567))
#define LOG_VER ((unsigned long) (1)) /*Номер версии*/
#define RLOG ((unsigned long) (1)) /*Номер процедуры*/
extern int *rlog_l () ;
/*Внутренняя процедура - нам ее использовать не придется*/ extern int log_prog_l_freeresult();
#endif /* !_LOG_H_RPCGEN */
Рассмотрим этот файл внимательно. Компилятор транслирует имя RLOG определенное в файле описания интерфейса, в rlog_1, заменяя прописные символы на строчные и добавляя номер версии программы с подчеркиванием. Тип возвращаемого значения изменился с int на int *. Таково правило - RPC позволяет передавать и получать только адреса объявленных при описании интерфейса параметров. Это же правило касается и передаваемой в качестве аргумента строки. Хотя из файла print.h это не следует, на самом деле в качестве аргумента функции rlog_l () также передается адрес строки.
Помимо файла заголовков компилятор rpcgen(l) создает модули заглушки клиента и заглушки сервера. По существу, в тексте этих файлов заключен весь код удаленного вызова.
Заглушка сервера является головной программой, обрабатывающей все сетевое взаимодействие с клиентом (точнее, с его заглушкой). Для выполнения операции заглушка сервера производит локальный вызов функции, текст которой необходимо написать:
log.c
#include
#include
#include
#include "log.h"
int *rlog_1 (char **arg)
/*Возвращаемое значение должно определяться как static*/
static int result;
int fd; /*Файловый дескриптор журнала*/
/*0ткроем файл журнала (создадим, если он не существует), в случае неудачи вернем код ошибки result == 1.*/
if ((fd=open("./server .log",
O_CREAT | O_RDWR | O_APPEND)) < 0) return (&result);
len = strlen(*arg);
if (write(fd, *arg, strlen(*arg)) != len)
return(&result); /*Возвращаем результат - адрес result*/
Заглушка клиента принимает аргумент, передаваемый удаленной процедуре, делает необходимые преобразования, формирует запрос на сервер portmap(1M), обменивается данными с сервером удаленной процедуры и, наконец, передает возвращаемое значение клиенту. Для клиента вызов удаленной процедуры сводится к вызову заглушки и ничем не отличается от обычного локального вызова.
client.c
#include
#include "log.h"
main(int argc, char *argv)
char *server, *mystring, *clnttime;
if (argc != 2) {
fprintf(stderr, "Формат вызова: %s Адрес_хоста\n",
/*Получим дескриптор клиента. В случае неудачи - сообщим о
невозможности установления связи с сервером*/
if ((с1 = clnt_create (server,
LOG_PROG, LOG_VER, "udp")) == NULL) {
clnt_pcreateerror (server);
/*Выделим буфер для строки*/
mystring = (char *)malloc (100);
/*Определим время события*/
bintime = time ((time_t *) NULL);
clnttime = ctime(&bintime);
sprintf (mystring, "%s - Клиент запущен", clnttime);
/*Передадим сообщение для журнала - время начала работы клиента. В случае неудачи - сообщим об ошибке*/
if ((result = rlog_l(&mystring, cl)) == NULL) {
fprintf(stderr, "error2\n");
clnt_perror(cl, server);
/*B случае неудачи на удаленном компьютере сообщим об ошибке*/
if (*result !=0)
fprintf(stderr, "Ошибка записи в журнал\n");
/*0свободим дескриптор*/
cint destroy(cl);
Заглушка клиента log_clnt.c компилируется с модулем client.c для получения исполняемой программы клиента.
cc -о rlog client.c log_clnt.c -Insl
Заглушка сервера log_svc.c и процедура log.c компилируются для получения исполняемой программы сервера.
cc -о logger log_svc.c log.c -Insl
Теперь на некотором хосте server.nowhere.ru необходимо запустить серверный процесс:
После чего при запуске клиента rlog на другой машине сервер добавит соответствующую запись в файл журнала.
Схема работы RPC в этом случае приведена на рис. 1. Модули взаимодействуют следующим образом:
1. Когда запускается серверный процесс, он создает сокет UDP и связывает любой локальный порт с этим сокетом. Далее сервер вызывает библиотечную функцию svc_register(3N) для регистрации номеров программы и ее версии. Для этого функция обращается к процессу portmap(IM) и передает требуемые значения. Сервер portmap(IM) обычно запускается при инициализации системы и связывается с некоторым общеизвестным портом. Теперь portmap(3N) знает номер порта для нашей программы и версии. Сервер же ожидает получения запроса. Заметим, что все описанные действия производятся заглушкой сервера, созданной компилятором rpcgen(IM).
2. Когда запускается программа rlog, первое, что она делает, - вызывает библиотечную функцию clnt_create(3N), указывая ей адрес удаленной системы, номера программы и версии, а также транспортный протокол. Функция направляет запрос к серверу portmap(IM) удаленной системы server.nowhere.m и получает номер удаленного порта для сервера журнала.
3. Клиент вызывает процедуру rlog_1 () , определенную в заглушке клиента, и передает управление заглушке. Та, в свою очередь, формирует запрос (преобразуя аргументы в формат XDR) в виде пакета UDP и направляет его на удаленный порт, полученный от сервера portmap(IM). Затем она некоторое время ожидает отклика и в случае неполучения повторно отправляет запрос. При благоприятных обстоятельствах запрос принимается сервером logger (модулем заглушки сервера). Заглушка определяет, какая именно функция была вызвана (по номеру процедуры), и вызывает функцию rlog_1 () модуля log.c. После возврата управления обратно в заглушку последняя преобразует возвращенное функцией rlog_1 () значение в формат XDR, и формирует отклик также в виде пакета UDP. После получения отклика заглушка клиента извлекает возвращенное значение, преобразует его и возвращает в головную программу клиента.
Очень важный механизм для приложений "клиент-сервер" обеспечивается RPC (Remote Procedure Call ). RPC был разработан Sun Micrsystems, и эта система представляет собой набор инструментальных средств и библиотечных функций. В частности на RPC работают NIS (Network Information System) и NFS (Network File System).
Сервер RPC состоит из системы таких процедур, что клиент может обратится, посылая RPC-запрос к серверу вместе с параметрами процедуры. Сервер вызовет обозначенную процедуру и вернет значение возврата процедуры, если оно есть. Чтобы быть машинно-независимыми, все данные, которыми обмениваются клиент и сервер, преобразуются к так называемому внешнему представлению данных (External Data Representation , XDR). RPC связан с сокетами UDP и TCP для передачи данных в формате XDR. Sun объявила RPC как public domain, и его описание доступно в серии документов RFC.
Иногда изменения в приложениях RPC вводят несовместимость в процедуру вызова интерфейса. Конечно, при простом изменении сервер разрушил бы все приложения, которые все еще ожидают прежних вызовов. Следовательно, RPC-программы имеют номера версии, приписываемые им, обычно начинающиеся с 1. Каждая новая версия RPC следит за номером версии. Часто сервер может предлагать несколько версий одновременно. Клиенты в этом случае указывают номером версии, которую они хотят использовать.
Сетевая связь между серверами и клиентами RPC немного особая. RPC-сервер предлагает одну или более системных процедур, каждое множество таких процедур называется программой (program ) и однозначно идентифицировано номером программы (program number ). Список имен сервисов обычно хранится в /etc/rpc , пример которого приведен ниже.
Пример 12-4. Образец файла /etc/rpc
# # /etc/rpc - miscellaneous RPC-based services # portmapper 100000 portmap sunrpc rstatd 100001 rstat rstat_svc rup perfmeter rusersd 100002 rusers nfs 100003 nfsprog ypserv 100004 ypprog mountd 100005 mount showmount ypbind 100007 walld 100008 rwall shutdown yppasswdd 100009 yppasswd bootparam 100026 ypupdated 100028 ypupdate |
В TCP/IP сетях перед авторами RPC стояла задача отображения номеров программ на общие сетевые услуги. Каждый сервер обеспечивает TCP и UDP порт для каждой программы и каждой версии. Вообще, RPC-приложения используют UDP для передачи данных и возвращаются обратно к TCP тогда, когда данные, которые надо передать, не вписываются в единственную датаграмму UDP.
Конечно, клиентские программы должны иметь способ выяснить, который порт соответсвует номеру программы. Использование для этого файла конфигурации было бы слишком негибким; с тех пор, как RPC-приложения не используют зарезервированные порты, нет никакой гарантии, что порт не занят каким-то приложением и доступен нам. Следовательно, RPC-приложения выбирают любой порт, который они могут получить, и регистрируют это с помощью portmapper daemon . Клиент, который хочет войти в контакт с сервисом с данным номером программы, сначала сделает запрос portmapper для выяснения номера порта нужного сервиса.
Этот метод имеет тот недостаток, что вводит единственную точку отказа, очень похожую на inetd daemon. Однако, этот случай немного хуже, потому что когда portmapper валится, вся информация RPC о портах теряется. Это обычно значит, что Вы должны перезапустить все RPC-серверы вручную или перезагрузить машину.
В Linux portmapper называется /sbin/portmap или /usr/sbin/rpc.portmap . Кроме того, что он должен быть запущен из скрипта запуска сети, рortmapper не требует никакой работы по конфигурации.