Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Otvety_k_zachetu_po_OS_UNIX.docx
Скачиваний:
11
Добавлен:
16.09.2019
Размер:
547 Кб
Скачать
☆

4. Среда программирования unix

- программный интерфейс UNIX

  • Первая реализация программного интерфейса для создания сетевых программ была основана на понятии "socket" и введена в версии BSD 4.2 операционной системы Unix. В 1985 г., с выходом версии System V Release 2.0p, появляется понятие потоков (STREAMS), а начиная с версии Unix SVR3 (System V Release 3.0) — библиотеки интерфейса транспортного уровня (TLI — Transport Layer Interface). Наиболее полно современный набор программных интерфейсов для сетевого программирования представлен в Unix SVR4. Программист может выбрать наиболее удобный и привычный для себя интерфейс среди sockets, STREAMS, TLI, POSIX1.

  • Сетевое программирование, как и любое другое, можно условно разбить на два основных вида: прикладное и системное. Прикладное программирование использует уже созданные сетевые разработки и решает прикладные задачи. В терминах семиуровневой модели взаимодействия открытых систем (OSI) данный тип программирования в основном решает задачи 4-го и выше лежащих уровней. Системное программирование, как правило, имеет дело с более низкими уровнями.

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

  • STREAMS

  • Подсистема потоков является одной из самых важных в Unix SVR4. Основная идея данной подсистемы заключается в том, что любой поток информации, организованный в виде сообщений, последовательно обрабатывается набором программных модулей. Существует простая возможность для загрузки нового модуля (программного элемента) в стек обработки сообщения. Модули загружаются (и выгружаются) в систему по стековой дисциплине LIFO (последний пришел — первый ушел). Таким образом, становится возможным набирать из простых "кирпичиков" (модулей) достаточно сложное программное решение. Единственное ограничение заключается в необходимости строго следовать системным соглашениям об интерфейсах, т. е. строго придерживаться требований к входным и выходным данным. Сообщением может быть практически любая информация. В случае протокола IP сообщением является IP-пакет. Внутри стека модулей передается не само сообщение, а указатель на него, что существенно сокращает объем памяти, требуемый для обработки.

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

  • Еще один важный элемент каждого потока — это драйвер, который может быть либо драйвером некоторого физического устройства, либо программным драйвером (псевдодрайвером — pseudo-driver), обеспечивающим определенный сервис для приложения. Хорошим примером псевдодрайвера является эмулятор терминала для Telnet. Число модулей, встраиваемых для обработки потока, не ограничено.

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

  • Привычный механизм программного канала (pipe) реализован в Unix SVR4 на основе механизма потоков, что позволяет использовать один дескриптор для двунаправленной передачи данных.

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

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

  • Sockets

  • Программный интерфейс Sockets впервые появился в BSD Unix 4.1c в 1982 г. Основная цель создания данного интерфейса заключалась в возможно более полном сокрытии деталей конкретной реализации сетевых примитивов. Механизм, предоставляемый с помощью Sockets, не зависит от того, действительно ли устанавливается сетевое соединение или же обмен информацией происходит на локальной машине.

  • Интерфейс Sockets реализован практически во всех версиях операционной системы Unix. Как программный объект, socket является абстракцией и представляет собой конечную точку коммуникационной структуры. Каждый socket связан с коммуникационным доменом, т. е. с группой протоколов, использующих одинаковые транспорт и правила трансляции имен. Наиболее известен Internet-домен, в который входят протоколы TCP, UDP, IP и т. д.

  • Работа с Sockets вo многом напоминает файловые операции, часто для передачи данных можно использовать обычные файловые операции. В приведенном на рис. 2 фрагменте программы представлены оба подхода к передаче данных через интерфейс Sockets: с помощью обычного системного вызова write() и с помощью специфического для socket вызова sendto(). Следует обратить внимание на функцию (макроподстановку) htons, которая предназначена для преобразования информации от представления, принятого на данном компьютере, к представлению, стандартному для передачи информации по сети. Необходимость ее использования, равно как и обратной функции ntohs, связана с различным представлением данных для различных компьютеров. Порядок байтов в слове зависит от архитектуры процессора. В зависимости от конкретного процессора указанные функции могут выполнять преобразования информации, а могут и ничего не делать.

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

  • WinSock

  • Исторически сетевое программирование развивалось в среде Unix. Необходимость использовать сетевые ресурсы и иметь доступ к сети Internet заставила фирмы-производители программного обеспечения для персональных компьютеров вводить программные интерфейсы, аналогичные рассмотренным. Наибольшую популярность в средах Windows, Windows 95 и Windows NT завоевала библиотека WinSock, реализующая интерфейс Sockets. Основное отличие WinSock от интерфейса Sockets, принятого в операционных системах Unix, состоит в названиях функций. В название большинства функций WinSock добавлено слово "socket".

  • В первую очередь, это сделано для тех функций, которые в ОС Unix могут использоваться двояко — как для файловых операций, так и для операций над socket. Так, например, в Unix для управления поведением файлов или socket применяется вызов ioctl, которому в библиотеке WinSock соответствует функция ioctlsocket.

  • Пример программы, аналогичной рассмотренным выше, приведен на рис. 4. Легко заметить, что в данном примере последовательность действий по передаче информации через socket почти полностью совпадает с рассмотренной ранее для среды Unix, за исключением одной детали: в библиотеке WinSock для передачи сообщения нельзя использовать операцию write.

  • Важно отметить, что ОС Windows не является в полной мере многозадачной средой, и в некоторых случаях использование библиотеки WinSock может приводить к неожиданным результатам: например, ожидание прихода информации через socket не приводит к переключению на другую задачу, а вызывает блокировку всей системы.

  • RPC

  • Естественным продолжением программирования в распределенной среде является механизм RPC (Remote Procedure Call) — удаленный вызов процедур. Подобно тому, как интерфейс Sockets служит своеобразным продолжением файл-ориентированного программного интерфейса ОС Unix, только уже для межкомпьютерного взаимодействия, механизм RPC является естественным продолжением работы с локальными процедурами, но уже в сетевой среде.

  • Основная проблема для программиста, связанная с различным порядком байтов в машинных словах для разных компьютеров, решается путем использования протокола XDR (eXternal Data Representation).

  • Схема взаимодействия прикладной программы и системы состоит в следующем.

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

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

  • Все эти действия сильно облегчаются при использовании специальных средств, например компилятора rpсgen, который преобразует описание структур данных и профилей процедур в соответствующие исходные тексты. Пример описания простой программы для последующей обработки утилитой rpcgen приведен на рис. 5. Данная программа состоит из процедуры, возвращающей строку, с единственным параметром типа int. Рассмотрим некоторые синтаксические конструкции, приведенные в этом описании. Конструкция вида program HOST_NAME_PROG {...} = 20000076 предназначена для привязки данной программы к номеру сервиса, предоставляемого на серверной части. Конструкция version HOST_NAME_VERS {...} = 1; предназначена для дополнительной идентификации программы по номеру версии, а конструкцию nametype READ_HOST_ NAME(int) = 1; — для нумерации процедур внутри программы (т.е. процедура READ_HOST_NAME будет иметь номер 1). Результатом обработки этого описания при помощи утилиты rpcgen будет необходимый набор компонентов для клиентской и серверной частей программы, использующей интерфейс RPC.

- программы

  • ОС UNIX одновременно является операционной средой использования существующих прикладных программ и средой разработки новых приложений. Новые программы могут писаться на разных языках (Фортран, Паскаль, Модула, Ада и др.). Однако стандартным языком программирования в среде ОС UNIX является язык Си (который в последнее время все больше заменяется на Си++). Это объясняется тем, что во-первых, сама система UNIX написана на языке Си, а, во-вторых, язык Си является одним из наиболее качественно стандартизованных языков.

  • Поэтому программы, написанные на языке Си, при использовании правильного стиля программирования обладают весьма высоким уровнем мобильности, т.е. их можно достаточно просто переносить на другие аппаратные платформы, работающие как под управлением ОС UNIX, так и под управлением ряда других операционных систем (например, DEC Open VMS или MS Windows NT). Более подробно мы рассмотрим принципы мобильного программирования в среде ОС UNIX в четвертой части курса.

  • Приведем краткий обзор процесса разработки программы на языке Си (или Си++), которую можно выполнить в среде ОС UNIX. Любая выполняемая программа компонуется из одного или нескольких объектных файлов. Поэтому разработка программы начинается с создания исходных файлов, содержащих текст на языке Си. Эти файлы могут содержать определения глобальных имен переменных и/или функций (имен, которые могут быть видимы из других файлов), а также ссылки на внешние имена (объявленные как глобальные в одном из других файлов, которые будут составлять программу).

  • Текстовые файлы производятся с помощью одного из текстовых редакторов, поддерживаемых в среде UNIX. Традиционным текстовым редактором ОС UNIX является упоминавшийся в первом разделе редактор vi, исходная версия которого была разработана Биллом Джоем. Этот редактор достаточно старый, он может работать практически на всех терминалах и не является в полном смысле оконным.

  • В последние годы все большую популярность получает редактор Emacs (разработанный и непрерывно совершенствуемый президентом Free Software Foundation Ричардом Столлманом). Это очень мощный многооконный редактор, который позволяет не только писать программы (и другие тексты), но также и компилировать, компоновать и отлаживать программы (а также делать многое другое, например, принимать и отправлять электронную почту). Основным недостатком редактора Emacs является исключительно большой набор (более 200) функциональных клавиш.

  • Следует, правда, заметить, что при использовании Emacs в оконной системе X он обеспечивает более удобный интерфейс.

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

  • После того, как текстовый файл создан, его нужно откомпилировать для получения объектного файла. Наиболее популярными компиляторами для языка Си в среде ОС UNIX сейчас являются pcc (Ритчи и Томпсон) и gcc (Ричард Столлман). Оба эти компилятора являются полностью мобильными и обладают возможностью генерировать код для разнообразных компьютеров, т.е. эти компиляторы могут быть установлены практически на любой аппаратной платформе под управлением ОС UNIX.

  • Можно отметить следующие преимущества gcc. Во-первых, этот компилятор свободно, т.е. бесплатно (вместе со своими исходными текстами) распространяется Free Software Foundation. Во-вторых, gcc тщательно поддерживается и сопровождается. В-третьих, начиная с версии 2.0, gcc может компилировать программы, написанные на языках Си, Си++ и Objective C, а результирующая выполняемая программа может быть скомпонована из объектных файлов, полученных из текстовых файлов на любом из этих языков. В-четвертых, открытость исходных текстов gcc и тщательно разработанная структура компилятора позволяют сравнительно просто добавлять к gcc новые кодогенераторы. Относительным недостатком gcc является то, что используемый диалект языка Си включает слишком много расширений по сравнению со стандартом ANSI/ISO (однако имеется режим, в котором компилятор указывает все расширенные конструкции языка, встречающиеся в компилируемой программе).

  • Оба компилятора обрабатывают программу в два этапа. На первом этапе синтаксически правильный текст на языке Си преобразуется в текст на языке ассемблера. На втором этапе на основе текста на языке ассемблера генерируются машинные коды и получается объектный файл. Исторически в ОС UNIX использовались различные форматы объектных модулей. Для обеспечения совместимости с предыдущими версиями почти все они поддерживаются в современных версиях компиляторов. Однако в настоящее время преимущественно используется формат COFF (Common Object File Format). При желании можно остановить процесс компиляции после первого этапа и получить для изучения файл с текстом программы на языке ассемблера.

  • После того, как необходимый для построения выполняемой программы набор объектных файлов получен, необходимо произвести компоновку выполняемой программы. В ОС UNIX компоновщик выполняемых программ называется редактором связей (link editor) и обычно вызывается командой ld. Редактору связей указывается набор объектных файлов и набор библиотек, из которых нужно черпать недостающие для компоновки программы.

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

  • В ОС UNIX имеется несколько стандартных библиотек. В большинстве случаев наиболее важной является библиотека ввода/вывода (stdio). Грамотное использование стандартных библиотек способствует созданию легко переносимых прикладных программ (мы вернемся к обсуждению стандартных библиотек ОС UNIX в четвертой части курса).

  • Выполняемая программа может быть запущена в интерактивном режиме как команда shell или выполнена в отдельном процессе, образуемом уже запущенной программой.

Процессы

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

  • Управление процессами подробно обсуждается в третьей части курса. Тем не менее кратко опишем здесь общий подход. Для образования нового процесса и запуска в нем программы используются два системных вызова (примитива ядра ОС UNIX) - fork() и exec (имя-выполняемого-файла). Системный вызов fork приводит к созданию нового адресного пространства, состояние которого абсолютно идентично состоянию адресного пространства основного процесса (т.е. в нем содержатся те же программы и данные).

  • Другими словами, сразу после выполнения системного вызова fork основной и порожденный процессы являются абсолютными близнецами; управление и в том, и в другом находится в точке, непосредственно следующей за вызовом fork. Чтобы программа могла разобраться, в каком процессе она теперь работает - в основном или порожденном, функция fork возвращает разные значения: 0 в порожденном процессе и целое положительное число (идентификатор порожденного процесса) в основном процессе.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]