Э. Таненбаум, М. ван Стеен - Распределённые системы (принципы и парадигмы) (1162619), страница 24
Текст из файла (страница 24)
Это короткая процедура, которая обычно реализуется путем системного вызова read. Другими словами, процедураread — это интерфейс между кодом пользователя и локальной операционной системой.Даже если read — это системный вызов, он производится аналогичным образом, путем помещения параметров в стек, как показано на рис. 2.6, б. Таким образом, программист так и не узнает, что read делает что-то хитрое.RPC организует свою прозрачность аналогичным образом. Если read является удаленной процедурой (то есть будет исполняться на машине файлового сервера), в библиотеку помещается специальная версия read, называемая клиентской заглушкой {client stub).
Как и оригинальная функция, она также вызываетсяв соответствии с последовательностью, показанной на рис 2.6, б. Как и оригинал,она также производит вызов локальной операционной системы, только в отличие от оригинальной функции клиентская заглушка не запрашивает данныеу операционной системы. Вместо этого она упаковывает параметры в сообщениеи путем вызова процедуры send требует переслать это сообщение на сервер, какпоказано на рис. 2.7.
После вызова процедуры send клиентская заглушка вызывает процедуру receive, блокируясь до получения ответа.кОжидание результатаВызов \удаленной \процедуры \/// ЗавершениевызоваЗапрос\Сервер/ Ответ'Вызов локальнойпроцедуры и возвращениерезультатаВремя•Рис. 2.7. Схема RPC между программами клиента и сервераКогда сообщение приходит на сервер, операционная система сервера передаетего серверной заглушке {server stub). Серверная заглушка эквивалентна клиентской, но работает на стороне сервера.
Это фрагмент кода, который преобразуетприходящие по сети запросы в вызовы локальных процедур. Обычно сервернаязаглушка запускает процедуру receive и блокируется, ожидая входящих сообщений. После получения сообщения серверная заглушка распаковывает его, извлекая параметры, и традиционным способом (то есть так, как показано на рис.
2.6)2.2. Удаленный вызов процедур97вызывает процедуру сервера. С точки зрения сервера это воспринимается какпрямой вызов с клиента — параметры и адрес возврата находятся в стеке, гдеони и должны находиться, и ничего необычного в этом нет. Сервер делает своедело и обычным порядком возвращает результат вызвавшей процедуре. Так,например, в случае чтения файла сервер заполняет данными буфер, на который указывает второй параметр.
Этот буфер — внутренний буфер серверной заглушки.Когда серверная заглушка после окончания обработки вызова возвращаетуправление вызвавшей программе, она запаковывает результаты выполнения (буфер) в сообщение и вызывает процедуру send, чтобы возвратить их клиенту. После этого серверная заглушка вновь вызывает процедуру receive, переходя в режим ожидания следующего сообщения.Когда на клиентскую машину приходит ответное сообщение, операционнаясистема клиента обнаруживает, что оно адресовано клиентскому процессу (пасамом деле клиентской заглушке, но операционная система их не различает).Сообщение копируется в буфер ожидания, и клиентский процесс разблокируется. Клиентская заглушка рассматривает сообщение, распаковывает его, извлекаярезультаты, копирует их в память вызвавшей программы и, завершая работу, передает в нее код возврата.
Когда вызвавшая программа получает управление после вызова read, все, что она знает, — это то, что запрошенные данные находятсятам, где и предполагалось, то есть в буфере. У нее нет никакого представленияо том, как осуществлялся вызов — удаленно или в рамках локальной операционной системы.В блаженстве неведения, царящем на стороне клиента, и состоит прелесть такой схемы.
Как мы видели, доступ к удаленным службам осуществляется посредством вызова обычных (то есть локальных) процедур, без всяких там sendи гесе1 ve. Все детали пересылки сообщений скрыты в двух библиотечных процедурах, так же как в традиционных библиотеках скрыты детали реально производимых системных вызовов.Подведем итоги. При удаленном вызове процедур происходят следующиедействия.1. Процедура клиента обычным образом вызывает клиентскую заглушку.2. Клиентская заглушка создает сообщение и вызывает локальную операционную систему.3.
Операционная система клиента пересылает сообщение удаленной операционной системе.4. Удаленная операционная система передает сообщение серверной заглушке.5. Серверная заглушка извлекает из сообщения параметры и вызывает сервер.6. Сервер выполняет вызов и возвращает результаты заглушке.7. Серверная заглушка запаковывает результаты в сообщение и вызывает своюлокальную операционную систему.8. Операционная система сервера пересылает сообщение операционной системеклиента.98Глава 2.
Связь9. Операционная система клиента принимает сообщение и передает его клиентской заглушке.10. Заглушка извлекает результаты из сообщения и передает их клиенту.Сетевые эффекты этих шагов состоят в том, что клиентская заглушка превращает локальный вызов процедуры клиента в локальный вызов процедуры сервера, причем ни клиент, ни сервер ничего не знают о промежуточных действиях.2.2.2. Передача параметровНазначение клиентской заглушки состоит в том, чтобы получить параметры, запаковать их в сообщение и послать его серверной заглушке. Хотя эти действиявыглядят несложно, они не так просты, как кажется на первый взгляд. В этомпункте мы рассмотрим некоторые вопросы, связанные с передачей параметровв системах RPC.Передача параметров по значениюУпаковка параметров в сообщение носит название маршалиига параметров{parameter marshaling), В качестве простейшего примера рассмотрим удаленнуюпроцедуру, add (1, j), которая использует два целых параметра, 1 и j , и возвращаетв результате их арифметическую сумму.
(На практике так не делается, посколькуудаленная реализация столь простой процедуры крайне невыгодна, но для примера сойдет.) Вызов add иллюстрируется левой частью рис. 2.8 (в клиентскомпроцессе). Клиентская заглушка извлекает два ее параметра и, как показано нарисунке, упаковывает их в сообщение. Она также помещает туда имя или номервызываемой в сообщении процедуры, поскольку сервер может поддерживать несколько разных процедур и ему следует указать, какая из них потребоваласьв данном случае.Клиентская машинаСерверная машинаКлиентский процессСерверный процессРеализацияпроцедуры addСервернаязаглушкак^&^Щ).ргос: "add"inti val(i)int: val(j)Операционнаясистема клиентаКлиентскаязаглушкаргос: "add"val(i)int: val(j)jnt:Niiiiiiiргос: "add"int: val(i)int: val(j)Операционнаясистема сервераРис.
2.8. Процесс удаленных вычислений с использованием RPC2.2. Удаленный вызов процедур99Когда сообщение приходит на сервер, заглушка исследует сообщение в поискахуказания на то, какую процедуру следует вызвать, а затем делает соответствующий вызов. Если сервер поддерживает и другие удаленные процедуры, серверная заглушка должна содержать ргнструкцию типа switch для выбора вызываемойпроцедуры в зависимости от первого поля сообщения. Реальный вызов процедуры сервера из серверной заглушки выглядит почти как первоначальный клиентский вызов, если не считать того, что параметрами являются переменные, инициализированные значениями, взятыми из сообщения.ТакР1м образом, имеет место следующая пошаговая процедура.1.
Клиент вызрлвает процедуру add.2. Клиентская заглушка строит сообщение.3. Сообщение отправляется по сети на сервер.4. Операционная система сервера передает сообщение серверной заглушке.5. Серверная заглушка распаковывает сообщение.6. Серверная заглушка выполняет локальный вызов процедуры add.Когда сервер заканчивает работу, управление вновь передается серверной заглушке.
Она получает результат, переданный сервером, и запаковывает его в сообщение. Это сообщение отправляется назад, к клиентской заглушке, котораяраспаковывает его и возвращает полученное значение клиентской процедуре.До тех пор пока машины клиента и сервера идентичны, а все параметры и результаты имеют скалярный тип (то есть целый, символьный или логический),эта модель работает абсолютно правильно. Однако в больип1х распределенныхсистемах обычно присутствуют машины разных типов. Каждая из машин частоимеет собственное представление чисел, символов и других элементов данных.Так, в мэйнфреймах IBM используется кодовая таблица EBCDIC, а в персональных компьютерах той же фирмы — ASCII.
Вследствие этого, если передать символьный параметр с клиента на базе IBM PC на мэйнфрейм IBM, используемыйв качестве сервера, по простейшей схеме, показанной на рис. 2.8, сервер пойметэти символы неправильно.Сходные проблемы могут обнаружиться при передаче целых чисел (знаковый или значащий старший бит) и чисел с плавающей точкой. Вдобавок существует значительно более серьезная проблема, состоящая в том, что в некоторыхмашинах, таких как Intel Pentium, байты нумеруются справа налево, а в других,например в Sun SPARC, — в обратном направлении. Формат компании Intel называется остроконечным {little endian), а формат SPARC — тупоконечным (bigendian), по аналогии с названиями политрхческих партий из книги «ПутешествияГулливера», которые в спорах о том, с какой стороны разбивать яйца, дошли довойны [108]. Для примера рассмотрим процедуру с двумя параметрами, целымчислом и строкой из четырех символов. Для размещения каждого из параметровтребуется одно 32-битное слово.
На рис. 2.9, а показано, как будет выглядеть содержащий параметры фрагмент сообщения, построенного клиентской заглушкой, когда клиент работает на компьютере Intel Pentium. Первое слово содержитцелый параметр, в данном случае 5, а второе слово — строку JILL.100Глава 2. Связь1 2 |1о ' " " о'""' о ' " "1 3171L;_6_L'"" 11_5_|0б'""—1——1Р 1 11—3!0 ""о505\6J 7.J4 1J "' 1 " " LL—,— —.,—— 1"""о1 1""о_5_!L_6j 0\L1Л!51}Рис. 2.9. Исходное сообщение, подготовленное на Pentium (а). Сообщение послеполучения на SPARC (б).
Сообщение после инверсии (в). Цифры в квадратахпоказывают адрес каждого байтаПоскольку сообщение передается по сети байт за байтом (на самом деле бит забитом), первый посланный байт будет и первым принятым. На рис. 2.9, б мы видим, как будет выглядеть сообщение с рис. 2.9, а, принятое на компьютере SPARC.Нумерация байтов здесь такова, что нулевым байтом считается левый (верхню"!байт), а не правый (нижний байт), как в процессорах Intel. После того как серверная заглушка прочитает параметры по адресам О и 4, сервер получит, соответственно, целое число, равное 83 886 080 (5x2^"^), и строку JILL,Очевидное, но, к сожалению, неверное решение — просто инвертировать байты каждого слова после того, как оно будет принято (рис.
2.9, в). Теперь целоечисло стало правильным, а строка превратилась в LLIJ. Проблема состоит в том,что целые нужно приводить к другому порядку следования байтов, а строки —нет. Не имея дополнительной информации о том, где строка, а где целое, мы нев состоянии исправить положение дел.Передача параметров по ссылкеТеперь мы подошли к сложной проблеме: как передавать указатели или, в общемслучае, ссылки? Общий ответ таков: с величайшим трудом. Мы помним, что указатель имеет смысл только в адресном пространстве того процесса, в котором ониспользуется. Возвращаясь к нашему примеру с процедурой read, который мы обсуждали ранее, второй параметр (адрес буфера) для клиента может быть равен,например, 1000, но нельзя же просто передать на сервер число 1000 и ожидать,что это сработает. На сервере адрес 1000 вполне может прийтись на середину текста программы.Одно из решений состоит в том, чтобы вообще забыть про указатели и ссылки в качестве параметров.