Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Межпроцессные коммуникации в UNIX



ОС UNIX в своей основе наиболее полно отвечает требованиям технологии «клиент–сервер». Эта универсальная модель служит основой построения любых сколь угодно сложных систем, в том числе и сетевых. Разработчики СУБД, коммуникационных систем, систем электронной почты, банковских систем и т. д. во всем мире широко используют технологию «клиент–сервер». Для построения программных систем, работающих по принципам модели типа «клиент–сервер» в UNIX существуют следующие механизмы:

¨ сигналы;

¨ семафоры;

¨ программные каналы;

¨ очереди сообщений;

¨ сегменты разделяемой памяти;

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

Многие из этих механизмов нам уже знакомы, поэтому рассмотрим их вкратце.

Сигналы

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

¨ обработка исключительных ситуаций;

¨ средства обработки внешних и внутренних прерываний;

¨ средства управления системой прерываний (маскирование и демаскирование).

Всем этим требованиям в UNIX отвечает техника сигналов, которая может не только воспринимать и обрабатывать сигналы, но и порождать их и посылать на другие машины (процессы). Сигналы могут быть синхронными, когда инициа­тор сигнала – сам процесс, и асинхронными, когда инициатор возникновения сигнала – интерактивный пользователь за терминалом. Источником асинхрон­ных сигналов может быть также ядро, когда оно контролирует определенные со­стояния аппаратуры, рассматриваемые как ошибочные.

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

Семафоры

Механизм семафоров, реализованный в ОС UNIX, является обобщением классического механизма семафоров общего вида, предложенного известным голландским специалистом профессором Дейкстрой. Семафор в ОС UNIX состоит из следующих элементов:

¨ значение семафора;

¨ идентификатор процесса, который хронологически последним работал с семафором;

¨ число процессов, ожидающих увеличения значения семафора;

¨ число процессов, ожидающих нулевого значения семафора.

Для работы с семафорами имеются следующие три системных вызова:

¨ semget – для создания и получения доступа к набору семафоров;

¨ semop – для манипулирования значениями семафоров (с помощью именно это­го системного вызова осуществляют синхронизацию процессов на основе использования семафоров);

¨ semctl – для выполнения разнообразных управляющих операций над набо­ром семафоров.

Системный вызов semget имеет следующий синтаксис:

id = semget(key, count, flag);

где параметры key и flag и возвращаемое значение системного вызова (id) имеют тот же смысл, что для других системных вызовов семейства «get», а параметр count задает число семафоров в наборе семафоров, обладающих одним и тем же ключом. После этого индивидуальный семафор идентифицируется дескриптором набора семафоров и номером семафора в этом наборе. Если к моменту вы­полнения системного вызова semget набор семафоров с указанным ключом уже существует, то обращающийся процесс получит соответствующий дескриптор, но так и не узнает о реальном числе семафоров в группе (хотя позже это все-та­ки можно узнать с помощью системного вызова semctl).

Основным системным вызовом для манипулирования семафором является semop:

oldval = semop(id, oplist, count);

где id – это ранее полученный дескриптор группы семафоров, орlist – массив описателей операций над семафорами группы, a count – размер этого массива. Значение, возвращаемое системным вызовом, является значением последнего обработанного семафора. Каждый элемент массива орlist имеет следующую струк­туру:

¨ номер семафора в указанном наборе семафоров;

¨ операция;

¨ флаги.

Если проверка прав доступа проходит нормально и указанные в массиве орlist номера семафоров не выходят за пределы общего размера набора семафоров, то системный вызов выполняется следующим образом. Для каждого элемента мас­сива орlist значение соответствующего семафора изменяется в соответствии со значением поля «операция»:

¨ если значение поля операции положительно, то значение семафора увеличивается на единицу, а все процессы, ожидающие увеличения значения семафо­ра, активизируются (пробуждаются в терминологии UNIX);

¨ если значение поля операции равно нулю, то если значение семафора также равно нулю, выбирается следующий элемент массива орlist. Если же значе­ние семафора отлично от нуля, то ядро увеличивает на единицу число про­цессов, ожидающих нулевого значения семафора, а обратившийся процесс переводится в состояние ожидания (усыпляется в терминологии UNIX);

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

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

Наконец, среди флагов-параметров системного вызова semop может содержаться флаг с символическим именем IPC_NOWAIT, наличие которого заставляет ядро ОС UNIX не блокировать текущий процесс, а лишь сообщать в ответных параметрах о возникновении ситуации, которая может привести к блокированию процес­са при отсутствии флага IPC_NOWAIT. Мы не будем обсуждать здесь возможности корректного завершения работы с семафорами при незапланированном заверше­нии процесса; заметим только, что такие возможности обеспечиваются.

Системный вызов semctl имеет формат

semctl (id, number, cmd, arg);

где id – это дескриптор группы семафоров, number – номер семафора в группе, cmd – код операции, а arg – указатель на структуру, содержимое которой интерпретируется по-разному, в зависимости от операции. В частности, с помощью semctl можно уничтожить индивидуальный семафор в указанной группе. Однако детали этого системного вызова настолько громоздки, что мы рекомендуем в случае необходимости обращаться к технической документации используемого варианта операционной системы.

Программные каналы

Мы с вами уже знакомились с программными каналами в главе 6. Однако рассмотрим этот механизм ещё раз, так сказать в его исходном, изначальном толковании.

Программные каналы (pipes) в ОС UNIX являются очень важным средством взаимодействия и синхронизации процессов. Теоретически программный канал позволяет взаимодействовать любому числу процессов, обеспечивая дисциплину FIFO (first-in-first-out). Другими словами, процесс, читающий из программного канала, прочитает самые давние записанные в программный канал данные. В традиционной реализации программных каналов для хранения данных использова­лись файлы. В современных версиях ОС UNIX для реализации программных ка­налов применяются другие средства IPC (в частности, очереди сообщений).

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

Для создания именованного программного канала (или получения к нему досту­па) используется обычный файловый системный вызов open. Для создания же неименованного программного канала существует специальный системный вы­зов pipe (исторически более ранний). Однако после получения соответствующих дескрипторов оба вида программных каналов используются единообразно с по­мощью стандартных файловых системных вызовов read, write и close.

Системный вызов pipe имеет следующий синтаксис:

plpe(fdptr);

где fdptr – это указатель на массив из двух целых чисел, в который после создания неименованного программного канала будут помещены дескрипторы, предназначенные для чтения из программного канала (с помощью системного вызова read) и записи в программный канал (с помощью системного вызова write). Дескрипторы неименованного программного канала – это обычные дескрипторы файлов, то есть такому программному каналу соответствуют два элемента табли­цы открытых файлов процесса. Поэтому при последующем использовании сис­темных вызовов read и write процесс совершенно не обязан отличать случай ис­пользования программных каналов от случая использования обычных файлов (собственно, на этом и основана идея перенаправления ввода/вывода и организа­ции конвейеров).

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

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

Окончание работы процесса с программным каналом (независимо от того, име­нованный он или неименованный) производится с помощью системного вызова close.

Очереди сообщений

Для обеспечения возможности обмена сообщениями между процессами этот ме­ханизм поддерживается следующими системными вызовами:

¨ msgget для образования новой очереди сообщений или получения дескрипто­ра существующей очереди;

¨ msgsnd для посылки сообщения (вернее, для его постановки в указанную оче­редь сообщений);

¨ msgrcv для приёма сообщения (вернее, для выборки сообщения из очереди со­общений);

¨ msgctl для выполнения ряда управляющих действий.

Ядро хранит сообщения в виде связного списка (очереди), а дескриптор очереди сообщений является индексом в массиве заголовков очередей сообщений.

Системный вызов msgget обладает стандартным для семейства «get» системных вызовов синтаксисом:

msgqid = msgget (key, flag);

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

Для посылки сообщения используется системный вызов msgsnd:

msgsnd(msgqid, msg, count, flag);

где msg – это указатель на структуру, содержащую определяемый пользователем целочисленный тип сообщения и символьный массив – собственно сообщение;

count задает размер сообщения в байтах, a flag определяет действия ядра при вы­ходе за пределы допустимых размеров внутренней буферной памяти.

Для приёма сообщения используется системный вызов msgrcv:

count = msgrcv(id, msg, maxcount, type, flag);

Здесь msg – это указатель на структуру данных в адресном пространстве пользо­вателя, предназначенную для размещения принятого сообщения; maxcount задаёт размер области данных (массива байтов) в структуре msg; значение type специфицирует тип сообщения, которое желательно принять; значение параметра flag указывает ядру, что следует предпринять, если в указанной очереди сообщений отсутствует сообщение с указанным типом. Возвращаемое значение системного вызова задаёт реальное число байтов, переданных пользователю.

Системный вызов

Msgctl(id, cmd, mstatbuf);

служит для опроса состояния описателя очереди сообщений, изменения его со­стояния (например, изменения прав доступа к очереди) и для уничтожения указанной очереди сообщений.

Разделяемая память

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

¨ shmget – создаёт новый сегмент разделяемой памяти или находит существую­щий сегмент с тем же ключом;

¨ shmat – подключает сегмент с указанным дескриптором к виртуальной памя­ти обращающегося процесса;

¨ shmdt – отключает от виртуальной памяти ранее подключенный к ней сегмент с указанным виртуальным адресом начала;

¨ shmctl – служит для управления разнообразными параметрами, связанными с существующим сегментом.

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

Синтаксис системного вызова shmget выглядит следующим образом:

shmid = shmget (key, size, flag);

Параметр size определяет желаемый размер сегмента в байтах. Далее работа происходит по общим правилам. Если в таблице разделяемой памяти находится элемент, содержащий заданный ключ, и права доступа не противоречат текущим характеристикам обращающегося процесса, то значением системного вызова является дескриптор существующего сегмента (и обратившийся процесс так и не узнает реального размера сегмента, хотя впоследствии его все-таки можно уз­нать с помощью системного вызова shmctl). В противном случае создаётся новый сегмент с размером не меньше установленного в системе минимального размера сегмента разделяемой памяти и не больше установленного максимального раз­мера. Создание сегмента не означает немедленного выделения под него основной памяти. Это действие откладывается до выполнения первого системного вызова подключения сегмента к виртуальной памяти некоторого процесса. Аналогично, при выполнении последнего системного вызова отключения сегмента от вирту­альной памяти соответствующая основная память освобождается.

Подключение сегмента к виртуальной памяти выполняется путем обращения к системному вызову shmat:

virtaddr = shmat(id, addr, flags);

Здесь id – это ранее полученный дескриптор сегмента, а addr – желаемый процессом виртуальный адрес, который должен соответствовать началу сегмента в виртуальной памяти. Значением системного вызова является реальный виртуальный адрес начала сегмента (его значение не обязательно совпадает со значе­нием прямого параметра addr). Если значением addr является нуль, ядро выбира­ет подходящий виртуальный адрес начала сегмента.

Для отключения сегмента от виртуальной памяти используется системный вы­зов shmdt:

shmdt(addr);

где addr – это виртуальный адрес начала сегмента в виртуальной памяти, ранее полученный от системного вызова shmat. При этом система гарантирует (на осно­ве использования таблицы сегментов процесса), что указанный виртуальный ад­рес действительно является адресом начала разделяемого сегмента в виртуаль­ной памяти данного процесса.

Для управления памятью служит системный вызов shmctl:

shmctl(id, cmd, shsstatbuf);

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

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

Во многих случаях взаимодействие процессов носит характер «клиент–сервер». Один из процессов («клиент») запрашивает у другого процесса («сервера») не­которую услугу (сервис) и не продолжает свое выполнение до тех пор, пока эта услуга не будет выполнена (и пока процесс-клиент не получит соответствующие результаты). Видно, что семантически такой режим взаимодействия эквивален­тен вызову процедуры. Отсюда и соответствующее название. Кроме этого, ОС UNIX по своей идеологии идеально подходит для того, чтобы быть сетевой операционной системой. И на её основе можно создавать распределённые системы и организовывать распределённые вычисления. Свойства переносимости позволя­ют создавать «операционно однородные» сети, включающие разнородные компь­ютеры. Однако остаётся проблема разного представления данных в компьютерах разной архитектуры. Поэтому одной из основных идей RPC является автомати­ческое обеспечение преобразования форматов данных при взаимодействии про­цессов, выполняющихся на разнородных компьютерах.

Реализация технологии вызовов удаленных процедур (remote procedure call – RPC) достаточно сложна, поскольку этот механизм должен обеспечить работу взаимодействующих процессов, которые находятся на разных компьютерах. Если в случае обращения к процедуре, расположенной на том же компьютере, процесс общается с ней через стек или общие области памяти, то в случае удаленного вызова передача параметров процедуре превращается в передачу запроса по сети. Соответственно, и получение результата так же осуществляется посредством использования сетевых механизмов.

Удаленный вызов процедур включает следующие шаги [70]:

¨ процесс-клиент осуществляет локальный вызов процедуры, которую называ­ют «заглушкой» (stub). Задача этого модуля-заглушки – принять аргументы, преобразовать их в стандартную форму и сформировать сетевой запрос. Упа­ковка аргументов и создание сетевого запроса называется сборкой (marshal­ling);

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

Операционная система Linux

Linux – это современная POSIX-совместимая и UNIX-подобная операционная система для персональных компьютеров и рабочих станций.

Как известно, Linux – это свободно распространяемая версия UNIX, которая пер­воначально была разработана Линусом Торвальдсом (Linus Torvalds) (torvalds@ kruuna.helsinki.fi) в университете Хельсинки (Финляндия). Все компоненты системы, включая исходные тексты, распространяются с лицензией на свободное копирование и установку для неограниченного числа пользователей.

Linux был создан с помощью многих UNIX-программистов и энтузиастов из Ин­тернета. К данному проекту добровольно подключились те, кто имеет достаточно навыков и способностей развивать систему. Большинство программ Linux разра­ботано в рамках проекта GNU из Free Software Foundation в Кэмбридже, Массачусетс. Но в него внесли лепту также программисты всего мира.

Изначально Linux создавался как «самодельная» UNIX-подобная реализация для ПК типа IBM PC с процессором i80386. Однако Linux стал настолько популярен и его нынче поддерживает такое большое число компаний, что в настоящее время имеется реализация этой ОС практически для всех типов процессоров и компьютеров на их основе. На базе ОС Linux создаются и встроенные системы, и суперкомпьютеры. Система поддерживает кластеризацию и большинство современных интерфейсов и технологий.

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

Linux – это полноценная многозадачная многопользовательская операционная система (точно так же, как и все другие версии UNIX). Это означает, что одновременно много пользователей могут работать на одной машине, одновременно выполняя много программ. Linux достаточно хорошо совместим с рядом стандартов для UNIX (насколько можно говорить о стандартизации UNIX) на уровне исходных текстов, включая IEEE POSIX.l, System V и BSD. Такая совмести­мость учитывалась при его создании. Большинство свободно распространяемых по сети Интернет программ для UNIX может быть откомпилировано для LINUX практически без особых изменений. Кроме того, все исходные тексты для Linux, включая ядро, драйверы устройств, библиотеки, пользовательские программы и инструментальные средства распространяются свободно. Другие специфические внутренние черты Linux включают контроль работ по стандарту POSIX (исполь­зуемый оболочками, такими как csh и bash), псевдотерминалы (pty), поддержку национальных и стандартных клавиатур динамически загружаемыми драйвера­ми клавиатур.

Linux поддерживает различные типы файловых систем для хранения данных. Некоторые файловые системы, такие как файловая система ext2fs, были созданы специально для Linux. Поддерживаются также другие типы файловых систем, например Minix-1 и Xenix. Реализована также система управления файлами на основе FAT, позволяющая непосредственно обращаться к файлам, находящим­ся в разделах с этой файловой системой. Поддерживается и файловая система ISO 9660 CD–ROM для работы с дисками CD-ROM. Имеются системы управле­ния файлами и на томах с HPFS и NTFS, правда, они работают только на чте­ние файлов. Созданы варианты системы управления файлами и для доступа к FAT32.

Linux, как и все UNIX-системы, обеспечивает полный набор протоколов стека TCP/IP для сетевой работы. Это включает драйверы устройств для многих популярных сетевых адаптеров технологии Ethernet, протоколы SLIP (serial line Internet protocol, обеспечивающий доступ по TCP/IP при последовательном со­единении), PLIP (parallel line Internet protocol), PPP (point-to-point protocol), NFS (network file system) и т. д. Поддерживается весь спектр клиентов и услуг TCP/IP, таких как FTP, telnet, NNTP и SMTP. Очень часто на компьютерах, работаю­щих под управлением Linux, реализуют DNS-сервер, WWW-серверы (Apache), файерволы для защиты локальных сетей при работе в Интернете, почтовые сер­веры, сервер DHCP.

Ядро Linux сразу было создано с учётом возможностей защищённого режима про­цессоров Intel 80386 и 80486. В частности, Linux использует парадигму описания памяти в защищённом режиме и другие новые свойства процессоров. В отличие от старых версий UNIX, в которых задачи выгружались во внешнюю память на магнитных дисках целиком, ядро Linux поддерживает загрузку только нужных страниц. То есть с диска в память загружаются те сегменты программы, которые действительно используются. Возможно использование одной страницы, физически один раз загруженной в память, несколькими выполняемыми программами, то есть реентерабельность кода, присущая всем UNIX-системам, сохранилась. В настоящее время имеются ядра для этой системы, оптимизированные для ра­боты с процессорами Intel и AMD последнего поколения, хотя основные архи­тектурные особенности защищенного режима работы изменились мало.

Ядро также поддерживает универсальный пул памяти для пользовательских про­грамм и дискового кэша. При этом для кэширования может использоваться вся свободная память, и наоборот, кэш уменьшается при работе больших программ. Этот механизм агрессивного кэширования позволяет увеличить производительность системы.

Выполняемые программы используют динамически связываемые библиотеки, то есть выполняемые программы могут совместно использовать библиотечную про­грамму, представленную одним физическим файлом на диске. Это позволяет выполняемым файлам занимать меньше места на диске, особенно тем, которые многократно используют библиотечные функции. Есть также статические связываемые библиотеки для тех, кто желает пользоваться отладкой на уровне объектных кодов или иметь «полные» выполняемые программы, которые не нужда­ются в разделяемых библиотеках. В Linux разделяемые библиотеки динамически связываются во время выполнения, позволяя программисту заменять библиотеч­ные модули своими собственными.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-05-29; Просмотров: 736; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.037 с.)
Главная | Случайная страница | Обратная связь