Главная страница
Навигация по странице:

  • Лекция 21 Командный язык ОС Unix cshell (CSH)

  • Лекции Системное ПО. Лекция Структура и основные компоненты вычислительной системы


    Скачать 0.71 Mb.
    НазваниеЛекция Структура и основные компоненты вычислительной системы
    Дата13.09.2022
    Размер0.71 Mb.
    Формат файлаdoc
    Имя файлаЛекции Системное ПО.doc
    ТипЛекция
    #675338
    страница13 из 15
    1   ...   7   8   9   10   11   12   13   14   15

    Лекция 20

     

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

    • проектирование

    • кодирование

    • тестирование

    • отладка

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

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

     

    Этап кодирования

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

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

     

    Кросс-трансляторы.

    Если рассматривать системы трансляции, то есть еще один вид трансляторов — кросс-трансляторы (и кросс-компиляторы). Кросс транслятор работает на некотором типе вычислительной системы, которая называется инструментальная ЭВМ. Инструментальная ЭВМ может характеризоваться своей архитектурой и/или операционным окружением, которое функционирует на ней. Кросс-транслятор обеспечивает перевод программы, записанной в нотации некоторого языка, в код вычислительной системы, отличной от инструментальной ЭВМ. Та вычислительная система,для которой генерируется код, называется объектной ЭВМ, и соответственно, тот код, который мы получаем, называется объектным кодом (это не тоже, что объектный модуль). Например, компьютеру, который управляет двигательной установкой самолета, совершенно ненужно иметь операционную среду, которая обеспечит работу пользователя по разработке программ для него. Ему совершенно не нужно иметь средства редактирования текста, трансляции и т.д., потому что у него одна функция — управлять двигательной установкой. На этом компьютере будет работать операционная система реального времени. Для создания программ для такого рода компьютеров и используются системы кросс-программирования и кросс-трансляторы. На обычной машине типа PC может быть размещен транслятор, который будет генерировать код для заданного компьютера.

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

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

     

    Обработка модульной программы.

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

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

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

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

    В эту же схему также часто добавляется этап оптимизации программы, причем оптимизация может происходить до этапа трансляции (т.е. в терминах исходного языка) или/и после трансляции (в терминах машинного кода). Например до трансляции можно вычислить все константные подвыражения и т.д. Для машин типа PC этап оптимизации может быть не столь важен, потому что этот вопрос обычно разрешается покупкой какого-нибудь более быстрого компонента, но есть класс машин (mainframe), для которых этот этап необходим.

    Давайте посмотрим на проблему кодирования с другой стороны. Мы посмотрим как устроен этап трансляции.

    Каждый транслятор при обработке программы выполняет следующие действия.

      1. Лексический анализ.

      2. Синтаксический анализ.

      3. Семантический анализ и генерация кода.

     

    Лексический анализ.

    Лексический анализатор производит анализ исходного текста на предмет правильности записи лексических единиц входного языка. Затем он переводит программу из нотации исходного текста в нотацию лексем.

    Лексические единицы — это минимальные конструкции, которые могут быть продекларированы языком. К лексическим единицам относятся:

    • идентификаторы

    • ключевые слова

    • код операции

    • разделители

    • константы

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

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

    Тип лексемы

    № лексемы

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

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

     

    Синтаксический анализ.

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

     

    Семантический анализ.

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

    Система программирования и трансляции — очень наукоемкая область программного обеспечения. Организация трансляторов — это было первое применение теоретических достижений науки, которые заключались в следующем. За счет возможности использования тех или иных грамматик (наборов формальных правил построения лексических конструкций и синтаксических правил), можно разделить программную реализацию лексических и синтаксических анализаторов на два компонента. Первый компонент — это программа, которая в общем случае ничего не знает о том языке, который она будет анализировать. Второй компонент — это набор данных, представляющий из себя формальное описание свойств языка, который мы анализируем. Совмещение этих двух компонентов, позволяет автоматизировать процесс построения лексических и синтаксических анализаторов, а также генераторов кода, для различных языков программирования. Современные системы программирования в своем составе имеют средства автоматизации построения компиляторов. Для ОС UNIX есть пакет LEX — пакет генерации лексических анализаторов, и есть пакет YACC — для генерации синтаксических анализаторов. Это все достигается за счет возможности формализации свойств языка, и использования этого формального описания, как параметров для тех или иных инструментальных средств.

     

    Проходы трансляторов.

    Мы с вами посмотрели на транслятор с точки зрения функциональных этапов. Но очень часто мы слышим об однопроходных трансляторах, двухпроходных, трехпроходных, и т.д. С проблемой трансляции связано понятие "проход". Проход — это полный просмотр некоторого представления исходного текста программы.

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

    Если мы посмотрим Си-компилятор, с которым вы работаете, то скорее всего он двухпроходный. Первый проход — это работа препроцессора. После первого прохода появляется чистая Си-программа без всяких препроцессорных команд. На Втором проходе происходит лексический, синтаксический и семантический анализ, и в итоге вы получаете объектную программу в виде ассемблера.

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

    Make-файл. К этой же проблеме кодирования относится средство поддержки разработки программных проектов. Одним из популярных средств, ориентированных на работу одного или нескольких программистов, является т.н. make-средство. Название происходит от соответствующей команды ОС UNIX. C make-командой связан т.н. make-файл, в котором построчно указываются взаимосвязи всевозможных файлов, получаемых при трансляции, редактировании связей, и т.д., и те действия, которые надо выполнить, если эти взаимосвязи нарушаются. В частности можно сказать, что некоторый исполняемый файл зависит от группы объектных файлов, и если эта связь нарушена, то надо выполнить команду редактирования связей (link ...). Что значит нарушение зависимости и что значит связь? Make-команда проверяет существование этих объектных файлов. Если они существуют, то времена их создания должны быть более ранние, чем время создания исполняемого файла. В том случае, если это правило будет нарушено (а это проверяет make-команда), то будет запущен редактор связей (link), который заново создаст исполняемый файл. Тем самым такое средство позволяет нам работать с программой, состоящей из большого количества модулей, и не заботитьсяо том, соответствует ли в данный момент времени исполняемый файл набору объектных файлов или не соответствует (можно просто запустить make-файл).

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

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

     

    Этапы тестирования и отладки

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

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

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

    Лекция 21

     

    Командный язык ОС Unix cshell (CSH)

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

    Традиционными интерпретаторами команд Unix являются: sh, csh, bash и некоторые другие. В принципе, все эти интерпретаторы похожи друг на друга и являются развитием sh.

    Рассмотрим csh.

    Интерпретатор распознает структуру вводимой команды. Предполагается, что команда, это последовательность слов, заканчивающихся некоторым символом. Слово — последовательность символов, не содержащая разделителей. Разделители — это набор фиксированных символов, позволяющих разделять слова в командной строке (например, пробел, запятая, точка с запятой и т.д.), при этом разделители имеют в csh свою интерпретацию. В частности разделитель “||” — создание конвейера между командами (стандартный ввод одной команды будет стандартным выводом другой, например, “ls||more”).

    Unix поддерживает в своих интерпретаторах так называемые метасимволы. Они обычно встречаются в словах команд и интерпретируются по специальным правилам. Рассмотрим некоторые из них:

    “*”: означает любую последовательность символов. Например, команда “rm *” удалит все файлы, которые не начинаются с символа “точка” (чтобы их удалить следует набрать “rm *.*”);

    “?”: означает, что на месте этого символа может быть любой символ. Например “rm ?” удалит все файлы, имена которых состоят из одного символа;

    “квадратные скобки”: внутри скобок указываются альтернативные группы. Например, “[abc]” означает, что вместо квадратных скобок может быть подставлен один символ из набора “abc”. Можно указать диапазон, например “[0-9]”;

    Командный интерпретатор csh позволяет объединять команды. Для этого также используются метасимволы:

    “(...)” — Если внутри скобок перечислены команды, например “(cd /etc; ls -la || grep pas)”, то фактически запустится новый интерпретатор команд, который выполнит последовательность команд, находящуюся в круглых скобках. Заметим отличие такого запуска от обычного — если мы выполним команду “cd /etc” обычным способом, то наш интерпретатор сменит каталог, тогда как при запуске еще одного интерпретатора каталог у основного интерпретатора не изменится.

    “{...}” — все команды, перечисленные внутри фигурных скобок будут запущены последовательно слева направо, но при этом на стандартный вывод будет положена объединенная последовательность стандартных выводов всех команд. Например, “{more t.b; more t.c} > tt.b”. В результате в файле tt.b окажутся стандартные выводы обеих команд “more”, если бы фигурных скобок не было, то указатель перенаправления ввода вывода относился бы лишь к последней, в нашем случае второй, команде.

    Интерпретатор команд имеет набор встроенных команд. Все команды, которые мы используем делятся на два типа: первый — это те, которые находятся в отдельных файлах, их можно добавлять, удалять, модифицировать; второй — которые “зашиты” внутри интерпретатора. В частности, команда “kill” — команда интерпретатора, то есть передача сигнала осуществляется от имени интерпретатора. Есть, например, встроенная команда “alias” — она используется для переименования существующих команд.

    Интерпретатор команд csh позволяет работать с предысторией, то есть он организовывает буферизацию последних n команд и допускает доступ к списку этих команд — их можно запускать еще раз, редактировать и снова выполнять. Соответственно, интерпретатор команд csh имеет возможность именовать строки из списка предыстории, в частности, для ссылки на командную строку из списка предыстории можно пользоваться следующими конструкциями: “!<...>”, например, “!!” означает повторить последнюю команду.Это также может быть номер: “!10” — повторить командную строку с номером 10. Если номер положительный — то это абсолютный номер команды, если отрицательный — относительный от текущего номера (например, “!-1” указывает выполнить предыдущую команду). Также в качестве параметра могут быть некоторые контекстные ссылки.

    Интерпретатор команд предоставляет возможность программирования на уровне csh. Для этого в csh предусмотрена декларация переменных, присвоения им значения, а также набор высокоуровневых операторов, которые по своей семантике похожи на операторы языка C (поэтому он и называется cshell). Оперируя с переменными, csh можно создавать программы и выполнять многие другие действия. Кроме всего, имеются предопределенные имена переменных cshell, которые отвечают за настройку системы (например, количество строк в предыстории, ее сохранение — переменные HISTORY, SAVEHISTORY). Кроме таких переменных, через которые осуществляется настройка и которые мы можем изменять, есть еще один класс переменных cshell — это внутренние переменные, такие переменные, которые имеют предопределенные имена и определяют свое значение через внутренние функции интерпретатора команд.

    В частности, есть переменная path. Ее суть в том, что в ней хранится (она является текстовым массивом) текстовые строки, содержащие полные имена некоторых каталогов; в соответствии с содержимым path cshell осуществляет поиск файлов, которые являются командами, введенными пользователями. Мы говорили о том, что кроме команд, встроенных в интерпретатор, вUnix больше специальных команд нет, и любой исполняемый файл может являться командой. Но тогда возникает вопрос — мы ввели некоторое имя name — где будет осуществляться поиск файла name? В текущем каталоге? Но это неправильно, так как, например, команда “ls” лежит не в текущем каталоге. Но с другой стороны хотелось бы иметь возможность запускать файлы и из него тоже. Содержимое переменной path определяет порядок каталогов, в которых будет осуществлен поиск команды, если ее нет в текущем каталоге (например,если path имеет значение “.;/bin;/usr/bin”, то поиск будет начат в текущем каталоге, затем в “/bin” и затем в “/usr/bin”, это означает, что если мы сделаем команду “ls” и поместим ее в текущий каталог, то она перекроет “ls”, лежащий в “/bin”).

    Другие переменные:

    home — имя домашнего каталога.

    ignoreeof — установка этой переменной блокирует завершение сеанса по вводу Ctrl-D.

    prompt — в системе можно варьировать приглашение (по умолчанию — “$”), оно может быть достаточно интеллектуальным (включить дату, путь и прочее).

    Мы с вами рассмотрели path. Представьте себе, что мы вводим некоторую строку name, но такого файла нет. Тогда у нас будет осуществлен поиск по всем каталогам, перечисленным в path со всеми вытекающими последствиями (открытием каталогов, чтением, поиском и т.п.), это долго, а если учесть, что среда у нас многопользовательская, то если для каждого пользователя система будет так загружаться, то расходы получаются сумасшедшая. Но Unix — достаточно разумная ОС. При входе пользователя в систему на основании значения переменной path формируется некая hash-таблица имен исполняемых файлов, находящихся во всех перечисленных каталогах, эта таблица учитывает, естественно, порядок директорий и прочие атрибуты. И в этом случае поиск команды будет осуществляться следующим образом: сначала будет просмотрен текущий каталог, если там ничего найдено не будет, то последует обращение к быстрой hash-таблице. Эта скорость оплачена тем, что при входе в систему часть времени загрузки тратится на составление таблицы. Однако, здесь есть проблема — если в каталог из path была добавлена новая команда, то в таблицу она не попадет, так как появилась после формирования таблицы. В этом случае можно поступить двояко — указать полное имя команды (не использовать hash-таблицу) или переформировать hash-таблицу (для этого существует команда rehash), и, соответственно, произойдет та же процедура, что происходит при входе в систему.

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

    Все переменные окружения можно модифицировать средствами cshell’а.

     

    Работа с файлами.

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

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

    Специальные файлы cshell.

    Cshell имеет две разновидности этих файлов, это файлы, которые могут выполняться при старте cshell, и которые могут выполняться при завершении сеанса.

    При старте cshell работает с файлами:

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

    .login — Этот файл запускается после “.cshrc“, он запускается при входе в систему (“.cshrc” запускается при запуске cshell), то есть при запуске еще одного csh для нового сеанса будет запущен “.cshrc”, но “.login” запущен уже не будет. С учетом этого следует его использовать (указывать, например, переобозначения команд).

    При завершении работы cshell выполняется

    .logout — Здесь можно выполнить действия, необходимые для завершения работы с сеансом (например, установить команду уничтожения промежуточных файлов).

    Имеется стандартный файл, который образуется в ходе работы системы — это файл “.history”, если определена возможность savehistory, то история пишется в этот файл.

    Вот, наверное, и все, что стоит сказать о cshell в общих словах, остальное можно посмотреть в литературе, специально описывающих данный командный язык.
    1   ...   7   8   9   10   11   12   13   14   15


    написать администратору сайта