The design of the unix operating system by Maurice J

Вид материалаРеферат
11.3 Взаимодействие в сети
Подобный материал:
1   ...   45   46   47   48   49   50   51   52   ...   55
с процессом структуры восстановления и выполняет над указанным

семафором все обусловленные действия.

Ядро создает структуру восстановления всякий раз, когда про-

цесс уменьшает значение семафора, а удаляет ее, когда процесс

увеличивает значение семафора, поскольку установочное значение


----------------TT-------┐ ----------------TT-------T-------┐

│ идентификатор ││ │ │ идентификатор ││ │ │

│ семафора ││ semid │ │ семафора ││ semid │ semid │

+---------------++-------+ +---------------++-------+-------+

│ номер семафора││ 0 │ │ номер семафора││ 0 │ 1 │

+---------------++-------+ +---------------++-------+-------+

│ установочное ││ │ │ установочное ││ │ │

│ значение ││ 1 │ │ значение ││ 1 │ 1 │

L---------------++-------- L---------------++-------+--------

(а) После первой операции (б) После второй операции

----------------TT-------┐

│ идентификатор ││ │

│ семафора ││ semid │

+---------------++-------+

│ номер семафора││ 0 │ пусто

+---------------++-------+

│ установочное ││ │

│ значение ││ 1 │

L---------------++--------

(в) После третьей операции (г) После четвертой операции

Рисунок 11.17. Последовательность состояний списка структур

восстановления


структуры равно 0. На Рисунке 11.17 показана последовательность

состояний списка структур при выполнении программы с параметром

'a'. После первой операции процесс имеет одну структуру, состоя-

щую из идентификатора semid, номера семафора, равного 0, и уста-

новочного значения, равного 1, а после второй операции появляется

вторая структура с номером семафора, равным 1, и установочным

значением, равным 1. Если процесс неожиданно завершается, ядро

проходит по всем структурам и прибавляет к каждому семафору по

единице, восстанавливая их значения в 0. В частном случае ядро

уменьшает установочное значение для семафора 1 на третьей опера-

ции, в соответствии с увеличением значения самого семафора, и

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

становится нулевым. После четвертой операции у процесса больше

нет структур восстановления, поскольку все установочные значения

стали нулевыми.

Векторные операции над семафорами позволяют избежать взаимных

блокировок, как было показано выше, однако они представляют из-

вестную трудность для понимания и реализации, и в большинстве

приложений полный набор их возможностей не является обязательным.

Программы, испытывающие потребность в использовании набора сема-

форов, сталкиваются с возникновением взаимных блокировок на поль-

зовательском уровне, и ядру уже нет необходимости поддерживать

такие сложные формы системных функций.

Синтаксис вызова системной функции semctl:

semctl(id,number,cmd,arg);


Параметр arg объявлен как объединение типов данных:

union semunion {

int val;

struct semid_ds *semstat; /* описание типов см. в При-

* ложении */

unsigned short *array;

} arg;

Ядро интерпретирует параметр arg в зависимости от значения

параметра cmd, подобно тому, как интерпретирует команды ioctl

(глава 10). Типы действий, которые могут использоваться в пара-

метре cmd: получить или установить значения управляющих парамет-

ров (права доступа и др.), установить значения одного или всех

семафоров в наборе, прочитать значения семафоров. Подробности по

каждому действию содержатся в Приложении. Если указана команда

удаления, IPC_RMID, ядро ведет поиск всех процессов, содержащих

структуры восстановления для данного семафора, и удаляет соот-

ветствующие структуры из системы. Затем ядро инициализирует ис-

пользуемые семафором структуры данных и выводит из состояния при-

останова все процессы, ожидающие наступления некоторого связанно-

го с семафором события: когда процессы возобновляют свое выполне-

ние, они обнаруживают, что идентификатор семафора больше не явля-

ется корректным, и возвращают вызывающей программе ошибку.

11.2.4 Общие замечания


Механизм функционирования файловой системы и механизмы взаи-

модействия процессов имеют ряд общих черт. Системные функции типа

"get" похожи на функции creat и open, функции типа "control" пре-

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

на функцию unlink. Тем не менее, в механизмах взаимодействия про-

цессов отсутствуют операции, аналогичные операциям, выполняемым

системной функцией close. Следовательно, ядро не располагает све-

дениями о том, какие процессы могут использовать механизм IPC, и,

действительно, процессы могут прибегать к услугам этого механиз-

ма, если правильно угадывают соответствующий идентификатор и если

у них имеются необходимые права доступа, даже если они не выпол-

нили предварительно функцию типа "get". Ядро не может автомати-

чески очищать неиспользуемые структуры механизма взаимодействия

процессов, поскольку ядру неизвестно, какие из этих структур

больше не нужны. Таким образом, завершившиеся вследствие возник-

новения ошибки процессы могут оставить после себя ненужные и не-

используемые структуры, перегружающие и засоряющие систему. Нес-

мотря на то, что в структурах механизма взаимодействия после

завершения существования процесса ядро может сохранить информацию

о состоянии и данные, лучше все-таки для этих целей использовать

файлы.

Вместо традиционных, получивших широкое распространение фай-

лов механизмы взаимодействия процессов используют новое прост-

ранство имен, состоящее из ключей (keys). Расширить семантику

ключей на всю сеть довольно трудно, поскольку на разных машинах

ключи могут описывать различные объекты. Короче говоря, ключи в

основном предназначены для использования в одномашинных системах.

Имена файлов в большей степени подходят для распределенных систем

(см. главу 13). Использование ключей вместо имен файлов также

свидетельствует о том, что средства взаимодействия процессов яв-

ляются "вещью в себе", полезной в специальных приложениях, но не

имеющей тех возможностей, которыми обладают, например, каналы и

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

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

темных средств, поэтому включать их в состав ядра вряд ли следо-

вало бы. Тем не менее, их использование в составе пакетов прик-

ладных программ тесного взаимодействия дает лучшие результаты по

сравнению со стандартными файловыми средствами (см. Упражнения).

11.3 ВЗАИМОДЕЙСТВИЕ В СЕТИ


Программы, поддерживающие межмашинную связь, такие, как

электронная почта, программы дистанционной пересылки файлов и

удаленной регистрации, издавна используются в качестве специаль-

ных средств организации подключений и информационного обмена.

Так, например, стандартные программы, работающие в составе элект-

ронной почты, сохраняют текст почтовых сообщений пользователя в

отдельном файле (для пользователя "mjb" этот файл имеет имя "/usr

/mail/mjb"). Когда один пользователь посылает другому почтовое

сообщение на ту же машину, программа mail (почта) добавляет сооб-

щение в конец файла адресата, используя в целях сохранения це-

лостности различные блокирующие и временные файлы. Когда адресат

получает почту, программа mail открывает принадлежащий ему почто-

вый файл и читает сообщения. Для того, чтобы послать сообщение на

другую машину, программа mail должна в конечном итоге отыскать на

ней соответствующий почтовый файл. Поскольку программа не может

работать с удаленными файлами непосредственно, процесс, протекаю-

щий на другой машине, должен действовать в качестве агента ло-

кального почтового процесса; следовательно, локальному процессу

необходим способ связи со своим удаленным агентом через межмашин-

ные границы. Локальный процесс является клиентом удаленного обс-

луживающего (серверного) процесса.

Поскольку в системе UNIX новые процессы создаются с помощью

системной функции fork, к тому моменту, когда клиент попытается

выполнить подключение, обслуживающий процесс уже должен существо-

вать. Если бы в момент создания нового процесса удаленное ядро

получало запрос на подключение (по каналам межмашинной связи),

возникла бы несогласованность с архитектурой системы. Чтобы избе-

жать этого, некий процесс, обычно init, порождает обслуживающий

процесс, который ведет чтение из канала связи, пока не получает

запрос на обслуживание, после чего в соответствии с некоторым

протоколом выполняет установку соединения. Выбор сетевых средств

и протоколов обычно выполняют программы клиента и сервера, осно-

вываясь на информации, хранящейся в прикладных базах данных; с

другой стороны, выбранные пользователем средства могут быть зако-

дированы в самих программах.

В качестве примера рассмотрим программу uucp, которая обслу-

живает пересылку файлов в сети и исполнение команд на удалении

(см. [Nowitz 80]). Процесс-клиент запрашивает в базе данных адрес


и другую маршрутную информацию (например, номер телефона), откры-

вает автокоммутатор, записывает или проверяет информацию в деск-

рипторе открываемого файла и вызывает удаленную машину. Удаленная

машина может иметь специальные линии, выделенные для использова-

ния программой uucp; выполняющийся на этой машине процесс init

порождает getty-процессы - серверы, которые управляют линиями и

получают извещения о подключениях. После выполнения аппаратного

подключения процесс-клиент регистрируется в системе в соответс-

твии с обычным протоколом регистрации: getty-процесс запускает

специальный интерпретатор команд, uucico, указанный в файле "/etc

/passwd", а процесс-клиент передает на удаленную машину последо-

вательность команд, тем самым заставляя ее исполнять процессы от

имени локальной машины.

Сетевое взаимодействие в системе UNIX представляет серьезную

проблему, поскольку сообщения должны включать в себя как информа-

ционную, так и управляющую части. В управляющей части сообщения

может располагаться адрес назначения сообщения. В свою очередь,

структура адресных данных зависит от типа сети и используемого

протокола. Следовательно, процессам нужно знать тип сети, а это

идет вразрез с тем принципом, по которому пользователи не должны

обращать внимания на тип файла, ибо все устройства для пользова-

телей выглядят как файлы. Традиционные методы реализации сетевого

взаимодействия при установке управляющих параметров в сильной

степени полагаются на помощь системной функции ioctl, однако в

разных типах сетей этот момент воплощается по-разному. Отсюда

возникает нежелательный побочный эффект, связанный с тем, что

программы, разработанные для одной сети, в других сетях могут не

заработать.

Чтобы разработать сетевые интерфейсы для системы UNIX, были

предприняты значительные усилия. Реализация потоков в последних

редакциях версии V располагает элегантным механизмом поддержки

сетевого взаимодействия, обеспечивающим гибкое сочетание отдель-


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

задач. Следующий раздел посвящен краткому описанию метода решения

данных проблем в системе BSD, основанного на использовании гнезд.

11.4 ГНЕЗДА


В предыдущем разделе было показано, каким образом взаимодейс-

твуют между собой процессы, протекающие на разных машинах, при

этом обращалось внимание на то, что способы реализации взаимо-

действия могут быть различаться в зависимости от используемых


протоколов и сетевых средств. Более того, эти способы не всегда

применимы для обслуживания взаимодействия процессов, выполняющих-

ся на одной и той же машине, поскольку в них предполагается су-

ществование обслуживающего (серверного) процесса, который при вы-

полнении системных функций open или read будет приостанавливаться

драйвером. В целях создания более универсальных методов взаимо-

действия процессов на основе использования многоуровневых сетевых

протоколов для системы BSD был разработан механизм, получивший

название "sockets" (гнезда) (см. [Berkeley 83]). В данном разделе

мы рассмотрим некоторые аспекты применения гнезд (на пользова-

тельском уровне представления).


Процесс-клиент Процесс-сервер

│ │

│ │

L--┐ ----

--------------------------+--┐ ---+--------------------------┐

│ Уровень гнезд │ │ Уровень гнезд │

+-------------------------+--+ +--+--------------------------+

│ TCP │ │ TCP │

│ Уровень протоколов │ │ │ │ Уровень протоколов │

│ IP │ │ IP │

+-------------------------+--+ +--+--------------------------+

│ Драйвер│ │ Драйвер │

│ Уровень устройств Ethernet│ │Ethernet Уровень устройств │

L-------------------------+--- L--+---------------------------

L---┐ -----

│ │

│ │

С е т ь

Рисунок 11.18. Модель с использованием гнезд


Структура ядра имеет три уровня: гнезд, протоколов и уст-

ройств (Рисунок 11.18). Уровень гнезд выполняет функции интерфей-

са между обращениями к операционной системе (системным функциям)

и средствами низких уровней, уровень протоколов содержит модули,

обеспечивающие взаимодействие процессов (на рисунке упомянуты

протоколы TCP и IP), а уровень устройств содержит драйверы, уп-

равляющие сетевыми устройствами. Допустимые сочетания протоколов

и драйверов указываются при построении системы (в секции конфигу-

рации); этот способ уступает по гибкости вышеупомянутому потоко-

вому механизму. Процессы взаимодействуют между собой по схеме

клиент-сервер: сервер ждет сигнала от гнезда, находясь на одном

конце дуплексной линии связи, а процессы-клиенты взаимодействуют

с сервером через гнездо, находящееся на другом конце, который мо-

жет располагаться на другой машине. Ядро обеспечивает внутреннюю

связь и передает данные от клиента к серверу.

Гнезда, обладающие одинаковыми свойствами, например, опираю-

щиеся на общие соглашения по идентификации и форматы адресов (в

протоколах), группируются в домены (управляемые одним узлом). В

системе BSD 4.2 поддерживаются домены: "UNIX system" - для взаи-

модействия процессов внутри одной машины и "Internet" (межсете-

вой) - для взаимодействия через сеть с помощью протокола DARPA

(Управление перспективных исследований и разработок Министерства

обороны США) (см. [Postel 80] и [Postel 81]). Гнезда бывают двух

типов: виртуальный канал (потоковое гнездо, если пользоваться

терминологией Беркли) и дейтаграмма. Виртуальный канал обеспечи-

вает надежную доставку данных с сохранением исходной последова-

тельности. Дейтаграммы не гарантируют надежную доставку с сохра-

нением уникальности и последовательности, но они более экономны в

смысле использования ресурсов, поскольку для них не требуются

сложные установочные операции; таким образом, дейтаграммы полезны

в отдельных случаях взаимодействия. Для каждой допустимой комби-

нации типа домен-гнездо в системе поддерживается умолчание на ис-

пользуемый протокол. Так, например, для домена "Internet" услуги

виртуального канала выполняет протокол транспортной связи (TCP),

а функции дейтаграммы - пользовательский дейтаграммный протокол

(UDP).

Существует несколько системных функций работы с гнездами.

Функция socket устанавливает оконечную точку линии связи.

sd = socket(format,type,protocol);

Format обозначает домен ("UNIX system" или "Internet"), type -

тип связи через гнездо (виртуальный канал или дейтаграмма), а

protocol - тип протокола, управляющего взаимодействием. Дескрип-

тор гнезда sd, возвращаемый функцией socket, используется другими

системными функциями. Закрытие гнезд выполняет функция close.

Функция bind связывает дескриптор гнезда с именем:

bind(sd,address,length);

где sd - дескриптор гнезда, address - адрес структуры, определяю-

щей идентификатор, характерный для данной комбинации домена и

протокола (в функции socket). Length - длина структуры address;

без этого параметра ядро не знало бы, какова длина структуры,

поскольку для разных доменов и протоколов она может быть различ-

ной. Например, для домена "UNIX system" структура содержит имя

файла. Процессы-серверы связывают гнезда с именами и объявляют о

состоявшемся присвоении имен процессам-клиентам.

С помощью системной функции connect делается запрос на подк-

лючение к существующему гнезду:

connect(sd,address,length);

Семантический смысл параметров функции остается прежним (см.

функцию bind), но address указывает уже на выходное гнездо, обра-

зующее противоположный конец линии связи. Оба гнезда должны ис-

пользовать одни и те же домен и протокол связи, и тогда ядро

удостоверит правильность установки линии связи. Если тип гнезда -

дейтаграмма, сообщаемый функцией connect ядру адрес будет исполь-

зоваться в последующих обращениях к функции send через данное

гнездо; в момент вызова никаких соединений не производится.

Пока процесс-сервер готовится к приему связи по виртуальному

каналу, ядру следует выстроить поступающие запросы в очередь на

обслуживание. Максимальная длина очереди задается с помощью сис-

темной функции listen:

listen(sd,qlength)

где sd - дескриптор гнезда, а qlength - максимально-допустимое

число запросов, ожидающих обработки.


---------------------┐ --------------------------┐

│ Процесс-клиент │ │ Процесс-сервер │

│ │ │ │ │ │

│ │ │ │ ------ │

│ │ │ │ │ │

│ │ │ │listen addr accept addr│

L---------+----------- L-----+-------------------

│ │

L---------------------------

Рисунок 11.19. Прием вызова сервером


Системная функция accept принимает запросы на подключение,

поступающие на вход процесса-сервера:

nsd = accept(sd,address,addrlen);

где sd - дескриптор гнезда, address - указатель на пользователь-

ский массив, в котором ядро возвращает адрес подключаемого клиен-

та, addrlen - размер пользовательского массива. По завершении вы-

полнения функции ядро записывает в переменную addrlen размер

пространства, фактически занятого массивом. Функция возвращает

новый дескриптор гнезда (nsd), отличный от дескриптора sd. Про-

цесс-сервер может продолжать слежение за состоянием объявленного

гнезда, поддерживая связь с клиентом по отдельному каналу (Рису-

нок 11.19).


Функции send и recv выполняют передачу данных через подклю-

ченное гнездо. Синтаксис вызова функции send:

count = send(sd,msg,length,flags);

где sd - дескриптор гнезда, msg - указатель на посылаемые данные,

length - размер данных, count - количество фактически переданных

байт. Параметр flags может содержать значение SOF_OOB (послать

данные out-of-band - "через таможню"), если посылаемые данные не

учитываются в общем информационном обмене между взаимодействующи-

ми процессами. Программа удаленной регистрации, например, может

послать out-of-band сообщение, имитирующее нажатие на клавиатуре

терминала клавиши "delete". Синтаксис вызова системной функции

recv:

count = recv(sd,buf,length,flags);

где buf - массив для приема данных, length - ожидаемый объем дан-

ных, count - количество байт, фактически переданных пользователь-

ской программе. Флаги (flags) могут быть установлены таким обра-

зом, что поступившее сообщение после чтения и анализа его содер-

жимого не будет удалено из очереди, или настроены на получение