Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Разработка операционной системы TinyOS

.doc
Скачиваний:
19
Добавлен:
25.03.2016
Размер:
62.98 Кб
Скачать

Разработка операционной системы TinyOS [HSW00] связана с появлением новой концепции беспроводной связи – Motes. Motes (в переводе с английского – пылинки, соринки) – это реализация идеи "smart-dust" ("распыленной разумности"), предложенной оборонным агентством Darpa (Defense Advanced Research Projects Agency), в частности, для отслеживания передвижений противника.

Motes разработаны в Калифорнийским университете в Беркли совместно с Intel, и в настоящее время ведутся испытания этих самоорганизующихся сетей, построенных на основе открытых технологий Intel Mote и программного обеспечения TinyOS, TinyDB.

Умные сенсоры Motes, распределенные в пространстве, могут самостоятельно связываться друг с другом, образуя распределенную беспроводную информационную сеть. "Пылинка разума" состоит из 8-битового микроконтроллера семейства Amtel AVR, приемопередающего интегрального модуля TR1000 и двух микросхем средней степени интеграции – энергонезависимой памяти и дополнительного загрузочного микроконтроллера, позволяющего по радиоканалу обновлять ПО центрального процессора – AVR.

"Smart-dust" создавалась для динамических, изменяющихся как в пространстве, так и во времени сетей – для той области, в которой абсолютно неприменимы ни традиционные алгоритмы управления, ни отработанные принципы маршрутизации, ни архитектурные решения, лежащие в основе традиционного системного ПО. Стремление конструкторов сделать ее как можно более компактной (в перспективе – 1 мм3) влечет за собой ряд существенных ограничений, в первую очередь энергетических. Ограниченные вычислительные ресурсы и динамический характер сети приводят к тому, что функциональность "пылинки" надо время от времени изменять, что может быть достигнуто только одним способом – передачей по радиоканалу нужного ПО. С другой стороны, энергетическая дороговизна передачи информации требует сверхкомпактного представления передаваемого кода, в противном случае "пылинки" просто не будут работать из-за быстрого истощения крохотных автономных источников питания.

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

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

Архитектура TinyOS объединяет такое привычную составляющую, как планировщик задач (scheduler), и оригинальное понятие – компонентный граф. Термин "компонент" здесь одновременно и соответствует общепринятому пониманию, и существенно расширяет его. Так, интерфейс компонента TinyOS состоит из двух множеств – верхнего, предоставляемого этим компонентом, и нижнего, требуемого для его функционирования. Каждое из этих множеств содержит описания команд и событий – синхронных и асинхронных процессов.

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

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

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

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

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

В системе предусмотрена также отдельная абстракция задачи, понимаемая как продолжительный вычислительный процесс. Взаимоотношение между понятиями “команда” и “задача” следующее: команда – это атомарная составляющая задачи. Команда ставится в очередь на исполнение планировщика, затем она выполняется и может быть временно прервана обработкой события.

Планировщик работает по принципу очереди FIFO, т.е. для передачи управления следующей задаче требуется полное завершение предыдущей задачи. Однако, в зависимости от требований приложения, могут использоваться и более сложные механизмы планирования, основанные на приоритетах или на дедлайнах. Ключевым моментом является то, что планировщик ориентирован на энергосбережение: процессор засыпает, если очередь планировщика пуста, а периферийные устройства работают, и каждое из них может разбудить систему. Когда очередь становится пустой, новый поток может быть запущен на исполнение только в результате какого-либо события, которое может возникнуть только в аппаратных устройствах. Планировщик имеет крайне малые размеры – всего 178 байтов, данные планировщика занимают только 16 байтов.

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

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

Для обеспечения динамичности во время выполнения была разработана виртуальная машина, которая является надстройкой над ОС TinyOS. Код для виртуальной машины можно загрузить в систему во время выполнения. Для работы этой виртуальной машины необходимы 600 байт оперативной памяти и менее 8 KB памяти команд 8-битового микроконтроллера. Программы виртуальной машины представляются 8-битовыми инструкциями всего трех типов, объединяемых в "капсулы" – атомарные последовательности не более чем двадцати четырех инструкций.

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

Беспроводные сенсорные сети (БСС) состоят из миниатюрных вычислителей — мотов, снабженных сенсорами (температуры, освещенности, давления и т.д.) и приемопередающими радио­устройствами. Поскольку размер мота должен быть небольшим, его питание осуществляется от маломощной батареи. Моты применяются для сбора и первичной обработки сенсорных данных, которые они передают друг другу. Структура сенсорной сети формируется автоматически таким образом, чтобы в конечном счете все сенсорные данные были получены шлюзом, имеющим соединение с корпоративной сетью. Как уже отмечалось (см. КомпьютерПресс № 4’2008), современный технологический уровень развития БСС дает возможность перейти к практическому использованию сенсорных сетей, но для этого необходимо уметь разрабатывать программное обеспечение для мотов. В настоящее время для этой цели имеются специальные средства и даже технологии, к которым в первую очередь относятся операционная система TinyOS и язык программирования NesC.

TinyOS — это операционная система класса Open Source, разработанная специально для использования в БСС. Более того, TinyOS — первая ОС для БСС. Сама TinyOS написана на языке NesC, который разрабатывался именно для программирования приложений, ориентированных на работу в среде TinyOS, что позволило сделать работу программ, написанных на NesC, максимально эффективной. В связи с этим есть смысл сначала остановиться на принципах организации работы TinyOS, а затем перейти к конкретным инструментальным средствам языка NesC.

Язык программирования NesC

NesC (network embedded system C) — это компонентно-ориентированный язык программирования, построенный на базе C. Основной структурной единицей программы на NesC является компонент, который через интерфейсы взаимодействует с другими компонентами. Интерфейсы в NesC двунаправленны, они определяют способы взаимодействия между двумя типами компонентов: пользователями и провайдерами. Интерфейсы могут содержать объявления команд и событий. Реализации команд должны быть описаны в компоненте-провайдере, а реализации обработчиков объявленных в интерфейсе событий — в компоненте-пользователе:

interface SendMsg {

command result_t send(uint16_t address, uint8_t length, TOS_MsgPtr msg);

event result_t sendDone(TOS_MsgPtr msg, result_t success);

}

В приведенном примере представлена часть стандартного интерфейса SendMsg для отправки сообщения в сеть. Компонент-провайдер этого интерфейса предоставляет команду send, которой может воспользоваться компонент-пользователь для отправки сообщения, а после отправления сообщения компонент-провайдер вернет компоненту-пользователю сигнал о событии sendDone, значением параметра которого будет результат операции отправки сообщения (см. рисунок).

 

В NesC существует два типа компонентов: модули и конфигураторы. Модули описывают действующие механизмы компонента: команды, которые он предоставляет, и обработчики событий. Обычно разработка модулей не вызывает больших сложностей, если разработчик знаком с программированием на стандартном языке С. Конфигураторы описывают связи между интерфейсами других компонентов, а также могут предоставлять свои интерфейсы (экспортировать интерфейсы). Несмотря на то что при реализации конфигуратора используется весьма простой язык, содержащий всего три оператора: «->», «<-», «=», — написание конфигуратора является довольно нетривиальной задачей, поскольку далеко не всегда можно сразу продумать эффективные связи между интерфейсами компонентов.

Описание любого компонента в NesC, будь то конфигуратор или модуль, состоит из двух секций: интерфейсной и секции реализации. Интерфейсная секция компонента содержит объявления команд, событий и интерфейсов (в дальнейшем команды, события и интерфейсы мы будем называть одним общим термином — функции), которые использует или предоставляет компонент. Если компонент использует функцию, на это указывает зарезервированное слово uses, если предоставляет — provides. Эта секция также может быть пустой, что свойственно конфигураторам, или содержать объявления только используемых или только предоставляемых функций. Взаимодействие с другими компонентами осуществляется через объявленные функции.

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

Как видно из примера, в компоненте-конфигураторе BlinkС отсутствует интерфейсная секция, а значит, сам конфигуратор BlinkС не предоставляет и не использует никаких функций, а лишь связывает другие компоненты. В секции реализации присутствуют четыре компонента. Синтаксис оператора связи «->» следующий:

<компонет-пользователь>.<имя_интерфейса> -> <компонент_провайдер>[.<имя_нтерфейса>];

Итак, приложение для TinyOS представляет собой набор компонентов, каждый размером примерно 200 байт, и интерфейсов для межкомпонентного взаимодействия. Для каждого конкретного приложения формируется свой набор компонентов. Полученное приложение на этапе компиляции для конечной платформы (iris, mica2, telos, и т.д.) интегрируется с ядром системы в один выполняемый файл, который и загружается на сенсорный узел.

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