cobegin
х:= 1; х:= 2; х:= 3; coend; write (x); Исполнение команд между ключевыми словами cobegin и coend происходит параллельно (рис. 2). Пара операторных скобок cobegin-coend приводит к генерации потоков в рамках многозадачной системы. Оператор cobe-gin не накладывает условий на относительный порядок исполнения отдельных процессов, а оператор coend достигается только тогда, когда все процессы внутри блока завершены. Если бы исполнение было последовательным, то окончательное значение переменной х было бы равно 3. Для параллельных процессов конечный результат однозначно предсказать нельзя; задачи выполняются, по крайней мере, с внешней точки зрения, в случайной последовательности. Поэтому окончательное значение х в приведенном примере может быть как 1, так и 2 или 3. Рисунок 2. - Граф очередности для операторов cobegin – coend Иногда в технической литературе термин "параллельное программирование" используется как синоним мультипрограммирования. Однако эти понятия несколько различаются по смыслу. Параллельное программирование -это абстрактный процесс разработки программ, который потенциально может исполняться параллельно, вне зависимости от программно-аппаратной среды. Иными словами, предполагается, что каждая задача реализуется на собственном виртуальном процессоре. С другой стороны, мультипрограммирование представляет собой практический способ исполнения нескольких программ на одном центральном процессоре или в распределенной вычислительной системе. Параллельное программирование более трудоемко, чем последовательное, поскольку способность человека следить за развитием связанных процессов, и исследовать их взаимодействие, ограничена. Программирование в реальном времени основано на параллельном программировании и включает в себя технику повышения эффективности и скорости исполнения программ - управление прерываниями, обработку исключений и непосредственное использование ресурсов операционной системы. Кроме того, программы реального времени требуют специальных методов тестирования.
Лекция 4.2. Языки программирования реального времени 1. Требования к языкам программирования реального времени. 2. Языки разработки для систем реального времени.
1. Требования к языкам программирования реального времени Основными критериями при выборе языка для разработки приложения реального времени являются: 1. Получение наивысшей производительности приложения реального времени. Из этого требования вытекает, что язык должен быть компилируемого (как C, C++), а не интерпретируемого (как Java) типа, и для него должен существовать компилятор с высокой степенью оптимизации кода. Для современных процессоров качество компилятора особенно важно, поскольку для них оптимизация может ускорять работу программы в несколько раз по сравнению с не оптимизированным вариантом, причем часто оптимизирующий компилятор может породить код более быстрый, чем написанный на ассемблере. Технологии оптимизации развиваются достаточно медленно и часто требуются годы на разработку высокоэффективного компилятора. Поэтому обычно для более старых и с более простой структурой языков имеются более качественные компиляторы, чем для достаточно молодых, и сложно устроенных языков. 2. Получение доступа к ресурсам оборудования либо посредством языковых конструкций, либо посредством имеющихся для выбранного языка библиотечных функций. 3. Возможность вызова процедур, написанных на другом языке, например, на языке ассемблера. Из этого требования вытекает, что последовательность вызова подпрограмм (механизм именования объектов, передачи аргументов и получения возвращаемого значения) должна быть документирована для выбранного языка. 4. Переносимость приложения, под которой обычно понимают, как возможность его скомпилировать другим компилятором, имеющимся на той же платформе, так и возможность его скомпилировать на другой платформе и/или другой операционной системе. 5. Поддержка объектно-ориентированного подхода стала в последнее время необходимостью, зачастую выходя в списке требований на первое место. Это объясняет использование языка Java в ОСРВ. Программирование в реальном времени требует специальных средств, которые не всегда встречаются в обычных языках последовательного программирования. Язык или операционная система для программирования в реальном времени должны предоставлять следующие возможности: – описание параллельных процессов; – переключение процессов на основе динамических приоритетов, которые могут изменяться, в том числе и прикладными процессами; – синхронизацию процессов; – обмен данными между процессами; – функции, связанные с часами и таймером, абсолютное и относительное время ожидания; – прямой доступ к внешним аппаратным портам; – обработку прерываний; – обработку исключений. Немногие языки обеспечивают все эти возможности. Большинство имеет лишь часть из них, хотя для определенных приложений этого оказывается достаточно. Некоторые компании разработали специальные языки для поддержки своих собственных аппаратных средств. Эти языки не претендуют на универсальность и ориентированы скорее на конкретные ЭВМ и их интерфейсы. Обычно они базируются на существующих языках - FORTRAN, BASIC - с расширениями, включающими функции реального времени, о чем свидетельствуют их названия типа "Process BASIC" и "Real-time FORTRAN". Некоторые языки не поддерживают программирования в реальном времени в строгом смысле, но они легко расширяются, например С и C++. В 1970-е годы широкую поддержку получила концепция единого переносимого многоцелевого языка программирования. В результате был разработан язык ADA. Его главная идея состоит в том, что среда программирования, то есть язык, должна быть полностью отделена от аппаратных средств. Программист не должен сталкиваться с деталями машинного уровня, а работать только в терминах абстрактных структур и типов данных. Опыт показал не реалистичность такого подхода. Универсальные, сильно типизированные языки программирования гарантируют определенный уровень надежности программы, но в то же время ограничивают гибкость. Быстрое развитие технических средств, предъявляет новые требования, которые не могли быть предусмотрены в существующих языках, и многие программисты чувствуют ограничения, используя, не самые современные языки программирования. Цена надежности языка - сложность и громоздкость, а генерируемый при этом компилятором код - избыточен и малоэффективен. Открытый язык типа С, основанный на ограниченном количестве базовых идей, обладает большей гибкостью и предоставляет опытному программисту больше возможностей. Не существует наилучшего языка -для каждого приложения и среды необходимо подбирать свои средства и при этом учитывать квалификацию и предпочтения разработчиков. 2. Языки разработки для систем реального времени Ассемблер. Обеспечивает получение наивысшей производительности, прямой доступ к оборудованию, возможность вызова любых процедур на других языках. Однако, приложения получаются не переносимыми, объектно-ориентированный подход отсутствует. Обычно ассемблер используется только для написания небольших и четко локализованных фрагментов приложения, таких, как обработчики прерываний, драйверы устройств, критические по времени исполнения секции. Язык программирования ADA. Первым полным языком программирования в реальном времени является ADA. В середине 1970-х годов Министерство обороны США для сокращения расходов на разработку и сопровождение своих систем управления реального времени приняло решение ввести единый язык программирования в качестве альтернативы сотням использовавшихся тогда языков. В 1979 году министерство одобрило предложения, выдвинутые французской компанией Honeywell Bull. Язык назван в честь Августы Ады Байрон, графини Лавлейс (Augusta Ada Byron, Countess of Lovelace, 1815-1852), которую можно считать первым программистом в истории - она писала программы для аналитической машины (механического компьютера, который никогда не был построен), спроектированной английским изобретателем Чарльзом Бэббиджем (Charles Babbage). Язык ADA является полной средой разработки программ с текстовым редактором, отладочными средствами, системой управлениями библиотеками и т.д. Спецификации ADA закреплены американским стандартом ANSI/MIL-STD-1815A и включают средства контроля соответствия этому стандарту. Не допускаются диалекты языка - для сертификации компилятор должен правильно выполнить все эталонные тесты. Структура языка ADA похожа на структуру языка Pascal, но его возможности значительно шире, в особенности применительно к системам реального времени. Процессу в ADA соответствует задача, которая выполняется независимо от других задач на выделенном виртуальном процессоре, то есть параллельно с другими задачами. Задачи могут быть связаны с отдельными прерываниями и исключениями, и работать как их обработчики. Новым понятием, введенным в ADA, является пакет - модуль со своими собственными описаниями типов данных, переменных и подпрограмм, в котором явно указано, какие из программ и переменных доступны извне. Пакеты могут компилироваться отдельно с последующим объединением в один исполняемый модуль. Это средство поддерживает модульную разработку программ и создание прикладных библиотек. В начале 1990-х годов язык ADA был пополнен новыми функциями для объектно-ориентированного программирования и программирования в реальном времени. Машинно-ориентированное программирование низкого уровня поддерживается ADA не достаточно эффективно - это следствие постулата, что все задачи должны решаться средствами высокого уровня. Например, для операций ввода/вывода в ADA используются прикладные пакеты с заранее определенными функциями для управления аппаратными интерфейсами и доступа к внешним данным. Основным недостатком ADA является его сложность, которая делает язык трудным для изучения и применения. Существующие компиляторы являются дорогостоящими продуктами и требуют мощных процессоров. До сих пор ADA не получил ожидавшейся популярности, и сомнительно, что это когда-нибудь произойдет. Языки С и C++. Язык программирования С, несмотря на отсутствие в нем многих средств, которые теоретики считают необходимыми для хорошего языка программирования, пользуется большим успехом, начиная с 1980-х годов и по настоящее время. Этот язык стал популярным для всех приложений, требующих высокой эффективности, в частности для программ реального времени. Для обычных микропроцессоров, используемых в системах управления, имеются С-компиляторы и системы разработки многих производителей. В промышленности существует явная тенденция к широкому применению языка С и операционной системы UNIX, которая сама написана на С, поскольку приложения, написанные на С, машинно-независимы и требуют не больших усилий для адаптации к работе в различной аппаратной среде. Философией С является разбиение программ на функции. С - слаботи-пизированный язык и позволяет программисту делать почти все вплоть до манипуляции с регистрами и битами. Такая свобода делает язык небезопасным, поскольку компилятор не может проверить, являются ли подозрительные операции умышленными или нет. Небольшое количество заранее определенных функций и типов данных делает программы легко переносимыми между разными системами. С поддерживает как хороший, структурированный, так и плохой стиль программирования, оставляя ответственность за качество разработки на программисте. Стиль программирования приобретает особое значения при сопровождении программ: плохо написанная и откомментированная программа на С - такая же загадка, как и ассемблерский код. Язык С регламентирован международным стандартом ISO 9899. Язык С предпочтителен для написания программ с обращениями к функциям операционной системы, так как он обладает отличной совместимостью между логикой определения переменных и синтаксисом обращения к системе. Поскольку наиболее распространенные операционные системы в приложениях автоматического управления процессами основываются на UNIX, язык С является почти вынужденным выбором при разработке программ. Почти все примеры в современной технической литературе представлены на С. C обеспечивает получение высокой производительности за счет хорошо разработанных оптимизирующих компиляторов, которые для современных процессоров часто дают код более эффективный, чем написанный на ассемблере. Язык C дает прямой доступ к оборудованию и возможность вызова процедур на других языках. Приложения получаются переносимыми (особенно, если ОСРВ поддерживают одинаковый стандарт, например POSIX), однако, объектно-ориентированный подход на уровне языковых конструкций отсутствует. Язык C++ представляет собой значительно более мощный инструмент, чем С, на основе которого он создан. В C++ значительно улучшена абстракция данных с помощью понятия класса, похожего на абстрактный тип данных с четким разделением между данными и операциями. Классы C++ значительно легче использовать на практике, чем аналогичные понятия в других языках, поскольку C++ поддерживает объектно-ориентированное программирование и поэтапное уточнение типов данных. Главным преимуществом языка C++ является его способность поддерживать разработку легко используемых библиотек программ. Программирование в реальном времени непосредственно в C++ не поддерживается, но может быть реализовано с помощью специально разработанных программных модулей и библиотек классов. C++ включает язык C как подмножество и наследует все его положительные качества. C++ добавляет поддержку объектно-ориентированного подхода на уровне языковых конструкций. Java. Как язык интерпретируемого типа, имеет очень низкую эффективность получаемого кода. Доступ к оборудованию и вызовы процедур на других языках - только посредством библиотечных функций (обычно написанных на C). Java обеспечивает наивысшую переносимость приложения на уровне двоичного кода и является объектно-ориентированным языком. BASIC. Язык BASIC является простейшим среди языков программирования высокого уровня. Этот язык был создан в 1964 году для поддержки интерактивной разработки программ с удаленных терминалов. Из-за своей простоты BASIC часто критикуется опытными программистами, и несомненно, что этот язык не является хорошим средством для создания больших структурированных систем. С другой стороны, небольшие приложения на BASIC можно разработать значительно быстрее, чем на других языках. Кроме того, BASIC имеется почти на всех мини- и микрокомпьютерах. Программа на BASIC может компилироваться, но чаще она интерпретируется, то есть каждая команда транслируется в машинные коды только в момент ее выполнения. BASIC удобен для разработки небольших прикладных задач в составе крупных систем, но его не следует использовать для приложений порядка 500-1000 строк или более. Тем не менее, BASIC является наилучшим средством для непрофессиональных программистов, которым требуется быстро решить частную задачу. Командные языки, основанные на BASIC, имеются во многих системах промышленной автоматики. Они применяются для написания простых программ управления без обращения к более сложным средствам программирования, требующим компиляции и загрузки. FORTRAN. FORTRAN - это первый язык программирования высокого уровня, который, по-видимому, способствовал, более чем какой-либо другой язык, распространению и практическому применению ЭВМ. Выпущенный в 1957 году, он до сих пор широко используется, в особенности для математических вычислений. В целом FORTRAN имеет ограниченные возможности определения типа, весьма сложный способ работы с нечисловыми данными и не содержит многих важных функций языков реального времени, чтобы его серьезно рассматривать для этой цели. Новые версии FORTRAN заимствовали некоторые возможности из других языков и поддерживают более развитые структуры данных. В этом смысле различия между FORTRAN и другими языками сглаживаются. Благодаря тому, что язык имеет устойчивое применение в научных приложениях, нередко данные в системах реального времени обрабатываются существующими FORTRAN-программами, а новые программы анализа и статистики пишутся на FORTRAN. В подобных случаях основной проблемой является координация передачи информации между базами данных реального времени и прикладными модулями, написанными на FORTRAN. Такая координация обычно выполняется операционной системой. FORTRAN не рекомендуется для написания драйверов устройств или модулей на уровне операционной системы, так как для этой цели лучше подходят другие языки. Pascal и Modula-2. Pascal был разработан швейцарцем Николасом Вир-том (Niklaus Wirth) в 1971 году как дидактический язык для обучения хорошей технике программирования. Он быстро перерос свои первоначальные рамки и в настоящее время используется во множестве разнообразных приложений. Успех Pascal, как в случае BASIC, основан на распространении микро- и персональных компьютеров, на которых он широко используется. Язык Modula-2 был разработан тем же автором в 1975 году специально для программирования встроенных промышленных и научных вычислительных систем реального времени. Pascal и Modula-2 весьма похожи по стилю и структуре, хотя Modula-2 обладает большим количеством функций и синтаксических конструкций. В Pascal и Modula-2 предполагается, что программист постоянно остается в ограниченной среде, предоставляемой программой, что совсем не соответствует реальной практике. Гибкость их использования несколько выше, если некоторые программы для специальных приложений (драйверы устройств, обработчики прерываний) написаны на языке ассемблера. Оба языка поддерживают подключение внешних модулей на ассемблере. Pascal и Modula-2 являются хорошим средством для разработки встроенных систем, но не подходят для сложных приложений в распределенных компьютерных системах. Их ориентация на структуру делает программы хорошо читаемыми, что является существенным фактором для последующего сопровождения. Языки четвертого поколения (CASE средства). Средства CASE (Computer Aided Software Engenering) получили широкое распространение при разработке приложений реального времени в силу большой сложности последних. Языки «четвертого поколения» представляют собой формализованный способ описания объектов, их свойств и взаимоотношений между собой. По этому формальному описанию «компилятор» строит текст приложения на языке более низкого уровня (обычно предоставляется выбор между C/C++/Java). Затем этот текст можно скомпилировать уже «обычным» компилятором. Поскольку можно добавлять фрагменты на языке более низкого уровня, то CASE средства наследуют все положительные свойства последнего.
Лекция 4.3. Программирование асинхронной и синхронной обработки данных 1. Обработка прерываний и исключений. 2. Программирование операций ожидания. 3. Внутренние подпрограммы операционной системы. 4. Приоритеты процессов и производительность системы. 5. Тестирование и отладка.
1. Обработка прерываний и исключений Системы реального времени соединены с внешней средой (физический процесс) через аппаратные интерфейсы. Доступ к интерфейсам и внешним данным осуществляется либо по опросу, либо по прерыванию. При опросе программа должна циклически последовательно проверять все входные порты на наличие у них новых данных, которые затем считываются и обрабатываются. Очередность и частота опроса определяют время реакции системы реального времени на входные сигналы. Опрос является простым, но неэффективным методом из-за повторяющихся проверок входных портов. Получение данных по прерыванию происходит иначе. Интерфейсное устройство, получившее новые данные, привлекает внимание центрального процессора, посылая ему сигнал прерывания через системную шину. По отношению к текущему процессу прерывания являются асинхронными событиями, требующими немедленной реакции. Получив сигнал прерывания, процессор приостанавливает исполнение текущего процесса, сохраняет в стеке его контекст, считывает из таблицы адрес программы обработки прерывания и передает ей управление. Эта программа называется обработчиком прерывания. Другой вариант обработки прерываний заключается в том, что планировщик выбирает из очереди ожидания этого события или прерывания следующий процесс и переводит его в очередь готовых процессов. Когда процессор передает управление обработчику прерываний, он обычно сохраняет только счетчик команд и указатель на стек текущего процесса. Обработчик прерываний должен сохранить во временных буферах или в стеке все регистры, которые он собирается использовать, и восстановить их в конце. Эта операция критична по времени и, как правило, требует запрета прерываний для того, чтобы избежать переключения процессов во время ее выполнения. При управлении прерываниями время реакции должно быть как можно меньше. Оно представляет собой сумму времени, необходимого процессору, чтобы среагировать на прерывание (латентность прерывания), и времени, необходимого на переключение контекста до запуска обработчика прерываний. Типичная загрузка системы также играет определенную роль. Если система должна обслуживать много одновременных прерываний, вновь поступающие прерывания будут ждать в очереди, пока процессор не освободится. Программы обработки прерывания должны быть предельно компактными (длина кода) и короткими (время выполнения). Если сложное действие, требующее большого расхода процессорного времени, например вычисления или доступ к базе данных, необходимо выполнить после возникновения прерывания, то его лучше вынести из обработчика прерывания в процесс. Программа обработки прерывания должна выполнять лишь минимально необходимые операции, например, считать входные данные, сформировать сообщение и передать другой программе, извещая ее, что произошло прерывание и требуется дальнейшая обработка. Хорошим стилем для обработчиков прерываний является использование реентерабельного кода. Это позволяет избежать конфликтов в случае, если прерывается сам обработчик и тот же код вызывается для обслуживания нового прерывания прежде, чем закончилась обработка предыдущего. Реакция на исключения (exceptions) похожа на обработку прерываний. Исключениями называются нештатные ситуации, когда процессор не может правильно выполнить команду. Примером исключения является деление на ноль или обращение по несуществующему адресу. В англоязычной литературе для разных видов исключений применяются термины trap, fault, abort (не путать с "взаимным исключением" - mutual exclusion). Обычно операционная система обрабатывает исключения, прекращая текущий процесс, и выводит сообщение, четко описывающее ситуацию, на устройство отображения, обычно монитор или принтер. Приемлемая при последовательной интерактивной многопользовательской обработке, внезапная остановка процесса в системах реального времени должна быть абсолютно исключена. Нельзя допустить, чтобы управляемые микропроцессором автопилот самолета или автоматическая тормозная система автомобиля (Automatic Braking System - ABS), внезапно прекратили работу из-за деления на ноль. В системах реального времени все возможные исключения должны анализироваться заранее с определением соответствующих процедур обработки. Сложной проблемой при обработке исключений является проверка, что исключение не возникнет снова после того, как оно было обработано. Или иными словами, обработка исключений должна заниматься причиной, а не симптомами аномальной ситуации. Если исключение обработано некорректно, оно может возникнуть опять, заставляя процессор снова и снова переходить к модулю обработки. Например, обработчик деления на нуль должен проверять и изменять операнды, а не просто возобновлять исполнение с места, предшествующего ошибке, что приведет к бесконечному циклу. Фактические адреса программных модулей известны только после их загрузки. При запуске системы в таблицу обработки прерываний записываются адреса памяти, куда загружаются обработчики, которые затем вызываются по ссылкам из этой таблицы. 2. Программирование операций ожидания Процесс реального времени может явным образом ждать истечения некоторого интервала (относительное время) или наступления заданного момента (абсолютное время). Соответствующие функции обычно имеют следующий формат: wait (n) и wait until (время) где n - интервал в секундах или миллисекундах, а переменная "время" имеет формат часы, минуты, секунды, миллисекунды. Когда выполняется одна из этих функций, операционная система помещает процесс в очередь ожидания. После истечения/наступления заданного времени процесс переводится в очередь готовых процессов. Распространенный, но не лучший метод организации временной задержки - цикл, контроль системного времени в цикле занятого ожидания repeat (*холостой ход*) until (time = 12:00:00); Как правило, подобные активные циклы ожидания представляют собой бесполезную трату процессорного времени, и их следует избегать. Однако имеются исключения. В системе, где аналого-цифровое преобразование занимает 20 мкс, а операция переключения процессов - 10 мкс, более экономно организовать ожидание на 20 мкс перед тем, как считать новые данные, чем начинать процедуру переключения процессов, неявно подразумеваемую "хорошей" операцией ожидания. Каждый случай требует индивидуального подхода - для этого обычно нужно хорошее знание системы и развитое чутье. Важной особенностью процессов, запускаемых периодически, - например, фильтрация и алгоритмы регулирования, - является накопленная ошибка времени. Это связано с тем, что процесс из очереди ожидания события опять попадает в очередь, но уже готовых процессов и должен ждать некоторый случайный интервал времени прежде, чем получит управление (рис. 1а). Требуемое и фактическое время пробуждения процесса не совпадают. Ошибки ожидания накапливаются, если это время рассчитывается так новое время пробуждения = время начала ожидания + интервал По такому алгоритму работает холостой цикл "ждать 10 секунд". Накопленная временная ошибка представляет собой сумму времени, проведенного в очереди, и времени, необходимого для непосредственного исполнения. Правильное решение получается, если отсчет ведется от момента предыдущего пробуждения новое время пробуждения = время предыдущего пробуждения + интервал Таким образом, относительное время преобразуется в абсолютное. На практике необходимы две команды wait until (ref_time); ref_time:= ref_time + 10 seconds; Этот принцип проиллюстрирован на рис. 1б, где номинальное время отложено по горизонтальной оси. Когда абсолютное время принимается в качестве опорного, накопления ошибок времени удается избежать. Рисунок 1. - (а) Неправильный способ определения момента очередного запуска периодических задач - ошибка времени накапливается; (б) правильное решение - ошибка времени не накапливается
3. Внутренние подпрограммы операционной системы Типичной ситуацией при программировании в реальном времени является непосредственное обращение к подпрограммам операционной системы из-за того, что в используемом языке программирования отсутствует эквивалентное средство. Обращения к функциям операционной системы также необходимы при работе в сетевой и распределенной среде. Операционная система отвечает за все обслуживание прикладных задач, включая файловые и сетевые операции. Простое обращение к операционной системе может привести к сложной последовательности действий для доступа к удаленной базе данных, включая все сопутствующие проверки и операции управления, избавляющие прикладную программу от лишних деталей. Интерфейс операционной системы делает выполнение таких операций более прозрачным и упрощает написание сложных программ. Многие языки программирования высокого уровня, например С, обеспечивают интерфейс с операционной системой для непосредственного вызова ее модулей из исполняемых процессов. Существуют различные виды программных интерфейсов с операционной системой непосредственные вызовы, примитивы и доступ через библиотечные модули. Непосредственные (системные) вызовы осуществляются с помощью конструкции языка высокого уровня, которая передает управление подпрограмме, являющейся частью операционной системы. Необходимые параметры передаются списком, как при обычном обращении к подпрограмме. После завершения системной процедуры результат возвращается вызывающей программе. Так как в многозадачной среде системные программы и примитивы могут вызываться одновременно разными процессами, их код всегда реентера-белен. Это позволяет избежать конфликтов при прерывании системной программы другим запросом, требующим ту же услугу из другого контекста. В некоторых случаях для доступа к внутренним ресурсам операционной системы можно использовать библиотечные модули. Эти модули уже предварительно откомпилированы, и их остается только связать с основной программой. Необходимо проверить по документации системы требуемые параметры, а также механизмы их передачи и редактирования связей в языке высокого уровня. 4. Приоритеты процессов и производительность системы Многозадачная операционная система реального времени должна допускать назначение приоритетов исполняемым процессам. Обычно приоритеты являются динамическими, что означает, что во время исполнения они могут изменяться как самими процессами, так и операционной системой. Обычно существуют определенные ограничения и механизмы контроля, которые определяют, кто и как может менять приоритеты. Назначение приоритетов оказывает серьезное влияние на работу системы в целом. Наиболее важные процессы или процессы, время реакции которых жестко ограничено, получают более высокий приоритет. К последним относятся обработчики прерываний. Задачи, выполняющие менее важные действия, например печать, получают более низкий приоритет. Очевидно, что необходимо обращать внимание на соглашения, используемые в системе относительно того, связан ли более высокий приоритет с большим или меньшим числом. Приоритеты имеют относительное значение и оказывают влияние только тогда, когда существуют процессы с разными приоритетами. В системах реального времени реакция на прерывания отделена от вычислений, требующих значительных ресурсов процессора. Как только происходит событие или прерывание, его обработчик немедленно включается в очередь готовых процессов. Программы обработчиков прерываний обычно компактны, так как они должны обеспечивать быструю реакцию, например ввод новых данных, и передавать управление более сложным процессам, интенсивно потребляющим ресурсы процессора, которые исполняются с более низким приоритетом. В примере системы управления манипулятором робота одна задача, которую можно построить как обработчик прерываний, ждет поступления от датчика новых данных о текущем положении манипулятора. Когда поступает прерывание от датчика - есть новые данные, - эта задача должна сразу получить управление. Затем она передает данные о положении программе их обработки, требующей больших вычислительных ресурсов. Эта программа не отвечает за обработку прерываний и может использовать больше времени для вычислений. Производительность системы реального времени значительно труднее поддается оценке, чем систем, использующих обычные последовательные программы. Если обычная последовательная программа исполняется на конкретном процессоре с известной скоростью, то программа реального времени зависит от поведения окружающей среды, то есть управляемых технических процессов. Общая производительность системы должна быть достаточной для того, чтобы выполнять все операции и выдавать результаты за установленное время. Иными словами, система реального времени всегда должна быть готова к максимальной нагрузке, которую может создать технический процесс. В развитых и сложных операционных системах, таких как UNIX, и в еще большей степени в распределенных операционных системах, доступ к большинству функций (ввод/вывод, сетевая поддержка и т.д.) происходит через системные вызовы или механизм удаленного вызова процедур. В прикладных программах для вызова системных функций используется довольно простая нотация, за которой, как правило, стоит длинная последовательность действий операционной системы. Если между двумя процессами, исполняющимися в разных узлах сети, организован программный канал, то считывание одного символа из этого канала требует целой серии операций в обоих узлах. Поскольку на эти операции обычно наложены жесткие ограничения по времени, необходимо провести глубокий предварительный анализ прежде, чем принимать то или иное проектное решение. Если локальная сеть используется не только задачами реального времени, но и интерактивными пользователями, то от количества и активности последних, в значительной мере зависит и ее общая нагрузка. Многозадачные операционные системы имеют команды, показывающие в каждый момент все активные процессы, их текущий статус (например, ожидание ввода/вывода, ожидание прерывания) и долю в потреблении ресурсов процессора с момента последней перезагрузки системы или какого-либо иного события. Первый шаг по проверке характеристик системы - анализ ее работы с помощью подобной команды. Выявление процессов, занимающих слишком большую долю процессорного времени, может быть хорошей отправной точкой для поиска узких мест и оптимизации характеристик системы. Нет ничего плохого в том, если некоторые процессы загружают процессор больше, чем другие, однако разработчик системы должен иметь ясное представление о том, когда это происходит и почему. 5. Тестирование и отладка Доказательство правильности работы программы является обязательным шагом в ее разработке. Необходимо проверить, что программа выполняет свои функции без ошибок. Визуальные и формальные методы позволяют выявить только ограниченное количество ошибок. На практике это означает, что формальная теория тестирования имеет мало смысла, а основную роль играет собственный опыт и "народные программистские" предания. Реальное тестирование проводится в "боевых" условиях. Выявлять ошибки трудно - многие из них проявляются спорадически и их нельзя воспроизвести по желанию. Никакое доказательство не может гарантировать, что программа полностью свободна от ошибок, и никакие тесты не могут убедить, что выявлены все ошибки. Цель тестирования - найти как можно большее число ошибок и гарантировать, что программа работает с разумной надежностью. Один из создателей теории операционных систем, Эдс-гер Дейкстра (Edsger Dijkstra), заметил: "Тестирование может доказать только наличие ошибок, но не их отсутствие". Тщательный тест требует соответствующей разработки и подготовки; необходимо сочетание практических и аналитических тестов. Сначала тестовые процедуры и данные, ожидаемые результаты описываются в специальном документе. В процессе тестирования ведется журнал испытаний, который затем сравнивается со спецификацией тестов. Желательно, чтобы коллектив разработчиков системы отличался от того, который будет определять процедуры испытаний и проводить их. При тестировании систем реального времени существует дополнительная сложность из-за большого количества возможных взаимосвязей между задачами. Вероятность внесе
|