Передача сообщений в распределенных системах 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Передача сообщений в распределенных системах



Единственным по-настоящему важным отличием распределенных систем от централизованных является способ взаимодействия процессов. Межпроцессное взаимодействие может осуществляться двумя способами [ 28 ]:

• с помощью совместного использования одних и тех же данных, помещенных в разделяемую память;

• путем передачи данных друг другу в виде сообщений.

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

В заголовке содержатся следующие элементы:

• адреса процесса-отправителя и процесса-получателя;

• последовательный номер, являющийся идентификатором сообщения. Он может использоваться для идентификации потерянных сообщений и дубликатов сообщений в случае отказов в сети.

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

• поле типа данных, указывающее, какие данные (целые числа, строки символов и т. п.) передаются;

• поле длинны данных - определяет длину передаваемых в сообщении даных, т. е. размер поля сообщения;

• поле данных - содержит передаваемые данные.

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

В любой сетевой ОС имеется подсистема передачи сообщений, называемая транспортной подсистемой, которая имеет набор средств для организации взаимодействия процессов в сети. Назначение этой системы - экранировать детали сложных сетевых протоколов от программистов. Подсистема позволяет процессам взаимодействовать посредством достаточно простых примитивов. В самом простом случае системные средства обеспечения связи могут быть сведены к двум основным коммуникационным примитивам: один send (отправить), другой receive (получить) - для получения сообщения.

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

Транспортная подсистема сетевой ОС имеет сложную структуру, отражающую структуру семиуровневой модели взаимодействия открытых систем (Open System Interconnection, OSI). Представление сложной задачи сетевого взаимодействия компьютеров в виде иерархии частных задач позволяет организовать это взаимодействие максимально гибким образом. В это же время каждый уровень модели OSI экранирует особенности лежащих под ним уровней от вычисляющих уровней, что делает средства взаимодействия компьютеров все более универсальными по мере продвижения вверх по уровням. Таким образом, в процесс выполнения примитивов send и received вовлекаются средства всех нижележащих коммуникационных протоколов (рис. 5.25).

Несмотря на концептуальную простоту примитивов send и received, существуют различные варианты их реализации, от правильного выбора которых зависит эффективность работы сети.

Центральным вопросом взаимодействия процессов в сети является способ их синхронизации, который полностью определяется используемыми в ОС коммуникационными примитивами. В этом отношении коммуникационные примитивы делятся на блокирующие (синхронные) и неблокирующие (асинхронные), причем смысл данных терминов в целом соответствует смыслу аналогичных терминалов, применяемых при описании системных вызовов и операций ввода-вывода._

рис 5.25

 

Коммуникационные примитивы могут быть оформлены в операционной системе двумя способами: как внутренние процедуры ядра ОС (в этом случае ими могут пользоваться только модули ОС) или как системные вызовы (доступные в этом случае процессам в пользовательском режиме).

При использовании блокирующего примитива send процесс, выдавший запрос на его выполнение, приостанавливается до момента получения по сети сообщения о том, что приемник получил отправленное сообщение. А вызов блокирующего примитива received приостанавливает вызывающий процесс до момента, когда он получил сообщение.

При использовании неблокирующих примитивов send и receive управление возвращается вызывающему процессу немедленно, сразу после того, как ядру передается информация о том, где в памяти находится буфер, в который нужно поместить сообщение, отправляемое в сеть или ожидаемое из сети.

Преимуществом этой схемы является параллельное выполнение вызывающего процесса и процедур передачи сообщения.

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

• опрос (polling). Этот метод предусматривает наличие еще одного базового примитива test (проверить), с помощью которого процесс-получатель может анализировать состояние буфера;

• прерывание (interrupt). Этот метод использует программное прерывание для уведомления процесса-получателя о том, что сообщение помещено в буфер (хотя это и эффективно, но усложняет программирование).

При использовании блокирующего примитива send может возникнуть ситуация, когда процесс-отправитель блокируется навсегда. Например, если процесс-получатель потерпел крах или отправленное было утеряно из-за сетевой ошибки. Чтобы предотвратить такую ситуацию, блокирующий примитив send, часто используя механизм тайм-аута, завершается со статусом ≪ошибка≫ (это же относится к примитиву receive).

Если при взаимодействии двух процессов оба примитива - send и receive – являются блокирующими, говорят, что процессы взаимодействуют по сети синхронно, в противном случае взаимодействие асинхронное (рис. 5.26.).

Синхронное взаимодействие является более простым в реализации, не требует обязательной буферизации сообщений, оно и более надежно, так как гарантирует процессу-отправителю, возобновившему свое выполнение, что его сообщение было получено. Однако недостатком является ограниченный параллелизм и возможность возникновения тупиковых ситуаций. Асинхронное взаимодействие не гарантирует высокой надежности обмена, требует организации большого буфера для исключения потерь сообщений, но обеспечивает высокую скорость обмена сообщения__

рис 5.26

 

Вызов удаленных процедур

Удобным способом взаимодействия ОС и приложений в сети является вызов удаленных процедур (Remote Procedure Call, RPC). Этот механизм представляет собой надстройку над системой обмена сообщениями ОС, поэтому в ряде случаев он позволяет более удобно и прозрачно организовать взаимодействие программ в сети, однако его полезность не универсальна [28].

Средства удаленного вызова процедур предназначены для обеспечения организации распределенных вычислений. Наибольшая эффективность RPC достигается в тех приложениях, в которых существует интерактивная связь между удаленными компонентами с небольшим временем ответов и относительно малым количеством передаваемых данных. Такие приложения называются RPC-ориентированными.

Суть метода состоит в том, чтобы позволить программам, находящимся на разных компьютерах, взаимодействовать с использованием семантики вызова и возврата из процедур так же, как если бы программы располагались на одном компьютере. Таким образом, обращение к сервису на удаленной машине выполняется как простой вызов процедуры. Популярность этого подхода обусловлена рядом достоинств метода.

1. Вызов процедуры - широко распространенная, используемая и понятная абстракция.

2. Использование вызова удаленных процедур позволяет задавать удаленный интерфейс как множество именованных операций определенного типа. Таким образом, интерфейс можно точно документировать, и распределенные программы смогут выполнять статическую проверку типов.

3. Поскольку интерфейсы стандартизованы и точно определены, коммуникационный код может генерироваться автоматически.

4. По этой же причине разработчики могут создавать клиентские и серверные модули, которые требуют минимальных доработок при переносе между различными компьютерными платформами и операционными системами.

Характерными чертами вызова локальных процедур являются:

• асимметричность - одна из взаимодействующих сторон является инициатором взаимодействия;

• синхронность - выполнение вызывающей процедуры блокируется с момента выдачи запроса и возобновляется только после возврата из вызываемой процедуры.

Хотя идея вызова удаленных процедур состоит в расширении хорошо известного и понятного механизма передачи управления и данных внутри программы, выполняющейся на одной машине, реализация удаленных вызовов существенно сложнее. Во-первых, вызывающая и вызываемая процедуры выполняются на разных машинах, а поэтому имеют разные адресные пространства. Это создает проблемы при передаче параметров и результатов, особенно если машины и их операционные системы не идентичны.

• Следующим отличием RPC от локального вызова является то, что он обязательно использует нижележащую систему обмена сообщениями, однако это не должно быть явно видно ни в определении процедур, ни в самих процедурах.

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

• при аварийном завершении вызывающей процедуры удаленно вызванные процедуры становятся ≪осиротевшими≫;

•при аварийном завершении удаленных процедур становятся ≪обездоленными родителями≫ вызывающие процедуры, которые будут безрезультатно ожидать ответа от удаленных процедур.

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

Чтобы понять работу RPC, рассмотрим сначала выполнение вызова локальной процедуры в автономном компьютере. Пусть это будет процедура записи данных в файл:

М = my_write (fd, buf, length);

Здесь fd - дескриптор файла (целое число), buf - указатель на массив символов, length - длина массива (целое число).

Чтобы осуществить вызов, вызывающая процедура помещает указанные параметры в стек в обратном порядке и передает управление вызываемой процедуре my_write. Эта пользовательская процедура после некоторых манипуляций с данными символьного массива buf выполняет системный вызов write для записи данных в файл, передавая ему параметры тем же способом, т. е. помещая их в стек (при реализации системного вызова они копируются в стек системы, а при возврате из него результат помещается в пользовательский стек). После того как процедура my_write выполнена, она помещает возвращаемое значение m в регистр, перемещает адрес возврата и возвращает управление вызывающей про__ которая выбирает параметры из стека, возвращая его в ис

Заметим, что в языке С параметры могут вызываться по ссылке (by name), представляющей собой адрес глобальной области памяти, в которой хранится параметр, или по значению (by value) в этом случае параметр копируется из исходной области памяти в локальную память процедуры, располагаемую обычно в стековом сегменте. В первом случае вызываемая процедура работает с оригинальными значениями параметров и их изменения сразу же видны вызывающей процедуре. Во втором случае вызываемая процедура работает с копиями значений параметров и их изменения никак не влияют на значение оригиналов этих переменных в вызывающей процедуре. Эти обстоятельства весьма существенны для RPC.

Решение о том, какой механизм передачи параметров использовать, принимается разработчиками языка. Иногда это зависит от типа передаваемых данных. В языке С, например, целые и другие скалярные данные всегда передаются по значению, а массивы - по ссылке.

Рис. 5.27 иллюстрирует передачу параметров вызываемой процедуре: стек до выполнения вызова write (а), стек во времени выполнения процедуры (б), стек после возврата в вызывающую программу (в).

Идея, положенная в основу RPC, состоит в том, чтобы вызов удаленной процедуры по возможности выглядел так же, как и вызов локальной процедуры, т. е. чтобы он был прозрачным для программиста: вызывающей процедуре не требуется знать, что вызываемая процедура находиться на другой машине, и наоборот.__

рис 5.27

 

Механизм RPC достигает прозрачности следующим образом. Когда вызываемая процедура действительно является удаленной, в библиотеку процедур вместо локальной реализации оригинального кода процедуры помещается другая версия процедуры, называемая клиентским стабом (stub - заглушка). На удаленный компьютер, который выполняет роль сервера процедур, помещается оригинальный код вызываемой процедуры, а также еще один стаб, называемый серверным стабом. Назначение стабов - организовать передачу параметров вызываемой процедуре и возврат значения процедуры через сеть, при этом код оригинальной процедуры, помещенной на сервер, должен быть полностью сохранен.

Стабы используют для передачи данных через сеть средства подсистемы обмена сообщениями, т. е. существующие в ОС примитивы send и receive. Иногда в подсистеме обмена сообщениями выделяется программный модуль, организующий связь стабов с примитивами передачи сообщений, называемый модулем RPCRuntime.

Подобно оригинальной процедуре, клиентский способ вызывается путем обычной передачи параметров через стек, однако затем вместо выполнения системного вызова, работающего с локальным ресурсом, происходит формирование сообщения, содержащего имя вызываемой процедуры и ее параметры (рис. 5.28).__

рис 5.28

 

 

Эта операция называется операцией упаковки параметров. После этого клиентский стаб обращается к примитиву send для передачи этого сообщения удаленному компьютеру, на который помещена реализация оригинальной процедуры. Получив из сети сообщение, ядро ОС удаленного компьютера вызывает серверный стаб, который извлекает из сообщения параметры и вызывает обычным образом оригинальную процедуру.

Для получения сообщения серверный стаб должен предварительно вызвать примитив receive, чтобы ядро знало, для кого пришло сообщение.

Серверный стаб распаковывает параметры вызова, имеющиеся в сообщении, и обычным образом вызывает оригинальную процедуру, передавая ей параметры через стек. После окончания работы процедуры серверный стаб упаковывает результат ее работы в новое сообщение и с помощью примитива send передает сообщение по сети клиентскому стабу, а тот возвращает обычным образом результат и управление вызывающей процедуре. Ни вызывающая, ни оригинальная процедуры не изменились от того, что они стали работать на разных компьютерах.

Стабы могут генерироваться вручную или автоматически. В первом случае программист использует средства, предоставляемые ему разработчиком средств RPC, но это достаточно объемная работа. При автоматическом способе применяется специальный язык определения интерфейса (Interface Definition Language), с помощью которого программист описывает интерфейс между клиентом и сервером RPC [28].

Остановимся на вопросе установления взаимодействия между удаленной процедурой и вызывающей программой. Процедура, устанавливающая такое взаимодействие, называется связыванием (binding). Непостоянное связывание (nonpersistent binding) означает, что логическая связь между двумя процессами устанавливается во время вызова удаленной процедуры и немедленно уничтожается после получения возвращаемого значения. Поскольку соединение требует поддержки информации о состоянии на обоих концах, оно потребляет ресурсы. Поэтому непостоянное связывание позволяет эти ресурсы сэкономить. Однако при этом возникают излишние накладные расходы по установлению связывания при каждом вызове процедуры, если такие вызовы осуществляются часто.

При постоянном связывании (persistent binding) установленная при вызове процедуры связь не уничтожается после окончания вызова. Поэтому она может использоваться и для других вызовов удаленных процедур. Если в течение предопределенного времени не осуществляется ни один вызов, такая связь с целью экономии ресурсовраз рывается. Этот метод хорошо подходит для интенсивного вызова удаленных процедур, позволяя множеству вызовов использовать одно и то же соединение.

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

Для обеспечения большей гибкости и большей степени параллельности реализуются различные варианты асинхронного RPC, сохраняющие простоту и удобство традиционных вызовов удаленных процедур. Асинхронные вызовы не блокируют вызывающую программу. Ответ может быть получен вызывающей программой тогда, когда он ей потребуется, так что клиент может продолжать работу параллельно с обработкой его запроса сервером.__

 

 

Обычно асинхронный RPC используется для того, чтобы позволить клиенту сделать несколько запросов к серверу, каждый со своим набором данных, не дожидаясь его ответов. Синхронизация клиента и сервера при этом может быть выполнена одним из двух способов:

1. Приложение-клиент дожидается ответа на все отправленные запросы.

2. По окончании выполнения последовательности асинхронных RPC клиент выполняет синхронный вызов удаленной процедуры. Сервер ответит на синхронный вызов только после окончания обработки всех предшествующих асинхронных вызовов.

Сетевые файловые системы

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

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

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

Сетевая файловая система в общем случае включает следующие элементы [28]:

• локальные файловые системы;

• интерфейсы локальной файловой системы;

• серверы сетевой файловой системы;

• клиенты сетевой файловой системы;

• интерфейсы сетевой файловой системы;

• протокол клиент-сервер сетевой файловой системы.

Клиенты сетевой ФС - это программы, работающие на многочисленных компьютерах, подключенных к сети. Эти программы обслуживают запросы приложений на доступ к файлам, хранящимся на удаленных компьютерах. Клиент сетевой ФС передает по сети запросы другому программному компоненту - серверу сетевой ФС, работающему на удаленном компьютере. Сервер, получив запрос, может выполнить его самостоятельно либо, что является более распространенным вариантом, передать запрос для обработки локальной файловой системе. После получения ответа от локальной ФС сервер передает его по сети__

рис 5.29

 

Клиент и сервер сетевой ФС взаимодействуют друг с другом по сети по определенному протоколу. В случае совпадения интерфейсов локальной и сетевой ФС этот протокол может быть достаточно простым. Одним из механизмов, используемых для этой цели, может быть механизм RPC.

В операционных системах Windows основной сетевой файловой службы является протокол SMB (Server Message Block), который был совместно разработан компаниями Microsoft, Intel и IBM. Его последние расширенные версии получили название Common Internet File System, CIFS.

Протокол работает на прикладном уровне модели OSI. Для передачи по сети своих сообщений SMB использует различные транспортные протоколы. Исторически первым таким протоколом был NetBIOS (и его более поздняя версия NetBEUI), но сейчас сообщения SMB могут передаваться и с помощью других протоколов (TCP/UDP и IPX).

SMB относится к классу протоколов, ориентированных на соединение. Его работа начинается с того, что клиент отправляет серверу специальное сообщение с запросом на установление соединения. Если сервер готов к установлению соединения, он отвечает сообщением-подтверждением. После установления соединения клиент может обращаться к серверу, передавая ему в сообщениях SMB команды манипулирования файлами и каталогами. В процессе работы возможно возникновение ряда ситуаций, которые могут повлиять на эффективность удаленного доступа к файлам [28]:

1. Отказ компьютера, на котором выполняется сервер сетевой файловой системы, во время сеанса связи с клиентом. Локальная ФС запоминает состояние последовательных операций, которые приложение выполняет с одним и тем же файлом, за счет ведения__ внутренней таблицы открытых файлов (системные вызовы open, read, write изменяют состояние этой таблицы). При крахе системы таблица открытых файлов теряется после перезагрузки серверного компьютера. В этом случае приложение, работаю-щее на клиентском компьютере, не может продолжить работу с файлами, открытыми до краха.

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

2. Большие задержки в обслуживании из-за запросов в сети и перезагрузки файлового сервера при подключении большого числа клиентов. Решением проблемы может быть кэширование файлов (частично или целиком) на стороне клиента. Однако в этом случае протокол должен учитывать возможность образования нескольких копий одного и того же файла, которые могут независимо модифицироваться разными пользователями, т. е. протокол должен обеспечивать согласованность копий файлов, имеющихся на разных компьютерах.

3. Потери данных и разрушение целостности файловой системы при сбоях и отказах компьютеров, играющих роль файловых серверов. Для повышения отказоустойчивости сетевой ФС можно хранить несколько копий каждого файла (или целиком всей ФС) на нескольких серверах. Такие копии файла называются репликами (replica).

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

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

Перечисленные проблемы решаются комплексно путем создания службы центра лизованной аутентификации, репликации, кэширования и др. Эти дополнительные службы находят свое отражение в протоколе взаимодействия клиентов и серверов, в результате чего создаются различные протоколы этого типа, поддерживающие тот или иной набор дополнительных функций. Поэтому для одной и той же локальной ФС могут существовать различные протоколы сетевой ФС (рис. 5.30). Так, к файловой системе NTFS сегодня можно получить доступ с помощью протоколов SMB, NCP (NetWare Control Protocol) и NFS (Network File System - протокол сетевой ФС компании Sun Microsystems, используемой в различных вариантах ОС семейства UNIX).

С другой стороны, с помощью одного и того же протокола может реализоваться удаленный доступ к локальным ФС разного типа. Например, протокол SMB используется для доступа не только к ФС типа FAT, но и ФС NTFS, HPFS (рис. 5.31). Эти ФС могут располагаться как на разных, так и на одном компьютере.__

рис 5.30

 

рис 5.31

 

 

Контрольные вопросы к главе 5

1. Какими преимуществами обладают сети по сравнению с раздельным использованием компьютеров?

2. Всегда ли совпадают физическая и логическая топологии сети?

3. Как классифицируются сети по величине охватываемой территории?

4. Какой компьютер может выполнять роль сервера в сети?

5. Что такое файловый сервер и сервер печати?

6. Какие функции выполняют регистрационные серверы?

7. Какие функции выполняют серверы удаленного доступа?

8. Что такое прокси-сервер?

9. Перечислите возможных клиентов компьютерной сети.

10. Что такое ≪толстый≫ и ≪тонкий≫ клиенты в компьютерной сети?

11. Как вы понимаете термин ≪сегментация≫ сети?

12. Что такое МАС-адрес?

13. Чем распределенная ОС отличается от сетевой? Существуют ли в настоящее время по-настоящему распределенные сетевые системы?

14. Перечислите основные компоненты сетевой ОС. Что такое сетевая служба? Какие сетевые службы вы можете назвать?

15. Часть сетевых служб направлена не на пользователя, а на администратора. Какие это службы?

16. Что представляли собой первые сетевые ОС? Какие подходы к созданию сетевых ОС используются в настоящее время?

17. Назовите характерные черты одноранговых сетей. В чем основная особенность многоранговой сети?

18. Что такое серверная ОС? Какие они бывают? Чем серверная ОС отличается от клиентской?

19. Сколько вариантов двухзвенных схем используется для распределенной обработки приложений?

20. Чем хороша двухзвенная обработка приложений при сотрудничестве сервера и клиента?

21. Есть ли преимущества у трехзвенной схемы обработки приложений, в чем они заключаются?

22. Как могут взаимодействовать процессы в распределенных системах?

23. Какие основные примитивы используются в транспортной системе сетевой ОС?

24. Как организуется синхронизация процессов в сети?

25. Что понимается под вызовом удаленных процедур?



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 249; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.12.34.178 (0.065 с.)