Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебники 6066.doc
Скачиваний:
5
Добавлен:
01.05.2022
Размер:
484.86 Кб
Скачать

3.4. Понятие системы программирования

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

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

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

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

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

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

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

жащие в себе наиболее часто используемые функции и

подпрограммы в виде готовых объектных модулей;

- загрузчики, обеспечивающие подготовку готовой про-

граммы к выполнению;

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

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

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

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

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

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

программирования. Язык Makefile стал стандартным средством, единым для компиляторов всех разработчиков.

Такая структура средств разработки существовала достаточно долгое время, а в некоторых случаях она используется и по сей день (особенно при создании системных программ). Ee широкое распространение было связано с тем, что сама по себе вся эта структура средств разработки была очень удобной при пакетном выполнении программ на компьютере, что способствовало ее повсеместному применению в эпоху «mainframe».

Следующим шагом в развитии средств разработки стало появление так называемой «интегрированной среды разработки». Интегрированная среда объединила в себе возможности текстовых редакторов исходных текстов программ и командный язык компиляции. Пользователь (разработчик исходной программы) теперь не должен был выполнять всю последовательность действий от порождения исходного кода до его выполнения, от него также не требовалось описывать этот процесс с помощью системы команд в Makefile. Теперь ему было достаточно только указать в удобной интерфейсной форме состав необходимых для создания программы исходных модулей и библиотек. Ключи, необходимые компилятору и другим техническим средствам, также задавались в виде интерфейсных форм настройки.

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

Создание интегрированных сред разработки стало возможным благодаря бурному развитию персональных компьютеров и появлению развитых средств интерфейса пользователя (сначала текстовых, а потом и графических). Их появление на рынке определило дальнейшие развитие такого рода технических средств. Пожалуй, первой удачной средой такого рода можно признать интегрированную среду программирования Turbo Pascal на основе языка Pascal производства фирмы Borland. Ee широкая популярность определила тот факт, что со временем все разработчики компиляторов обратились к созданию интегрированных средств разработки для своих продуктов.

Развитие интегрированных сред несколько снизило требования к профессиональным навыкам разработчиков исходных программ.

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

Дальнейшее развитие средств разработки также тесно связано с повсеместным распространением развитых средств графического интерфейса пользователя. Такой интерфейс стал неотъемлемой составной частью многих современных OC и так называемых графических оболочек [1,2]. Co временем он стал стандартом «де-факто» практически во всех современных прикладных программах.

Это не могло не сказаться на требованиях, предъявляемым к средствам разработки программного обеспечения. B их состав были сначала включены соответствующие библиотеки, обеспечивающие поддержку развитого графического интерфейса пользователя и взаимодействие с функциями API (application program interface, прикладной программный интерфейс) операционных систем. A затем для работы с ними потребовались дополнительные средства, обеспечивающие разработку внешнего вида интерфейсных модулей. Такая работа была уже более характерна для дизайнера, чем для программиста.

Для описания графических элементов программ потребовались соответствующие языки. Ha их основе сложилось понятие «ресурсов» (resources) прикладных программ.

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

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

Весь этот комплекс программно-технических средств в настоящие время составляет новое понятие, которое здесь названо «системой программирования».

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

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

Термин «ресурсы» следует признать не слишком удачным, так как этим словом обозначаются очень многие понятия, связанные с вычислительными системами (например, ресурсы вычислительного процесса). Однако так сложилось, что этот термин применяется при работе со средствами разработки, поэтому придется принять его.

С точки зрения терминологии компиляторы ресурсов правильнее было бы назвать «трансляторы», так как в результате своей работы они обычно порождают не объектный файл, а некий промежуточный код ресурсов. Однако термин «компилятор ресурсов» стал уже общепринятым.

B качестве основных тенденций в развитии современных систем программирования следует указать внедрение в них средств разработки на основе так называемых «языков четвертого поколения» - 4GL (four generation languages), - а также поддержка систем «быстрой разработки программного обеспечения» - RAD (rapid application development).

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

программа. Языки четвертого поколения являются следующим (четвертым по счету) этапом в развитии систем программирования.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- появлением интегрированных сред разработки;

- появление возможностей аппаратной поддержки

средств отладки во многих вычислительных системах.

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

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

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

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

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

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

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

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

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

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

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

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

Динамические библиотеки в отличие от традиционных (статических) библиотек подключаются к программе не в момент ее компоновки, а непосредственно в ходе выполнения, как только программа затребовала ту или иную функцию,

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

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

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

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

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