Два активных участника группы Real-Time for Java Experts Group рассказывают о функциях RTSJ и идеях, положенных в основу структуры спецификации.
Новые языки, дисциплины программирования, операционных систем и методики разработки программного обеспечения несут в себе огромный потенциал, важный для разработчиков ПО реального времени. Большой интерес для этого сообщества (хотя и с весьма недавнего времени) представляет объектно-ориентированное программирование. Язык Java, к примеру, имеет эффективные механизмы поддержки объектов и превосходно подходит для создания расширений к системам реального времени и встроенному ПО.
Принимая во внимание эту связь между Java и программным обеспечением реального времени, группа Real-Time for Java Experts Group (RTJEG) начала разработку спецификаций реального времени для Java (RTSJ) [1] в марте 1999 года в рамках программы Java Community Process [2]. Цель RTJEG, участниками которой являются оба автора этой статьи, состояла в создании платформы (среды исполнения и API-интерфейса Java), которая позволит программистам делать обоснованные предположения о временных характеристиках исполняемого программного обеспечения. Программисты, которые создают приложения для систем реального времени, должны иметь возможность априори определять, когда будет исполняться конкретная логика и быть уверенным, что ее исполнение завершится до определенного момента. Эта предсказуемость имеет крайне важное значение во многих случаях применения систем реального времени, таких как системы управления полетами, системы отдачи команд и управления, промышленные автоматизированные системы, транспорт и телефонные станции.
Мы начали свою работу с анализа спецификаций Java [3] и виртуальной машины Java [4]. Каждую функцию языка или исполняемого кода мы рассматривали с учетом того, позволяет ли имеющаяся семантика программистам обосновано, а также заранее предполагать и рассчитывать временные характеристики поведения этой функции (или того, чем эта функция управляет) во время исполнения. Мы выявили три функции: планирование, управление памятью и синхронизация, которые не допускают такого рода детерминизм. Требования, определенные во время исследования, проведенного под эгидой Национального института науки и технологии (National Institute of Science and Technology — NIST) в сочетании с данными, предоставленными другими отраслевыми организациями, помогли нам определить дополнительные функции и семантику.
Мы решили добавить четыре следующих функции: обработка асинхронных событий, асинхронная передача управления, асинхронное завершение нитей и доступ к физической памяти. Эти функции, а также те три, которые были выявлены в результате нашего анализа спецификаций Java и JVM, определили семь основных компонентов RTSJ. Мы считаем, что эти семь компонентов позволяют предоставить платформу разработки программного обеспечения реального времени для создания широкого круга приложений. Врезка «Насколько хорошо RTSJ удалось добиться поставленных целей?» более подробно рассказывает о требованиях и принципах, которыми мы руководствовались при разработке RTSJ.
Врезка «История создания RTSJ» рассказывает о важных этапах процесса подготовки спецификации и содержит адреса, где все заинтересованные лица могут найти ее предварительный вариант. RTSJ завершила этап широкого обсуждения в феврале 2000 года, но, по условиям программы Java Community Process, спецификация не считается законченной до тех пор, пока не будет создана эталонная реализация и тестовые пакеты. По мере накопления опыта работы с RTSJ в спецификацию могут быть внесены небольшие изменения. Цель данной статьи состоит в том, чтобы рассказать о рациональной основе RTSJ, в частности о том, как требования NIST и другие цели повлияли на структуру спецификации, а также показать, каким образом эта важная спецификация согласуется с общим стратегическим направлением создания объектно-ориентированной системы программирования, оптимизированной для разработки систем реального времени.
Планирование
Спецификация Java дает лишь самые общие правила реализации планирования. В ней сказано следующее.
Решение о предоставлении ресурсов должно приниматься с учетом того, что нитям с более высоким приоритетом в общем случае отдается предпочтение перед нитями с более низким приоритетом. Такое предпочтение, однако, не гарантирует, что нить с более высоким приоритетом всегда будет исполняться, и приоритеты нитей не могут использоваться для надежной реализации взаимного исключения.
Очевидно, что если вы создаете программное обеспечение с временными ограничениями, вам необходимы более строгие семантические правила, определяющие порядок исполнения нитей. Традиционно такая семантика реализуется через алгоритмы, которые определяют, как именно выбирается для исполнения следующая нить. Спецификация RTSJ указывает минимальный алгоритм планирования, который должен присутствовать во всех реализациях RTSJ.
Минимальные требования
Все реализации, как минимум, должны предоставлять вытесняющий диспетчер с фиксированными приоритетами, который поддерживает не меньше 28 уникальных приоритетов. «Фиксированные приоритеты» в данном случае означают, что система не меняет приоритеты нитей (к примеру, с учетом их «возраста»). Есть только одно исключение: система может менять приоритеты нитей при исполнении алгоритма предотвращения инверсии приоритетов (который поддерживает наследование приоритетов). Без каких бы то ни было исключений нити могут изменять свои собственные приоритеты или приоритеты других нитей.
Прикладная программа должна поддерживать как минимум 28 уникальных приоритетов; к примеру, она должна знать, что нить с более низким приоритетом никогда не будет исполняться, если к исполнению готова нить с более высоким приоритетом. Из этого следует, что нельзя просто отобразить 28 приоритетов на меньшее их число, определяемое возможностями базовой системы. Тем не менее, вы можете реализовать RTSJ на платформе, которая поддерживает меньше 28 приоритетов, но в этом случае необходимо предоставить определенные механизмы, которые смогут обеспечить их уникальность.
Почему именно 28 приоритетов? Мы выбрали это число потому, что теория планирования для систем реального времени утверждает, что близкого к оптимальному планированию можно добиться с помощью 32 приоритетов [5]. Мы решили оставить «незанятыми» четыре приоритета, поскольку RTSJ, скорее всего, войдет в состав более крупной системы, а некоторые системы поддерживают лишь 32 приоритета.
Безусловно, вы можете добавить к этому минимуму другие алгоритмы планирования. RTSJ предлагает три класса: Scheduler, SchedulingParameters и ReleaseParameters и подклассы этих классов, которые определяют требования к временным характеристикам. Эти классы привязаны к планируемым объектам (нитям или даже обработчикам событий). Требуемый планировщик - экземпляр класса PriorityScheduler, использует значения, присвоенные этим параметрическим объектам.
Создание нити
Спецификация RTSJ для создания нитей определяет класс RealtimeThread (RT), который исполняет резидентный планировщик. RT могут обращаться к объектам в динамической памяти и, тем самым, способны порождать задержки, вызванные сборкой мусора. Еще одна возможность создавать нити - это использовать подкласс RT - NoHeapRealtimeThread. Эти подклассы не могут обращаться ни к одному из объектов в динамической памяти, то есть они могут запускаться во время работы сборщика мусора (и, таким образом, позволяют избежать задержки, вызванной сборкой мусора). Подклассы NHRT подходят для кода, который практически не допускает незапланированных задержек. С другой стороны, классы RT более удобны для кода, выдерживающего более длительные задержки. Обычные нити Java подойдут для кода, в котором отсутствуют ограничения на временные характеристики.
Управление памятью
Использование динамической памяти, в которой выполняется сборка мусора, всегда считалось препятствием для программирования систем реального времени, поскольку сборка мусора порождает непредсказуемые задержки. Мы хотели создать RTSJ, которая предусматривает использование сборщика мусора, действующего в реальном времени, но технология оказалась недостаточно совершенной. Вместо этого, RTSJ расширяет модель памяти так, чтобы она поддерживала управление памятью, не влияя на способность кода реального времени работать предсказуемым образом. Эти расширения позволяют размещать краткосрочные и долгосрочные объекты вне динамической памяти, где выполняется сборка мусора.
Спецификация также обладает гибкостью, достаточной для использования уже знакомых решений, таких как предварительно зарезервированные пулы объектов.
Области памяти
Спецификация RTSJ вводит понятие области памяти (memory area) - раздела памяти, находящегося вне зоны динамической памяти со сборкой мусора, и которую можно использовать для резервирования объектов. Области памяти не подвергаются сборке мусора в обычном понимании. Строгие правила записи и чтения из областей памяти препятствуют возникновению «висящих» указателей и, таким образом, обеспечивается поддержка корректности указателей Java. Объекты, размещенные в областях памяти, могут содержать ссылки на объекты в динамической памяти. Таким образом, сборщик мусора должен иметь возможность сканировать память за пределами сегмента динамической памяти в поисках объектов внутри этого сегмента для того, чтобы обеспечить целостность динамической памяти после сборки мусора. Это сканирование отличается от создания графа размещения для сегмента динамической памяти. Сборщик просто добавляет все ссылки на объекты, расположенные в динамической памяти, к своему набору указателей. Поскольку классы NHRT могут игнорировать сборщика мусора, они не в состоянии обращаться или изменять какой-либо указатель в динамической памяти.
RTSJ использует абстрактный класс MemoryArea для представления областей памяти. Этот класс имеет три подкласса: физическую память, память с неограниченным временем жизни (immortal memory) и память с ограниченным временем жизни (scoped memory). Физическая память позволяет создавать объекты в областях памяти, которые имеют определенные важные особенности, такие как память, подключенная к постоянным устройствам хранения. Память с неограниченным временем жизни - это особый случай. На рис. 1 показаны три метода резервирования объекта с использованием памяти с неограниченным временем жизни, резервирования вручную и автоматического резервирования, использующего динамическую память Java. Традиционные языки программирования используют резервирование вручную, при котором срок существования объекта определяет логика приложения - процесс, который, как правило, требует больших затрат времени и подвержен ошибкам. Весь пул памяти с неограниченным временем жизни и все объекты, резервированные для него, существуют до окончания программы. Резервирование объектов, срок существования которых ограничен, часто применяется в современных системах реального времени. При использовании памяти с ограниченным временем жизни нет необходимости в традиционной сборке мусора и сопутствующих задержках. RTSJ реализует ограниченную по времени память или с помощью поля MemoryParameters, или метода Scoped-Memory.enter().
Память с ограниченным временем жизни
Рис. 1c показывает память с ограниченным временем жизни в сравнении с другими методами резервирования. Память с ограниченным временем жизни, реализованная в абстрактном классе ScopedMemory, позволяет резервировать объекты и управлять ими, используя область памяти (или сегмент, указываемый синтаксическими границами), которая определяют срок существования всех объектов, размещенных внутри нее. Когда система входит в область, очерченную синтаксическими границами, каждое использование «new» заставляет систему резервировать память из активной области памяти. Когда временные ограничения истекают или система покидает эту область памяти, она обычно устанавливает счетчик ссылок на память в ноль, уничтожает все объекты, размещаемые внутри, и вызывает их модули завершения.
Можно также использовать вложенные сегменты памяти. Когда система входит во вложенный сегмент, все последующее резервирование будет выполняться в памяти, определенной новыми границами. Когда система выходит из вложенного сегмента, она восстанавливает предыдущие границы и снова все последующее резервирование будет выполняться в этих границах.
Для создания экземпляров MemoryArea используются два конкретных подкласса - LTMemory (LT для линейного времени) и VTMemory (VT для переменного времени). В этом контексте под «временем» понимают временные затраты на резервирование нового объекта. LTMemory требует, чтобы время резервирования линейно зависело от размера объекта (без учета изменений в производительности аппаратного кэша или иных оптимизаций). При создании области LTMemory указывается ее размер, который остается фиксированным.
Хотя, VTMemory, в отличие от LTMemory, не имеет временных ограничений, их всегда можно установить для того, чтобы минимизировать возможные изменения затрат на резервирование. Для этого создается область VTMemory некоторого начального размера и указывается максимальный размер, которого она может достигать. Кроме того, в области VTMemory можно разрешить выполнение сборки мусора в реальном времени, хотя RTSJ этого не требует.
В этом случае вы можете создать объект VTMemory вместе с объектом сборки мусора для того чтобы указать на наличие механизма сборки мусора, специфического для данной реализации. Однако, если вы реализуете VTMemory, NHRT должен иметь возможность ее использовать.
Поскольку, как показано на рис. 1, срок существования объектов, размещенных в областях ограниченной по времени памяти, определяет управляющая логика, необходимо ограничить ссылки на эти объекты. RTSJ использует набор ограничивающих правил присваивания, позволяющих отделить долговременные объекты от объектов, расположенных в ограниченной по времени памяти, на которые есть ссылки и которые, возможно, существуют меньшее время. Виртуальная машина должна выявлять попытки некорректного присваивания, а когда они возникают выдавать соответствующее исключение.
RTSJ реализует ограниченную по времени память с помощью двух различных механизмов, которые действуют согласованно: метод ScopedMemory.enter() и поле области памяти в MemoryParameters. При создании нити указывается любой из них. Таким образом, несколько связанных нитей могут совместно использовать область памяти, и эта область остается активной до выхода из нее последней нити. Такая гибкость означает, что приложение может резервировать новые объекты из области памяти, которые имеют характеристики, однозначно соответствующие приложению или конкретным областям кода.
Синхронизация
При синхронизации RTSJ использует «приоритет» несколько более свободным образом, чем описано в традиционной документации, касающейся систем реального времени. «Нить с самым высоким приоритетом» просто указывает на то, что наиболее приемлемая нить - это нить, которую планировщик будет выбирать из всех нитей, готовых к исполнению. Таким образом, вовсе не обязательно наличие диспетчерского механизма, строго базирующегося на приоритетах.
Очереди ожидания
Система должна выстраивать в очередь все нити, ожидающие получения ресурса, в порядке их приоритетов. К этим ресурсам относятся процессор и синхронизированные блоки. Если активное правило планирования допускает нити с одним и тем же приоритетом, нити организуются в очередь, порядок обработки которых определяется принципом «первый пришел, первый ушел». Система выполняет следующее:
- упорядочивает в порядке приоритетов нити, ожидающие внесения в синхронизированные блоки;
- добавляет оформленную в блоки нить, которая становится готовой к работе, в конец очереди готовых к исполнению нитей с данным приоритетом;
- добавляет нить, приоритет котрой точно установлен либо ею самой, либо другой нитью, в конец очереди нитей с новым приоритетом;
- помещает нить, которая вычисляет результат, в конец очереди с ее приоритетом.
Предотвращение инверсии приоритетов
Реализация синхронизированных примитивов должна иметь правила исполнения по умолчанию, которые гарантируют отсутствие инверсии приоритетов. Это применимо к традиционному коду Java, если он выполняется в полной реализации RTSJ, а также к нитям реального времени. Протокол наследования приоритетов (также называемый алгоритмом планирования реального времени [6]) должен реализовываться по умолчанию.
Спецификация также предоставляет механизм, с помощью которого можно переопределять правила по умолчанию, действующие для всей системы, или управлять правилами, используемыми для конкретного монитора, если реализация поддерживает эти правила. Спецификация правил управления монитором расширяемая, поэтому в будущих реализациях можно добавлять другие механизмы. Второе правило - правило эмуляции максимального приоритета (или самой старшей блокировки), также указывается для систем, которые его поддерживают [6].
Детерминизм
Реализация, соответствующая спецификации, должна предоставлять фиксированную верхнюю временную границу, с учетом которой код приложения добавляется в синхронизированный блок для незаблокированного монитора.
Совместное использование и взаимодействие между нитями
Разработчики часто используют сочетание обычных нитей Java, RT и NHRT. Спецификация RTSJ допускает взаимодействие между различными типами нитей, даже в самом сложном случае, возникающем между обычными нитями и NHRT. Если NHRT пытается взаимодействовать с объектом, являющимся либо RT, либо обычной нитью, которая уже поддерживает взаимодействие, как правило, возникает наследование приоритетов. Есть один хитрый момент. Объект, не относящийся к подклассу NHRT и имеющий свой собственный приоритет, не может выполняться во время сборки мусора. Таким образом, во время сборка мусора исполнение загруженной нити откладывается до завершения этого процесса. Конечно из-за сборки мусора, NHRT испытывает задержку. Чтобы решить эту проблему, RTSJ предлагает механизмы, которые позволяют NHRT взаимодействовать (вид синхронизации) с RT и обычными нитями Java, в то же время избегая задержек, вызванных сборщиком мусора в NHRT.
Рис. 2. Как нити в реализации RTSJ взаимодействуют в очереди записи, свободной от ожидания. Однонаправленная очередь соединяет системы с различной динамикой исполнения операций (от реального времени к нереальному времени) |
RTSJ предоставляет классы очередей для связи между NHRT и обычными нитями Java. На рис. 2 показана однонаправленная очередь записи без ожидания, вход которой обслуживается в реальном времени, а выход - нет. NHRT используют операции записи (в реальном времени); обычные нити, как правило, используют операцию чтения. Со стороны записи очередь не блокирующая (любая попытка записать в заполненную очередь сразу же возвращает значение «ложь») и несинхронизируемая (если несколько NHRT получили разрешение на запись, они должны синхронизироваться самостоятельно), поэтому NHRT не будут подвергаться задержкам из-за сборки мусора. Операция чтения, с другой стороны, блокируемая (она будет ждать до тех пор, пока данные не появятся в очереди) и синхронизируемая (допускает несколько «читателей»). Когда NHRT посылает данные в обычную нить Java, он использует операцию постановки в очередь без ожидания, а обычная нить использует синхронизированную операцию изъятия из очереди.
Очередь чтения, которая является однонаправленной (от реального времени к не реальному времени) работает по иному. Поскольку запись выполняется без задержки, динамики записи и чтения не согласованы и при выполнении этих операций могут теряться данные. Чтобы избежать задержки в зарезервированных элементах памяти, конструкторы классов статичным образом резервируют всю память, используемую для элементов очереди, учитывая ограниченный размер очереди.
Если обычная нить не удаляет элементы из очереди на достаточно высокой скорости, очередь может заполниться. NHRT не в состоянии поддерживать блокировку по заполненной очереди, поскольку в этом случае NHRT будет испытывать задержки от операций нереального времени, таких как сборка мусора, и характеристики его выполнения предсказать тогда будет намного сложнее. Из-за этого ограничения при записи, новый элемент может быть помещен «поверх» уже существующего элемента. Это вполне обосновано, учитывая, что операция записи не может ждать, а каков бы ни был размер буфера, нельзя гарантировать, что все элементы будут размещены в очереди. Такое совместное использование удобно для данных, которые можно будет восстановить, для данных, которые должным образом будут регенерировать протоколы более высоких уровней, а также в тех ситуациях, когда утрата данных предполагается и корректность системы при этом не нарушается.
Обработка асинхронных событий
Механизм поддержки асинхронных событий состоит из двух классов: AsyncEvent и AsyncEventHandler. Объект AsyncEvent представляет событие, которое может произойти (например, сигнал Posix или аппаратное прерывание), или вычисляемое событие (такое как попадание самолета в конкретную зону). Когда возникает одно из этих событий, о чем свидетельствует вызов метода fire(), система планирует соответствующие объекты AsyncEventHandler.
AsyncEvent управляет двумя вещами: диспетчеризацией описателей при возникновении событий, и присваиванием описателей, связанных с событием. Приложение может обратиться с запросом о таком присваивании, а также о добавлении и удалении описателей.
AsyncEventHandler - это планируемый объект, несколько напоминающий нить. В случае возникновения события система вызывает методы run() соответствующих описателей.
Однако в отличие от запускаемых объектов, с AsyncEventHandler связаны параметры планирования, запуска и памяти, которые управляют реальным исполнением описателя после его вызова. Когда возникает событие, система запускает описатели асинхронно, планируя их в соответствии с параметрами. В результате вызванный описатель связан со своей собственной нитью. На самом деле, он может быть как связан, так и не связан со своей собственной нитью, но суть в том, что это должно выглядеть именно таким образом.
Описатели AsyncEventHandler можно реализовывать так чтобы расходовалось намного меньше системных ресурсов, чем используют реальные нити. Система должна иметь возможность хорошо ими управлять, даже когда объектов AsyncEvent и AsyncEvent-Handler десятки тысяч. Число вызванных (в процессе) дескрипторов должно быть меньше.
Специализированным объектом AsyncEvent является объект Timer, представляющий событие, появление которого определяется временем. Существует два вида Timer: OneShotTimer и PeriodicTimer. Первый из них - OneShotTimers - вызывается один раз, в определенное время. Если текущее время больше, чем указанное для этого объекта, то система немедленно вызывает описатель. PeriodicTimer вызывается в определенное время и существует в течение определенного промежутка.
Спецификация RTSJ представляет часы, используя класс Clock. Реализации могут предлагать приложениям более одних часов. Специальный объект Clock - Clock.getRealtimeClock(), представляет часы реального времени и должен присутствовать во всех реализациях RTSJ. Многие объекты, которые измеряют время, могут быть представлены с помощью любого экземпляра Clock, предлагаемого в данной реализации.
Асинхронная передача управления
Асинхронная передача управления позволяет указать конкретные методы, декларируя их с помощью вызова AsynchronouslyInterruptedException (AIE). Когда такой метод запускается на базе стека исполнения нити и система вызывает java.lang. Thread.interrupt() на этой нити, метод сразу начинает действовать так, как если бы система вызывала AIE. Если система вызывает прерывание в нити, которая не исполняет такой метод, она устанавливает AIE в состояние ожидания для данной нити и при вызове его в следующий раз, управление будет передано этому методу либо через его вызов, либо возвратом к нему. Система также устанавливает состояние AIE в состояние ожидания, в то время как управление передано ему, возвращено ему или передано на синхронизированные блоки.
Асинхронное завершение нитей
Система может использовать AIE напрямую или в сочетании с асинхронными событиями для реализации асинхронного завершения нитей. Иногда, в зависимости от реализации, все методы, которые использует экземпляр RealtimeThread, декларируются посредством AIE. В этом случае, когда система вызывает interrupt() на нити, результат аналогичен методу stop() в Java. В отличие от этого метода, однако, завершение нитей безопасно, поскольку код, написанный в предположении, что он не будет прерван, или код в синхронизированных блоках, выполняется нормально.
Доступ к физической памяти
RTSJ определяет два класса для программистов, которые хотят обращаться к физической памяти непосредственно из Java-кода. Первый класс - RawMemoryAccess, определяет методы, позволяющие создавать представление объекта в виде диапазона физических адресов, и затем обращается к физической памяти как к последовательности байт, слов, длинных слов или цепочек байт заданной длины. RTSJ не предусматривает никакой семантики за исключением методов присваивания и считывания.
Второй класс - PhysicalMemory, позволяет создавать объект PhysicalMemoryArea, который представляет диапазон адресов физической памяти, где система может располагать объекты Java. Чтобы создать новый объект Java в конкретном объекте PhysicalMemory, можно использовать или метод newInstance(), или метод newArray().
Экземпляр RawMemoryAccess формирует область памяти как последовательность байт фиксированной длины. Производственные методы позволяют создавать объекты RawMemoryAccess из памяти в конкретном диапазоне адресов или используя конкретный тип памяти. Реализация должна предоставлять и устанавливает производственный метод, способный корректно интерпретировать эти запросы.
Полный комплект методов присваивания и получения позволяет системе обращаться к содержимому физической области памяти с помощью параметров смещения и базы, интерпретируемых как значения типа байта, short, int или long, и копировать их в/из массивов чисел типа байта, short, int или long. Интерпретируя значение как смещение, программа может использовать физическую область памяти для хранения ссылок на другие значения данных в той же самой области. Программа может также определять сегмент одной области памяти как другую область памяти. Базовый адрес и размер, а также любые смещения в физической области памяти представляют собой величины типа long (64 разряда).
Что касается спецификаций Java Language и виртуальной машины Java, то RTSJ усиливает семантику алгоритмов планирования, управления памятью и синхронизации. Все остальные функции, за исключением доступа к физической памяти, подходят для реализации некоторых классов систем реального времени и позволят создавать системы, способные на приемлемом уровне выдерживать временные ограничения. Для удобства разработчиков в RTSJ были добавлены функции доступа к физической памяти, поскольку многие системы реального времени требуют такого обращения на программном уровне.
Мы предпочли решать задачу поддержки программирования реального времени в Java за счет определения функций и семантики, которые дают программистам возможность управлять исполнением нитей и позволяют сделать более предсказуемым исполнение нитей определенных типов. Мы могли бы сосредоточиться на атрибутах объектов и добиваться предсказуемости временных характеристик, за счет определения некоторых наборов атрибутов. Хотя такой подход достаточно интересен, мы пришли к выводу о том, что, по крайней мере, сейчас, подобная парадигма программирования слишком далека от принятой сейчас практики. По мере упрощения создания программного обеспечения реального времени благодаря использованию объектного программирования, такие подходы могут стать практически осуществимыми.
В основном спецификация RTSJ завершена. Мы можем внести незначительные изменения с учетом опыта создания эталонной реализации и тестовых пакетов, но рассчитываем, что все три компонента - эталонная реализация, тестовые пакеты и RTSJ - будут представлены в окончательном виде к концу 2000 года.
Благодарности
Группе Real-Time for Java Experts Group в ее работе помогали многие, но здесь, учитывая ограниченные размеры статьи, мы можем упомянуть лишь некоторых. Мы благодарны Роду Смиту из IBM и Пэт Шульц, которая раньше работала в этой же компании (сейчас она перешла в Sun), усилиями которых IBM стала ведущим и активным участником этого процесса, а также Джиму Митчелу и Кену Уркарту из Sun за участие в формировании и управлении процессом Java Community Process. Среди сотрудников IBM, которые работают над эталонной реализацией, особо хотелось бы отметить огромный вклад Питера Хаггара, Джима Майклсона и Дэвида Вендта. Мы благодарны всем бывшим и настоящим членами группы RTJEG. И, наконец, мы хотим сказать спасибо всем тем, кто нашел время прокомментировать данную спецификацию.
Описание функций и семантики RTSJ из документа RTSJ v0.8.1 публикуется с разрешения корпорации Sun Microsystems.
Об авторах
Грег Боллелла - старший проектировщик корпорации IBM и старший инженер группы Real-Time for Java Experts Groups. Ранее он занимался проектированием и реализацией коммуникационных протоколов в IBM. Его диссертация была посвящена теории планирования и реализации систем реального времени. С ним можно связаться по электронной почте по адресу bollella@us.ibm.com.
Джеймс Гузлинг - сотрудник корпорации Sun Microsystems и один из создателей языка программирования Java. Свой путь в программировании он начинал с разработки программного обеспечения реального времени для научных приборов.
Литература
[1] The Real-Time for Java Experts Group, The Real Time Specification for Java, Version 0.8.1, 27 Sept. 1999; http://www.rtj.org/rtj.pdf
[2] The Java Community Process Program Manual, Sun Microsystems, Inc., Dec. 1998; http://java.sun.com/aboutJava/communityprocess/
java_community_process.html
[3] J. Gosling, B. Joy, and G. Steele, The Java Language Specification, Addison-Wesley, Reading, Mass., 1996
[4] T. Lindholm and F. Yellin, The Java Virtual Machine Specification, 2nd ed., Addison Wesley Longman, Reading, Mass., 1999
[5] L. Sha, R. Rajkumar, and J. Lehoczky, «Real-Time Computing using Futurebus+,» IEEE Micro, June 1991, pp. 30-33; 95-99
[6] L. Sha, R. Rajkumar, and J. Lehoczky, «Priority Inheritance Protocols: An Approach to Real-Time Synchronization,» IEEE Trans. Computers, Sept. 1990, pp. 1175-1185
The Real-Time Specification for Java, Greg Bollella, James Gosling, IEEE Computer, June 2000, pp. 47-54, Reprinted with permission, 2000, Copyright IEEE CS, All rights reserved.
Насколько хорошо RTSJ удалось добиться поставленных целей?
При создании и совершенствовании RTSJ группа экспертов Real-Time for Java Experts Group (RTJEG) опиралась на список базовых требований, выработанных в результате исследования, проведенного под эгидой Национального института науки и технологии (NIST) в 1998 - 99 годах. Мы также учитывали основные принципы, выработанные с учетом мнения разработчиков, занимающихся созданием систем реального времени.
Основные требования NIST
Цель исследований, проведенных NIST, состояла в определении требований к поддержке программирования реального времени на платформе Java. Отчет об этом исследовании, опубликованный в сентябре 1999 года (L. Carnahan and M. Ruark, eds., «Requirements for Real-Time Extensions for the Java Platform»), определяет десять основных требований, перечисленных ниже.
1. Спецификация должна включать в себя оболочку для поиска и обнаружения имеющихся профайлов.
2. Любая сборка мусора должна иметь ограниченную, заранее обусловленную задержку.
3. Спецификация должна определять связи между нитями Java реального времени на том же уровне детализации, что и материалы существующих стандартов.
4. Спецификация должна включать в себя API-интерфейсы, допускающие взаимодействие и синхронизацию между задачами Java и других платформ.
5. Спецификация должна включать в себя обработку и внутренних, и внешних асинхронных событий.
6. Спецификация должна включать в себя некоторый механизм завершения асинхронных нитей.
7. Ядро должно предоставлять механизмы для реализации взаимных исключений без блокировки.
8. Спецификация должна предоставлять механизм, который позволит коду посылать запросы вне зависимости от того, исполняется ли он в нити Java реального времени или в нити, исполняющейся не в реальном времени.
9. Спецификация должна определять связи, которые существуют между нитями Java реального времени и нитями, исполняющимися не в реальном времени.
Как показано в таблице A, спецификация RTSJ удовлетворяет всем требованиям, за исключением первого, которое не применимо к RTSJ, поскольку последняя не включает в себя понятие профайлов. Доступ к физической памяти не является требованием NIST, но эта функция была включена по предложению программистов, создающих системы реального времени.
Основные принципы
Решение о том, какие функции и семантику включить в состав спецификации и как выбрать из возможных вариантов, принималось с учетом основных принципов. Ниже приводятся формулировки этих базовых принципов и комментарии, объясняющие, как RTSJ их выполняет. В тексте статьи функции и семантика RTSJ описаны более подробно.
Применимость к конкретной среде Java: спецификация RTSJ не должна включать в себя требования, которые ограничивают ее использование определенными средами Java, к примеру, конкретной версией JDK, Embedded Java Application Environment или Java 2 Micro Edition.
Обратная совместимость: RTSJ не должна препятствовать работе на реализациях RTSJ существующих, корректно написанных программ на Java, работающих не в реальном времени.
RTSJ несколько отступает от первого принципа в одном аспекте. Мы предлагаем два подкласса Thread, поскольку локализация понятий планирования, событий и асинхронной передачи управления в виде двух классов нитей реального времени позволяет предложить более понятную и согласованную спецификацию. Профайлы Java 2 Micro Edition могут исключать нити и, таким образом, подобные профайлы не поддерживают реализации RTSJ. Однако, мы считаем, что исключение нитей настолько меняет характеристики реализации, что другие функции RTSJ сами по себе окажутся бесполезны. Если такие профайлы станут широко использоваться, мы может рассмотреть вопрос о подмножествах RTSJ.
«Написано однажды - работает везде»: группа RTSJ признает важность этого принципа, но отдает себе отчет в том, насколько сложно добиться его выполнения для программ реального времени.
Этот принцип заставил нас усиливать существующую семантику, а не изобретать новую. Таким образом, мы допустили намного более тесную интеграцию внутри JVM, а также между стандартными механизмами Java и механизмами, которых требует RTSJ. Конечно мы могли бы просто потребовать, чтобы каждая реализация использовала две JVM, чтобы удовлетворить этому принципу, но мы посчитали, что более тесная интеграция позволит создавать более ясные, надежные и более гибкие реализации. Этот принцип также повлиял на наше решение добавить семантику синхронизированных слов вместо нового класса семафора.
Соблюдение этого принципа сразу позволило нам создавать спецификацию таким образом, чтобы разработчики могли выбирать между различными алгоритмами планирования и сборки мусора. Мы считаем, что такая гибкость сделает RTSJ приемлемым для более широкого круга систем реального времени.
Сложившаяся практика против новых возможностей: RTSJ должен учитывать существующую практику создания систем реального времени, а также позволять разработчикам в будущем легко добавлять более совершенные функции.
Требование использовать планирование приоритетов, а также нити реального времени, размещаемые вне динамической памяти, и память, линейно зависящую от времени, позволяет программистам придерживаться используемой сейчас модели разработки систем реального времени. Гибкость допускаемых реализаций, позволяющая включать альтернативные алгоритмы планирования и сборки мусора, поддерживает более совершенные модели программирования приложений.
Предсказуемое исполнение: При выборе различных вариантов решений основным приоритетом RTSJ было предсказуемое исполнение; и иногда ему в жертву приносились типичные параметры производительности вычислений общего назначения.
Требование, что описатели асинхронных событий должны иметь планируемую семантику нитей, подтверждает приоритетность принципа предсказуемости. Мы можем представить себе очень эффективные реализации механизма управления асинхронными событиями, но, скорее всего, они окажутся более ресурсоемкими решениями, чем простое исполнение описателя в контексте текущей нити. Мы считаем это необходимым, поскольку, в противном случае, исполнение описателей может потребовать значительного процессорного времени и будет невидимо для планировщика.
Никаких синтаксических расширений: Чтобы упростить работу создателя инструментария (и тем самым увеличить вероятность своевременных реализаций) RTSJ не должна вводить новые ключевые слова или вносить другие синтаксические расширения в язык Java.
Почти все функции RTSJ этому принципу удовлетворяют. Всегда проще добавить новые ключевые слова, но мы долго и упорно работали над тем, чтобы реализовать многие функции с помощью уже имеющегося синтаксиса.
Различия в компромиссах реализации: Реализации RTSJ могут отличаться эффективными и неэффективными алгоритмами поддержки принятия решения, реальным разрешением часов, использованием планирующих алгоритмов, не соответствующих минимальным требованиям, и длиной пути кода для исполнения байт-кодов. RTSJ не требует использования конкретных алгоритмов или параметров для этих реализаций, но, подчеркивает обязательность семантических требований спецификации. RTSJ предлагает разработчикам гибкость в создании реализаций, удовлетворяющих требованиям потребителей.
Хотя кажется естественным для спецификации реального времени требовать определенного временного детерминизма, мы постарались избежать этого. За исключением одного случая. Мы уверены, что требование даже самого, казалось бы, разумного детерминизма сужает область применения спецификации, поскольку может препятствовать созданию более дешевых реализаций. Мы, однако, прекрасно понимаем, что разработчики систем реального времени должны осознавать пределы детерминизма платформы, для которой он создают системы. В силу чего, RJSJ накладывает требования на документацию для определенных функций. Разработчики должны предоставить документацию, в которой указаны значения определенных параметров, которые дадут программисту достаточное количество информации для того, чтобы сконструировать корректную систему.
Базовые требования NIST | |||||||||
Функции RTSJ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Планирование | н/д | S | S | ||||||
Управление памятью | н/д | S | S | ||||||
Синхронизация | н/д | S | S | S | |||||
Обработка асинхронных событий | н/д | S | S | ||||||
Асинхронная передача управления | н/д | ||||||||
Асинхронное завершение нитей | н/д | S | S | ||||||
Доступ к физической памяти | н/д |
История создания RTSJ
Декабрь 1998: Sun Microsystems анонсировала программу Java Community Process и обратилась к корпорации IBM с просьбой возглавить направление деятельности, связанное с разработкой спецификаций для систем реального времени
Март 1999: Начало разработки
Сентябрь 1999: Анализ спецификаций участниками (все участники - лицензиаты Java и члены программы Java Community Process)
Декабрь 1999: Предварительные спецификации RTSJ предлагаются для широкого обсуждения
Февраль 2000: Завершение широкого обсуждения
Июнь 2000: RTSJ издается в виде книги (G. Bollella et al., The Real-Time Specification for Java, Addison Wesley Longman, Reading, Mass., June 2000)
Конец 2000: Подготовка окончательной версии RTSJ, создание эталонной реализации и тестовых пакетовRTSJ в открытом доступе находится в Web по адресу http://www.rtj.org. На этом узле также публикуются новости и изменения в спецификации. Комментарии и вопросы следует направлять по адресу comments@rtj.org