Введение в операционные системы реального времени


PDF версия

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

Системы реального времени и встраиваемые системы работают в «стесненных» условиях, когда объем памяти и мощность процессора ограничены. Они должны обеспечивать работоспособность служб для пользователей и окружающего мира, с которым они взаимодействуют, в строгих временных рамках. Именно ограничения по времени, памяти и скорости навязывают использование ОС реального времени во встраиваемом программном обеспечении (ПО).
Ниже мы рассмотрим ядро ОС — часть операционной системы, предоставляющей большинство основных служб (функций) для прикладного программного обеспечения, работающего на процессоре. Обычно в службы ядра входят:
• распределение времени между задачами прикладного ПО;
• взаимодействие и синхронизация между задачами;
• динамическое распределение памяти ОЗУ (RAM);
• службы таймеров.

Основные службы ядра ОС

Ядро операционной системы реального времени (RTOS — Real-time Operating System) является абстрактным уровнем, скрывающим от прикладного ПО аппаратные особенности процессора (или набора процессоров), на котором прикладное ПО будет работать. Это обеспечивается за счет предоставления прикладному ПО доступа к пяти основным категориям базовых служб (функций) (см. рис. 1).

Рис. 1. Основные службы, предоставляемые ядром ОС реального времени
Device I/O supervisor — управление задачами ввода/вывода

Наиболее общая категория служб ядра — это категория «управление задачами». Эта группа позволяет разработчикам прикладного ПО проектировать программные продукты как набор различных «частей» («chunks») — каждая часть содержит отдельную тему, отдельную цель, и, возможно, свое собственное ограничение по реальному времени выполнения. Такая отдельная часть ПО называется задачей. Службы из этой категории позволяют запускать задачи и присваивать им приоритеты выполнения. Основная служба RTOS в этой категории — это служба планирования задач (служба распределения задач по времени) во время работы встраиваемой системы. Эта служба контролирует выполнение задач прикладного ПО, и заставляет работать их своевременно и управляемо. (Позже мы рассмотрим в деталях, как это происходит.)
Вторая категория служб — это категория «взаимодействие и синхронизация между задачами». Эти службы организуют передачу информации между задачами, исключая опасность ее искажения. Они также позволяют координировать задачи для их более эффективного взаимодействия между собой. Без поддержки этих служб RTOS задачи смогут передавать искаженную информацию или мешать друг другу. Так как ко множеству встраиваемых систем предъявляют строгие требования по времени, большинство ядер RTOS также позволяют использовать некоторые основные таймерные службы, такие как тайм-ауты и задержки выполнения задач.
Многие (но не все) ядра RTOS поддерживают работу со службами динамического распределения памяти. Эта категория служб позволяет задачам «заимствовать» блоки памяти ОЗУ для временного использования в прикладном ПО. Зачастую эти блоки памяти затем переходят от задачи к задаче, посредством чего и передаются большие объемы данных между ними. В некоторых очень маленьких RTOS-ядрах, предназначенных для сильно ограниченной по объемам памяти среды, служб динамического распределения памяти нет.
Большинство (но опять же, не все) RTOS-ядер поддерживает также категорию служб по управлению устройствами ввода/вывода. Эти службы предоставляют унифицированную структуру для планирования и доступа к многочисленным аппаратным драйверам устройств, типичным для встраиваемых систем.
В добавление к службам ядра многие ОС реального времени предлагают набор необязательных подключаемых компонентов ОС для служб высокого уровня, таких как организация файловой системы, передача данных через сеть, управление сетями, управление базами данных и графический интерфейс пользователя. Также многие из этих добавочных компонентов занимают намного больший объем памяти и являются более сложными по сравнению с ядром RTOS; они опираются на ядро ОС и используют его базовые службы. Для уменьшения потребляемой памяти каждая из этих добавочных компонентов включается во встраиваемую систему только при необходимости.
В данной статье мы рассмотрим основные службы ядра ОС реального времени для решения проблем по управлению задачами, динамическому выделению памяти, взаимодействию и синхронизации между задачами.

Сравнение универсальной ОС и ОС реального времени

Многие операционные системы, работающие не в реальном времени, также поддерживают схожие службы ядра ОС. Ключевое различие между ОС реального времени и универсальными ОС — это необходимость в детерминированном временном поведении (что присуще RTOS).
Формально детерминированость времени означает, что службы ОС затрачивают на каждую задачу только известное и заранее определенное количество времени. В теории это время может быть выражено в математических формулах. Эти формулы должны быть только алгебраическими и не содержать каких-либо случайных временных компонент. Случайные составляющие времени работы службы могут привести к случайным задержкам в прикладном программном обеспечении, и, таким образом, привести к неожиданному выходу приложения за границы реального времени — случай, совершенно недопустимый для встраиваемых систем реального времени.
Универсальные операционные системы нереального времени зачастую достаточно недетерминированы. Их службы могут добавлять случайные задержки в работу прикладного ПО и, следовательно, приводить к замедлению начала работы приложения на непрогнозируемую величину. Если вы спросите у разработчика универсальной ОС алгебраическую формулу, описывающую временное поведение одной из его служб (такой, как отправка сообщения от задачи к задаче), вы ее точно не получите. Детерминированость времени не является целью разработки этих универсальных ОС.
С другой стороны, операционные системы реального времени зачастую идут на шаг впереди базового детерминизма. Для большинства служб ядра эти ОС предлагают постоянное, независимое от загруженности, время: Т (время_отправки_сообщения) = константа, безотносительно к длине отправляемого сообщения или другим факторам, таким как количество задач, очередей и сообщений, контролируемых RTOS.

Распределение задач по времени (планирование выполения)

Рис. 2. Пример работы планировщика с приоритетным прерыванием работы

Большинство ОС реального времени распределяют задачи по времени выполнения, используя схему, называемую планирование с приоритетным прерыванием работы (priority-based pre-emptive scheduling). Каждой задаче прикладного ПО должен быть присвоен приоритет, при этом, чем выше значение приоритета, тем выше необходимость в немедленном выполнении задачи. Немедленное выполнение достигается за счет приоритетной природы распределения задач. Это означает, что планировщик может остановить любую задачу в любой момент ее выполнения, если он определит, что другая задача, обладающая более высоким приоритетом, требует немедленного запуска. Другими словами, если одновременно готовы к выполнению задачи с высоким и низким приоритетом, планировщик вначале разрешит выполнение задачи с высоким приоритетом. Задача с низким приоритетом начнет работу только после того, как задача с высоким приоритетом закончит свою текущую работу.
А что если задача с низким приоритетом уже начала работу, и затем появилась задача с высоким приоритетом, готовая к выполнению? Подобная ситуация может произойти при внешнем инициировании, к примеру, при переключении ключа. Планировщик с приоритетным прерыванием работы будет действовать следующим образом: он позволит задаче с низким приоритетом завершить выполнение текущей ассемблерной команды (но он не будет ждать выполнения целой строки кода, написанной на языке высокого уровня, так же, как и не будет ждать до следующего сигнала таймера.) Затем он остановит работу задачи с низким приоритетом и разрешит выполнение задачи с высоким приоритетом. После того, как задача с высоким приоритетом закончит текущую работу, будет продолжено выполнение задачи с низким приоритетом (см. рис. 2).
Конечно, пока выполняется задача с высоким приоритетом, может появиться задача с еще более высоким приоритетом, готовая к работе. В таком случае выполнение текущей задачи будет приостановлено (временно), чтобы начать выполнение задачи с более высоким приоритетом. После того, как эта задача выполнит свою текущую работу, приостановленной задаче будет разрешено продолжить свою работу. И, следовательно, в таком случае, обе задачи с высоким приоритетом закончат свою работу, перед тем как задаче с низким приоритетом разрешат продолжить выполнение. Подобный сценарий может быть назван гнездовым прерыванием работы.
Каждый раз, когда при внешней инициализации (например, при переключении ключа) или при программной инициализации (например, при появлении сообщения) приводится в состояние готовности планировщик с приоритетным прерыванием работы, он должен пройти следующие 5 шагов:
• определить, должна ли выполняемая в настоящее время задача продолжать работу. Если нет, то перейти к следующему пункту;
• определить, какая из задач должна выполняться следующей;
• сохранить условия работы остановленной задачи (чтобы можно было продолжить ее выполнение позже);
• выставить необходимые условия выполнения задачи, которая будет запущена следующей;
• разрешить выполнение этой задачи.
Вместе эти 5 шагов называют переключением задач.

Переключение задач за фиксированное время

Рис. 3. Время переключения задач в зависимости от их количества для универсальной ОС и ОС реального времени

Время, уходящее на переключение задач, представляет интерес при оценке операционной системы. Простая универсальная (без приоритетных прерываний) ОС позволяет переключать задачи только в моменты времени, отсчитываемые таймером. Единица отсчета может равняться десяти миллисекундам. Следовательно, если появится необходимость в переключении задачи где-нибудь в пределах десятимиллисекундного окна, фактическое переключение сможет произойти только по окончании текущего периода. Подобная задержка недопустима в большинстве встраиваемых систем реального времени.
В более сложных планировщиках с приоритетным прерыванием работы необходимо производить поиск в очередях задач для определения той, которую требуется запустить. Если просматриваемых задач много, поиск займет больше времени. Подобные операции поиска предусмотрены зачастую в универсальных ОС, что делает их недетерминированными. Операционные среды реального времени, с другой стороны, избегают этого, используя пошагово обновляемые таблицы, что позволяет планировщику идентифицировать задачу, которая должна выполняться следующей, за фиксированный короткий промежуток времени.
Для универсальных (не реального времени) ОС, время, затрачиваемое на переключение, как правило, возрастает с добавлением новых задач, требующих управления (см. рис. 3). Однако, фактическое время, затрачиваемое на переключение, не равняется времени, показанному на рисунке 3 наклонной пунктирной линией. Вместо этого, для любого рассматриваемого примера переключения оно может быть намного дольше или напротив, короче. Закрашенные области, окружающие наклонную пунктирную линию, показывают вероятность попадания фактического времени переключения задач в области над или под ней.
Горизонтальная сплошная линия характеризует время переключения ОС реального времени. Она постоянно и не зависит от нагрузки — количества задач в системе программного обеспечения.
Следует отметить, что в некоторых случаях, относящихся в крайней левой области рисунка, время переключения для универсальных ОС может быть меньше, чем для ОС реального времени. Это не снижает «пригодность» ОС реального времени для встраиваемых приложений реального времени. Ибо, по существу, термин «реальное время» не означает «как можно быстрее». Скорее, реальное время требует последовательных, повторяемых, заранее известных временных характеристик. Хотя универсальная ОС может иногда осуществлять быстрые переключения при небольшом количестве задач, в следующий раз при переключении тех же задач она может выдать большую задержку. Достоинство RTOS в ее заранее известных, повторяемых временных характеристиках, которые, к тому же, обычно лучше, чем у недетерминированного планировщика в случаях большого числа задач в системе программного обеспечения. ОС реального времени показывает намного меньшие времена переключения чаще, чем ее конкурент — ОС не реального времени, при количестве задач больше 5…10.

Синхронизация и обмен информацией между задачами

Рис. 4. Обмен информацией между задачами

Большинство ОС, включая и ОС реального времени, предлагают разнообразные схемы (механизмы) для синхронизации и обмена информацией между задачами. Эти механизмы необходимы для режима приоритетного прерывания в любой задаче, т.к. без них они могут передавать искаженную информацию или мешать друг другу.
Например, задача может быть прервана в середине работы по обновлению таблицы с данными. Если следующая задача, выполнение которой началось после переключения, считает данные из этой таблицы, она получит сочетание устаревших и только что обновленных данных. Эта смесь старой и обновленной информация может быть некорректной или же вообще не иметь смысла. Примером может служить таблица с измерениями температуры, начинающаяся со значения «10°C». Задача начинает обновление содержимого таблицы, познаково записывая в нее новое значение «99°F». Если работа этой задачи будет прервана в середине процесса обновления, следующая задача, на которую произошло переключение, считает значения вроде «90°C» или «99°C» или «99°F», в зависимости от того, в какой момент произошло переключение. Частично обновленные значения, очевидно, ошибочны и вызваны непростым случайным совпадением по времени, очень трудным для отладки или точного воспроизведения.
Для того чтобы избежать появления подобных ошибок, в ОС реального времени и предусматривается механизм синхронизации и обмена информацией между задачами. Большинство ОС реального времени обладают несколькими подобными механизмами, причем каждый из них оптимизирован для надежной передачи информации различного типа от задачи к задаче.
Наверное, самый распространенный вариант обмена информацией между задачами во встраиваемых системах — это последовательная передача данных от одной задачи к другой. Большинство ОС реального времени предусматривают подобный механизм передачи данных (см. рис. 4). Каждое сообщение может содержать массив или буфер с данными. Если сообщения посылаются быстрее, чем они могут быть обработаны, ОС реального времени создает очереди для их хранения до тех пор, пока они не будут переданы дальше (см. рис. 5).
Другой вариант обмена информацией между задачами во встраиваемых системах — это передача от одной задачи к другой так называемой синхронизованной информации. Синхронизованная информация похожа на команды, причем некоторые команды могут быть положительными, а некоторые отрицательными. К примеру, отрицательной командой к задаче может быть что-либо, подобное следующему: «Пожалуйста, не распечатывайте тотчас же, т.к. моя задача использует принтер». Или более общее: «Я хочу заблокировать Х только для моего пользования». Положительной командой может быть такое: «Я обнаружил сердечный приступ, и я хочу, чтобы вы помогли мне справиться с ним». Или более общее: «Пожалуйста, помогите мне справиться с У».
Большинство ОС реального времени для реализации отрицательной синхронизации предусматривают наличие семафоров или механизма взаимоисключения. Эти подходы позволяют задачам резервировать определенные ресурсы встраиваемых систем только для собственного пользования, и впоследствии разблокировать их после выполнения работы.
Для положительной синхронизации ОС реального времени предусматривают различные механизмы. Некоторые RTOS предлагают флаги событий; другие — использование сигнало. При этом некоторые опираются на передачу информации при положительной синхронизации, как на способ передачи данных.

Детерминизм и высокоскоростная передача данных

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

Рис. 5. Передача сообщений с помощью очередей

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

Динамическое распределение памяти

Вопрос детерминированности времени в службах ОС возникает и в области динамического распределения памяти ОЗУ (RAM). Многие универсальные ОС используют для динамического размещения данных область памяти, называемую «кучей» (heap). Распространенные функции (службы) malloc и free, известные Си-программистам, работают, используя именно этот подход. Задачи могут временно заимствовать память из «кучи» операционной системы, вызывая malloc и указывая треуемый объем. После завершения этой (или другой) задачей работы с выделенной памятью, она может быть возвращена операционной системе функцией free. После этого операционная система вернет ее в heap, где она, возможно, будет использоваться как часть другого блока — возможно, большего объема, или напротив, разбита на более мелкие части.
На память, выделяемую под heap, влияет так называемый эффект «фрагментации внешней памяти» (external memory fragmentation), который может привести к ухудшению работы служб, управляющих «кучей». Дробление связано с тем, что при возвращении блока памяти в «кучу» он при последующем вызове функции malloc и соответствующем задании требуемого объема может быть разбит на более мелкие куски. После выполнения многих циклов malloc и free между используемыми блоками могут появиться маленькие «полоски» памяти. Они настолько малы, что являются бесполезныи для задач. Но «полоски» располагаются между блоками памяти, используемыми задачами, что приводит к невозможности объединения («склеивания») блоков в большие объемы памяти. Со временем в «куче» будет все больше и больше таких «полосок». Это в конечном счете приведет к ситуаци, при которой задачи попытаются зарезервировать блок памяти определенного объема (malloc), а ОС откажет им в этом — даже несмотря на наличие необходимого объема памяти в «куче».

Рис. 6. Список свободных блоков буферной памяти

Проблема фрагментации может быть решена с помощью дефрагментирующего ПО, реализующего алгоритм, называемый «сборка мусора» (garbage collection). К сожалению, зачастую это абсолютно недетерминированный алгоритм — он вносит в управление «кучей» беспорядочно возникающие задержки случайной длины. Подобную ситуацию можно часто наблюдать при работе служб динамического распределения памяти в универсальных ОС.
С другой стороны, ОС реального времени избегают реализации этого алгоритма и временных последствий его применения, предлагая нефрагментируемые схемы распределения памяти вместо «кучи». Они обходят фрагментацию памяти, ограничивая размер блоков памяти, доступных для прикладного ПО. Несмотря на то, что данный подход не столь гибок, как схема с применением «кучи», он позволяет избежать дробления внешней памяти и необходимости ее дефрагментации. К примеру, схема распределения буферной памяти (pools memory) позволяет прикладному программному обеспечению использовать блоки памяти от четырех до восьми различных размеров для каждого буфера. Подобная схема полностью исключает возможность дробления внешней памяти, т.к. не предусматривает в будущем деления возвращенного буфера на более мелкие части. Вместо этого при освобождении буфера он помещается в список свободных блоков буферной памяти прежнего размера, доступных в будущем для повторного использования (см. рис. 6). Память распределяется и перераспределяется с детерминированным, часто неизменным, временем.
Проблема детерминированности времени является основной при определении различий между универсальными ОС и ОС реального времени. Эта проблема прослеживается во многих составляющих частях ядер операционных систем, таких как планировщики задач, динамическое распределение памяти и передача информации между задачами. В то время как универсальные ОС в этих областях зачастую используют недетерминированные службы, в этой статье были описаны полностью детерминированные решения. OSE является примером ОС, реализующей все описанные решения в компактном высокопроизводительном ядре.
Детерминированность времени особенно важна во встраиваемых системах реального времени, применяемых в бортовых вычислительных машинах, медицинских системах и системах связи.

Оставьте отзыв

Ваш емейл адрес не будет опубликован. Обязательные поля отмечены *