Главная » Статьи » Операционные системы » Общее |
Механизм сокетов (sockets) впервые появился в версии 4.3 BSD
UNIX (Berkeley Software Distribution UNIX — ветвь UNIX, начавшая развиваться в
калифорнийском университете Беркли). Позже он превратился в одну из самых
популярных систем сетевого обмена сообщениями. Сегодня этот механизм реализован
во многих операционных системах, иногда его по-прежнему называют Berkeley
Sockets, отдавая дань уважения его создателям, хотя существует большое
количество его реализаций как для различных ОС семейства UNIX, так и для других
ОС, например для ОС семейства Windows, где он носит название Windows Sockets
(WinSock). Механизм сокетов обеспечивает удобный и достаточно
универсальный интерфейс обмена сообщениями, предназначенный для разработки
сетевых распределенных приложений. Его универсальность обеспечивают следующие
концепции.
Для обмена сообщениями механизм сокетов предлагает следующие
примитивы, реализованные как системные вызовы.
s =
socket(domain, type, protocol) Процесс должен создать сокет перед началом его
использования. Системный вызов socket создает новый сокет с параметрами,
определяющими коммуникационный домен (domain), тип соединения, поддерживаемого
сокетом (type), и транспортный протокол (например, TCP или UDP), который будет
поддерживать это соединение. Если транспортный протокол не задан, то система
сама выбирает протокол, соответствующий типу сокета. Указание домена определяет
возможные значения остальных двух параметров. Системный вызов socket возвращает
дескриптор созданного сокета, который используется как идентификатор сокета в
последующих операциях.
bind(s. addr. addrlen) Системный вызов bind связывает созданный сокет с его
высокоуровневым именем либо с низкоуровневым адресом. Адрес addr относится к
тому узлу, на котором расположен сокет. Для низкоуровневого адреса домена
Интернета адресом будет пара (IP-адрес, порт). Третий параметр делает адрес
доменно-независимым, позволяя задавать адреса различных типов, в том числе символьные.
Связывать сокет с адресом необходимо только в том случае, если на данный сокет
будут приниматься сообщения.
connect(s,
server_addr. server_addrlen) Системный вызов connect используется только в том случае,
если предполагается передавать сообщения в потоковом режиме, который требует
установления соединения. Процедура установления несимметрична: один процесс
(процесс-сервер) ждет запроса на установление соединения, а второй
(процесс-клиент) — инициирует соединение, посылая такой запрос. Системный вызов
connect является запросом клиента на установление соединения с сервером. Второй
и третий аргументы вызова указывают адрес сокета сервера, с которым
устанавливается соединение. После установления соединения сообщения по нему
могут передаваться в дуплексном режиме, то есть в любом направлении. Системный
вызов write, используемый для передачи сообщений в рамках установленного
соединения, не требует указания адреса сокета получателя, так как локальный
сокет, через который сообщение отправляется, уже соединен с определенным
удаленным сокетом. Способ, с помощью которого клиенты узнают адрес сокета
сервера, не стандартизован.
listen (s. backlog) Системный вызов listen используется для организации
режима ожидания сервером запросов на установление соединения. Система обмена
сообщениями после отработки данного системного вызова будет принимать запросы
на установление, имеющие адрес сокета s, и передавать их на обработку другому
системному вызову — accept, который решает, принимать их или отвергать.Аргумент
backlog оговаривает максимальное число хранимых системой запросов на
установление соединения, ожидающих принятия.
snew =
accept(s. client_addr. client_addr1en) Системный вызов accept используется сервером для приема
запроса на установление соединения, поступившего от системного вызова 11 sten
через сокет s от клиента с адресом cl ient_addr (если этот аргумент опущен, то
принимается запрос от любого клиента). При этом создается новый сокет snew,
через который и устанавливается соединение с данным клиентом. Таким образом,
сокет s используется сервером для приема запросов на установление соединения от
клиентов, а сокеты snew — для обмена сообщениями с клиентами по индивидуальным
соединениям.
write(s. message, msgjen) Сообщение длиной msg_len, хранящееся в буфере message,
отправляется получателю, с которым предварительно соединен сокет s.
Mbytes
=read(snew, buffer, amount) Сообщение, поступившее через сокет snew, с которым
предварительно соединен отправитель, принимается в буфер buffer размером
amount. Если сообщений нет, то процесс-получатель блокируется.
sendto(s.
message, receiver_address) Так как сообщение отправляется без предварительного
установления соединения, то в каждом системном вызове sendto необходимо
указывать адрес со-кета получателя.
amount =
recvfrom(s, message, sender_address) Аналогично предыдущему вызову при приеме без установленного
соединения в каждом вызове recvfrom указывается адрес сокета отправителя, от
которого нужно принять сообщение. Если сообщений нет, то процесс-получатель
блокируется. Рассмотрим использование системных вызовов механизма сокетов
для организации обмена сообщениями между двумя узлами. Для обмена короткими сообщениями, не требующими надежной
доставки и упорядоченности, целесообразно воспользоваться системными вызовами,
не требующими установления соединения. Фрагмент программы процесса-отправителя
может выглядеть так: s = socket(AF_INET. SOCKJJGRAM.0): bind(s,
sender_addr, sender_addrlen): sendto(s,
message, receiver_addr); close(s): Соответственно для процесса-получателя: s
=socket(AF_INET, SOCK_DGRAM,0); bind(s.
receiver_addr, receiver_addrlen): amount =
recvfrom(s, message, sender_addr): close(s); Константа AF_INET определяет, что обмен ведется в
коммуникационном домене Интернета, а константа SOCK_DGRAM задает дейтаграммный
режим обмена без установления соединения. Выбор транспортного протокола
оставлен на усмотрение системы. Если же необходимо организовать обмен сообщениями надежным
способом с упорядочением, то фрагменты программ будут выглядеть следующим
образом. Для процесса-клиента: s =socket(AF_INET. SOCK_STREAM.0); connect(s,
server_addr, server_addrlen); wrlte(s,
message, msgjen); wrlte(s,
message, msgjen); closets): Для процесса-сервера: s = socket(AFJNET. SOCKJTREAM.0): bind(s,
server_addr, server_addrlen); 1isten(s,
backlog): snew -
accept(s. c11ent_addr, cl1ent_addrlen); nbytes
=read(snew, buffer, amount); nbytes
= read(snew, buffer, amount); close(s); Еще одним удобным механизмом, облегчающим взаимодействие
операционных систем и приложений по сети, является механизм вызова удаленных
процедур (Remote Procedure Call, RPC). Этот механизм представляет собой
надстройку над системой обмена сообщениями ОС, поэтому в ряде случаев он
позволяет более удобно и прозрачно организовать взаимодействие программ по
сети, однако его полезность не универсальна. Концепция удаленного вызова процедур Идея вызова удаленных процедур состоит в расширении хорошо
известного и понятного механизма передачи управления и данных внутри программы,
выполняющейся на одной машине, на передачу управления и данных через сеть.
Средства удаленного вызова процедур предназначены для облегчения организации
распределенных вычислений. Впервые механизм RPC реализовала компания Sun
Microsystems, и он хорошо соответствует девизу «Сеть — это компьютер», взятому
этой компанией на вооружение, так как приближает сетевое программирование к
локальному. Наибольшая эффективность RPC достигается в тех приложениях, в
которых существует интерактивная связь между удаленными компонентами с
небольшим временем ответов и относительно малым количеством передаваемых
данных. Такие приложения называются RPC-ориентированными. Характерными чертами вызова локальных процедур являются:
Реализация удаленных вызовов существенно сложнее реализации
вызовов локальных процедур. Начнем с того, что поскольку вызывающая и
вызываемая процедуры выполняются на разных машинах, то они имеют разные
адресные пространства и это создает проблемы при передаче параметров и
результатов, особенно если машины и их операционные системы не идентичны. Так
как RPC не может рассчитывать на разделяемую память, это означает, что
параметры RPC не должны содержать указателей на ячейки памяти и что значения
параметров должны как-то копироваться с одного компьютера на другой. Следующим отличием RPC от локального вызова является то, что
он обязательно использует нижележащую систему обмена сообщениями, однако это не
должно быть явно видно ни в определении процедур, ни в самих процедурах.
Удаленность вносит дополнительные проблемы. Выполнение вызывающей программы и
вызываемой локальной процедуры в одной машине реализуется в рамках единого
процесса. Но в реализации RPC участвуют как минимум два процесса — по одному в
каждой машине. В случае если один из них аварийно завершится, могут возникнуть
следующие ситуации:
Кроме того, существует ряд проблем, связанных с
неоднородностью языков программирования и операционных сред: структуры данных и
структуры вызова процедур, поддерживаемые в каком-либо одном языке
программирования, не поддерживаются точно таким же способом в других языках. Рассмотрим, каким образом технология RPC, лежащая в основе
многих распределенных операционных систем, решает эти проблемы. Чтобы понять работу RPC, рассмотрим сначала выполнение
вызова локальной процедуры в автономном компьютере. Пусть это, например, будет
процедура записи данных в файл: m.=
my_write(fd,buf.length); Здесь fd — дескриптор файла, целое число, buf — указатель на
массив символов, length — длина массива, целое число. Чтобы осуществить вызов, вызывающая процедура помещает
указанные параметры в стек в обратном порядке и передает управление вызываемой
процедуре my_wr1te. Эта пользовательская процедура после некоторых манипуляций
с данными символьного массива buf выполняет системный вызов write для записи
данных в файл, передавая ему параметры тем же способом, то есть помещая их в
стек (при реализации системного вызова они копируются в стек системы, а при
возврате из него результат помещается в пользовательский стек). После того как
процедура my_write выполнена, она помещает возвращаемое значение m в регистр,
перемещает адрес возврата и возвращает управление вызывающей процедуре, которая
выбирает параметры из стека, возвращая его в исходное состояние. Заметим, что в
языке С параметры могут вызываться по ссылке (by name), представляющей собой
адрес глобальной области памяти, в которой хранится параметр, или по значению
(by value), в этом случае параметр копируется из исходной области памяти в
локальную память процедуры, располагаемую обычно в стековом сегменте. В первом
случае вызываемая процедура работает с оригинальными значениями параметров и их
изменения сразу же видны вызывающей процедуре. Во втором случае вызываемая
процедура работает с копиями значений параметров, и их изменения никак не
влияют на значение оригиналов этих переменных в вызывающей процедуре. Эти
обстоятельства весьма существенны для RPC. Решение о том, какой механизм передачи параметров
использовать, принимается разработчиками языка. Иногда это зависит от типа
передаваемых данных. В языке С, например, целые и другие скалярные данные
всегда передаются по значению, а массивы — по ссылке. Рисунок 9.6 иллюстрирует передачу параметров вызываемой
процедуре: стек до выполнения вызова write (а), стек во время выполнения
процедуры (б), стек после возврата в вызывающую программу (в). Идея, положенная в основу RPC, состоит в том, чтобы вызов
удаленной процедуры по возможности выглядел так же, как и вызов локальной
процедуры. Другими словами, необходимо сделать механизм RPC прозрачным для
программиста: вызывающей процедуре не требуется знать, что вызываемая процедура
находится на другой машине, и наоборот. Механизм RPC достигает прозрачности следующим образом. Когда
вызываемая процедура действительно является удаленной, в библиотеку процедур
вместо локальной реализации оригинального кода процедуры помещается другая
версия процедуры, называемая клиентским стабом (stub — заглушка). На удаленный
компьютер, который выполняет роль сервера процедур, помещается оригинальный код
вызываемой процедуры, а также еще один стаб, называемый серверным стабом.
Назначение клиентского и серверного стабов - организовать передачу параметров
вызываемой процедуры и возврат значения процедуры через сеть, при этом код
оригинальной процедуры, помещенной на сервер, должен быть полностью сохранен.
Стабы используют для передачи данных через сеть средства подсистемы обмена
сообщениями, то есть существующие в ОС примитивы send и receive. Иногда в
подсистеме обмена сообщениями выделяется программный модуль, организующий связь
стабов с примитивами передачи сообщений, называемый модулем RPCRimtime. Эта операция называется операцией упаковки параметров. После
этого клиентский стаб обращается к примитиву send для передачи этого сообщения
удаленному компьютеру, на который помещена реализация оригинальной процедуры.
Получив из сети сообщение, ядро ОС удаленного компьютера вызывает серверный
стаб, который извлекает из сообщения параметры и вызывает обычным образом
оригинальную процедуру. Для получения сообщения серверный стаб должен
предварительно вызвать примитив receive, чтобы ядро знало, для кого пришло
сообщение. Серверный стаб распаковывает параметры вызова, имеющиеся в
сообщении, и обычным образом вызывает оригинальную процедуру, передавая ей
параметры через стек. После окончания работы процедуры серверный стаб
упаковывает результат ее работы в новое сообщение и с помощью примитива send
./' передает сообщение по сети клиентскому стабу, а тот возвращает обычным
обра-' зом результат и управление вызывающей процедуре. Ни вызывающая процеду-'
ра, ни оригинальная вызываемая процедура не изменились оттого, что они стали
работать на разных компьютерах. | |
Просмотров: 2458 | Рейтинг: 0.0/0 |
Всего комментариев: 0 | |