Введение

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

Характеристики

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

Межзадачное общение и совместное использование ресурсов

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

Временное маскирование/отключение прерываний

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

Мутекс

Когда общий ресурс необходимо зарезервировать, не блокируя при этом все остальные задачи (например, ожидание завершения записи во флэш-память), лучше использовать механизмы, также доступные в операционных системах общего назначения, такие как мьютекс и межпроцессный обмен сообщениями под управлением ОС. Такие механизмы включают системные вызовы и обычно вызывают код диспетчера ОС при выходе, поэтому для их выполнения обычно требуются сотни инструкций процессора, в то время как маскировка прерываний может занимать всего одну инструкцию на некоторых процессорах. Мьютекс (нерекурсивный) находится либо в заблокированном, либо в разблокированном состоянии. Когда задача заблокировала мьютекс, все остальные задачи должны ждать, пока его владелец – исходный поток – разблокирует его. Задача может установить тайм-аут для ожидания мьютекса. Существует несколько известных проблем, связанных с проектами на основе мьютексов, таких как инверсия приоритетов и взаимные блокировки (дедлоки). При инверсии приоритетов задача с высоким приоритетом ожидает, потому что задача с низким приоритетом владеет мьютексом, но задаче с низким приоритетом не выделяется процессорное время для завершения работы. Типичное решение заключается в том, чтобы задача, владеющая мьютексом, "наследовала" приоритет самой высокоприоритетной ожидающей задачи. Однако этот простой подход усложняется при наличии нескольких уровней ожидания: задача A ожидает мьютекс, заблокированный задачей B, которая, в свою очередь, ожидает мьютекс, заблокированный задачей C. Обработка нескольких уровней наследования приводит к тому, что другой код выполняется в контексте с высоким приоритетом, что может вызвать "голодание" потоков среднего приоритета. При взаимной блокировке две или более задач блокируют мьютексы без тайм-аутов и затем бесконечно ждут мьютексы друг друга, создавая циклическую зависимость. Самый простой сценарий взаимной блокировки возникает, когда две задачи поочередно блокируют два мьютекса, но в обратном порядке. Взаимные блокировки предотвращаются тщательным проектированием.

Передача сообщения

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

Операторы сбоев и планировщик

Поскольку обработчик прерываний блокирует выполнение задачи с наивысшим приоритетом, а операционные системы реального времени разработаны для минимизации задержки потоков, обработчики прерываний обычно делают максимально короткими. Обработчик прерываний, по возможности, откладывает любое взаимодействие с аппаратным обеспечением; как правило, достаточно лишь подтвердить или отключить прерывание (чтобы оно не возникло повторно при возврате из обработчика прерываний) и уведомить задачу о необходимости выполнения работы. Это можно сделать, разблокировав задачу драйвера посредством освобождения семафора, установки флага или отправки сообщения. Планировщик часто предоставляет возможность разблокировать задачу из контекста обработчика прерываний. ОС поддерживает каталоги управляемых ею объектов, таких как потоки, мьютексы, память и т.д. Обновления этих каталогов должны строго контролироваться. Поэтому может быть проблематично, если обработчик прерываний вызывает функцию ОС в то время, как приложение также выполняет вызов этой функции. Функция ОС, вызванная из обработчика прерываний, может обнаружить, что база данных объектов находится в несогласованном состоянии из-за обновления, выполняемого приложением. Существует два основных подхода к решению этой проблемы: унифицированная и сегментированная архитектуры. RTOS, реализующие унифицированную архитектуру, решают эту проблему, просто отключая прерывания во время обновления внутреннего каталога. Недостатком этого является увеличение задержки прерываний и потенциальная потеря прерываний. Сегментированная архитектура не выполняет прямые вызовы ОС, а делегирует задачи, связанные с ОС, отдельному обработчику. Этот обработчик работает с более высоким приоритетом, чем любой поток, но ниже, чем обработчики прерываний. Преимущество этой архитектуры заключается в том, что она добавляет очень мало циклов к задержке прерываний. В результате ОС, реализующие сегментированную архитектуру, более предсказуемы и способны обрабатывать более высокую частоту прерываний по сравнению с унифицированной архитектурой. Аналогично, режим управления системой (System Management Mode) на оборудовании, совместимом с x86, может потребовать значительного времени для возврата управления операционной системе.

Распределение памяти

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