Таджикистан, Душанбе, Таджикский Технический Университет (ТТУ), 2013 |
Архитектура встраиваемой ОС реального времени – CE 6.0
Межпроцессная коммуникация
CE поддерживает для межпроцессной коммуникации модели общей памяти и передачи сообщений. Существует много ситуаций, где имеет место использование общей памяти. Наиболее распространенным является передача буфера между процессами пользователя, драйверами и серверами. Приложения пользователя также могут совместно использовать память. В CE функции CeRemoteHeapCreate и CeRemoteHeapTranslatePointer позволяют создавать кучу, которая доступна только ядру или серверу в режиме пользователя и клиенту, где сервер может выделять, освобождать, читать и писать, в то время как клиент может читать и писать, но не может исказить метаданные кучи. Отображенные в память файлы позволяют нескольким процессам общаться, используя объект файла в качестве нижележащей среды коммуникации. Файл может быть реальным или с поддержкой в памяти, а отображение обеспечивает доступ к данным с помощью обычных указателей. Операционная система заботится о том, чтобы все процессы видели одинаковые данные.
Для отображенных в память указателей в CE используйте функции CeOpenCallerBuffer, CeCloseCallerBuffer, CeAllocAsynchronousBuffer, и CeFreeAsynchronousBuffer. Используйте ReadProcessMemory вместе с WriteProcessMemory. Соедините парой указатель с идентификатором процесса и передавайте их вместе. Используйте функции VirtualAllocEx, VirtualCopyEx, и VirtualAllocCopyEx для задания альтернативных точек входа в память для процессов. Эти механизмы обычно реализуются драйверами или серверами процесса режима пользователя/ядра. CeOpenxxx/CeClosexxx на самом деле реализуются с помощью Read/WriteProcessMemory. Они являются вспомогательными функциями, используемыми обычно драйверами или серверами режима ядра/пользователя для доступа к буферу памяти клиентского процесса. Read/WriteProcessMemory могут использоваться любым процессом, но требуют указатель на целевой процесс. Функции VirtualAlloc/CopyEx также требуют указатель на целевой процесс. Функцию VirtualCopyEx можно вызывать только из режима ядра.
Очереди сообщений P2P позволяют процессам эффективно передавать сообщения. Это не настоящая реализация общей памяти, но имеет небольшие накладные расходы и полезна во многих сценариях. Ниже показаны предоставляемые в CE функции очереди сообщений.
- CreateMsgQueue - Создает или открывает определенную пользователем очередь сообщений.
- OpenMsgQueue - Открывает указатель на существующую очередь сообщений.
- CloseMsgQueue - Закрывает открытую очередь сообщений.
- ReadMsgQueue - Читает одно сообщение из очереди сообщений.
- WriteMsgQueue - Записывает одно сообщение из очереди сообщений.
- GetMsgQueueInfo - Возвращает информацию об очереди сообщений.
Сообщение WM_COPYDATA посылается, когда приложение передает данные другому приложению. Сообщение WM_SYSCOPYDATA посылается, когда компонент системы передает данные другому компоненту системы.
Message Queuing (MSMQ) является совершенно другим типом системы обмена сообщениями, которая используется для высокоуровневой коммуникации между устройствами в сети. MSMQ не используется для межпроцессной коммуникации на одном устройстве.
Обработка прерываний
Приложения реального времени используют прерывания для своевременного ответа на внешние события. Для этого CE разбивает обработку прерывания на два шага: процедуру обработки прерывания (ISR - Interrupt Service Routine) и поток обработки прерывания (IST - interrupt service thread). ISR выполняется сразу, IST может затратить некоторое время, которое необходима для выполнения работы. Каждый запрос прерывания (IRQ - Interrupt Request) ассоциируется с ISR. ISR может отвечать нескольким источникам IRQ.
Когда прерывания разрешены и происходит прерывание, ядро вызывает зарегистрированную ISR для этого прерывания. После завершения ISR возвращает идентификатор прерывания. Ядро проверяет возвращаемый идентификатор прерывания и задает соответствующее событие. Когда ядро задает событие, IST начинает обработку.
Обработчик исключительных ситуаций является основной целью всех прерываний. Когда происходит прерывание, микропроцессор передает управление обработчику исключительных ситуаций в ядре. Обработчик исключений затем вызывает ISR, зарегистрированную для обработки текущего прерывания. ISR отвечает за трансляцию прерывания в идентификатор логического прерывания, SYSINTR, который передает в ядро как свое возвращаемое значение. Ядро задает событие, ассоциированное с логическим прерыванием, которое вызывает установку очередности обслуживания потока обработки прерывания (IST). Код в IST отвечает за обслуживание прерывания устройства.
Обслуживание IRQ в Windows Embedded CE 6.0 начинается с ядра, которое перехватывает все исключения, а затем определяет соответствующее действие. В случае IRQ ядро перехватывает IRQ, сохраняет регистры, поддерживаемые для ISR, и вызывает определенную вами ISR. Примерами аппаратных IRQ могут быть изменение состояния последовательного порта COM, или нажатие кнопки на считывателе штрих-кода. ISR располагается в OAL.
Процедура обработки прерывания (ISR) является кодом, который обрабатывает запросы прерываний (IRQ) на целевом устройстве. ISR является центральной частью OAL и отвечает за определение источника прерывания, маскирование его, и возврат уникального идентификатора в ядро Windows Embedded CE 6.0 для указания, какой драйвер должен использоваться для обработки события. Когда в ISR добавляется поддержка для других источников прерывания, имеются другие процедуры поддержки прерываний, которые нужно будет реализовать для отображения аппаратных прерываний в уникальные идентификаторы - отображения IRQ в SYSINTR, активация и деактивация прерываний, и т.д. Используя код OAL система CE ассоциирует каждый IRQ с ISR. Когда происходит прерывание, обработчик исключений ядра вызывает зарегистрированную процедуру ISR для этого прерывания. Используя функцию HookInterrupt в OAL, можно зарегистрировать только один одну ISR для каждой линии IRQ. Однако можно ассоциировать ISR с одним или несколькими идентификаторами прерываний. Когда ISR возвращает управление в ядро, ядро проверяет возвращаемый идентификатор прерывания и задает связанное с ним событие. Соответствующий поток IST в драйвере устройства зависит от события и освобождается для обработки события, когда становится потоком с самым высоким приоритетом в системе. Для управления ISR необходимо реализовать функции управления ISR, которые позволяют ядру начать, обслужить и завершить обработку прерывания.
Поток обслуживания прерывания (IST) является потоком, который выполняет большую часть обработки прерывания. ОС активирует IST, когда ОС имеет прерывание для обработки. Иначе IST будет неактивным. Драйвер устройства или OAL может связать идентификатор прерывания с событием, создавая IST и вызывая функцию InterruptInitialize. IST затем ожидает событие. IST вызывает функции в аппаратном, зависящем от платформы, слое драйвера, чтобы прочитать или записать в целевое устройство. Чтобы ОС активировала IST, IST должен связать объект события с идентификатором прерывания. Используйте функцию CreateEvent для создания объекта события.
После обработки прерывания IST должен ожидать следующий сигнал прерывания. Этот вызов может находиться в цикле. Когда происходит аппаратное прерывание, ядро сигнализирует о событии от имени ISR, и затем IST выполняет необходимые операции В/В в устройстве, чтобы получить данные и обработать их. Когда обработка прерывания завершается, IST должен информировать ядро о новой возможности разрешения аппаратного прерывания. Обычно потоки IST выполняются с приоритетом выше нормального. Они могут повышать свой приоритет до регистрации своего события в ядре, вызывая функцию CeSetThreadPriority.
Рисунок 6.11 показывает действия, вовлеченные в обработку прерывания в CE:
- Оборудование генерирует запрос прерывания (IRQ).
- Программа обработки прерываний (ISH - Interrupt Service Handler) является приемником всех прерываний и исключений. Она действует при выключенных прерываниях. Вспомогательный обработчик задает стек и т.д. для вызываемых ISR и определяет подходящую ISR для вызова.
- Процедура ISR проверяет оборудование, чтобы определить, имеется ли действительное прерывание, и возвращает логический ID для прерывания ( SYSINTR_xxx ) или SYSINTR_NOP. ISR обычно отключает прерывание для этого IRQ в контроллере прерываний, чтобы избежать дополнительных прерываний, пока не завершится обработка.
- Обработчик поддержки прерываний ищет SYSINTR во внутренней таблице и находит событие, связанное с этим ID. Он задает это событие, чтобы планировщик мог спланировать и выполнить его.
- Обработчик поддержки прерываний снова разрешает прерывания для всех прерываний.
- Когда связанный с IRQ поток IST является выполняющимся потоком с самым высоким приоритетом, планировщик переключается на этот поток, чтобы обработать прерывание.
- IST выходит из своего вызова WaitForSingleObject() на событии прерывания и обрабатывает прерывание. Он должен минимально очистить или отключить прерывание на устройстве, а затем вызвать InterruptDone() перед дальнейшей обработкой.
- InterruptDone() восстанавливает IRQ на контроллере прерываний, чтобы происходили другие прерывания. Именно поэтому она должна вызываться как можно скорее, так как другие устройства, совместно использующие прерывание, блокируются.
- IST продолжает обработку и очищает и снова включает прерывание на устройстве и возвращается к ожиданию другого прерывания. Разрешение прерываний более высокого приоритета является ответственностью ISR в OAL. OAL/ISR также отвечают за отключение обрабатываемого прерывания или за очистку прерывания в источнике, чтобы позволить правильно обрабатывать последующие прерывания.
Одним из наиболее важных аспектов производительности ядра в реальном времени является возможность обслуживать IRQ в течение строго определенного периода времени.
Задержка прерывания относится, прежде всего, к задержкам программной обработки прерываний; то есть, количеству времени, которое проходит с момента, когда внешнее прерывание приходит в процессор и до момента, когда начинается обработка прерывания. Если подкачка страниц не происходит, время задержки прерываний в CE ограничено для потоков, заблокированных в памяти. Это делает возможным тестировать задержки худшего случая - общее время до запуска ISR и запуска IST. Общее количество времени, пока прерывание не будет обработано, можно затем определить вычисляя время, необходимое в ISR и IST.
Задержка для ISR включает время, которое требуется ядру для направления на регистры сохранения обработчика ISR (стандартное), и т.д., и время, в течение которого прерывания выключены (переменное). Задержка ISR является интервалом времени, который начинается, когда IRQ задается в ЦП, и заканчивается, когда начинает выполняться ISR. Начало ISR, которое измеряется, можно вычислить на основе текущего состояния других прерываний в системе. Если выполняется прерывание, вычисление начала новой процедуры ISR, которое будет замеряться, должно учитывать два фактора: число прерываний с более высоким приоритетом, которые произойдут после рассматриваемого прерывания, и время, затраченное на выполнение ISR.
Чтобы предотвратить потерю и задержку высокоприоритетных прерываний, ядро CE использует вложенные прерывания. Вложенные прерывания позволяют запросам прерываний (IRQ) с более высоким приоритетом вытеснять IRQ с более низким приоритетом. Вложенные прерывания допускаются в соединении с Real-Time Priority System (Система приоритетов реального времени). ISR с более высоким приоритетом могут вытеснять ISR с более низким приоритетом. Ядро управляет деталями сохранения состояния ISR, когда происходит прерывание с более высоким приоритетом, и восстановлением его после завершения ISR с высоким приоритетом. В большинстве случаев вытесненная ISR не обнаруживает, что была вытеснена. Уровень вложения прерываний ограничен только тем, что может поддерживать аппаратная платформа.
Процедура ISR, которая использует слишком много времени, может вызывать полную потерю других прерываний, приводя к нестабильной или замедленной производительности всей системы. Разделяя обработку прерываний на очень короткие процедуры ISR и более длинные потоки обслуживания прерываний (IST), прерывания можно замаскировать на самое короткое возможно время. ISR маскируют прерывания только равного или более низкого приоритета, чем они сами. CE может делать вложенные ISR такой глубины, которую поддерживает аппаратная платформа. Помимо возможной задержки завершения, ISR больше никак не зависит от изменения приоритетов в ядре.
Драйверы устройств
Драйвер устройства является программой, которая абстрагирует функции физического или виртуального устройства. Драйвер устройства управляет работой этих устройств. Примерами физических устройств являются сетевые адаптеры, таймеры, и универсальные асинхронные приемо-передатчики (UART). Примером виртуального устройства является файловая система. Реализация драйвера устройства позволяет предоставить функции этого устройства приложениям и другим частям операционной системы. При разработке драйвера устройств используйте возможности базовых служб предоставляемых ОС. Где только возможно, должна использоваться библиотека функций CEDDK для низкоуровневых операций в драйверах устройств.
Многие драйверы устройств CE реализуют потоковый интерфейс. Точками входа базового потокового интерфейса являются XXX_Open, XXX_Close, XXX_Read, и XXX_Write. Сетевые адаптеры, адаптеры дисплея, устройства мыши, клавиатуры, и другие устройства специального назначения не используют потоковый интерфейс. Эти устройства используют интерфейс, который соответствует функциям устройства. Различные процессы будут загружать различные драйверы устройств. Хотя драйверы устройств CE являются привилегированными модулями, они не должны выполняться в режиме ядра.
Большинство драйверов устройств CE состоят из зависимого от платформы драйвера (PDD) и драйвера устройства модели (MDD). Монолитный драйвер объединяет все PDD и MDD в один драйвер. Многоуровневый драйвер не объединяет их.
MDD имеет следующие характеристики:
- Содержит код, который является общим для всех драйверов данного типа.
- Вызов функций PDD для доступа к оборудованию.
- Соединение со слоем PDD и определение функций (DDSI) интерфейса поставщика службы драйвера устройства, которые MDD собирается вызывать в этом слое.
- Предоставляет функции (DDI) интерфейса драйвера устройства (DDI) операционной системе. Другие части ОС могут вызывать эти функции. Связанные устройства могут совместно использовать один DDI. Монолитные драйверы также предоставляют функции DDI.
- Управление обработкой прерываний.
- Предоставляет разработчикам возможность повторного использования.
- Может соединяться с несколькими PDD.
- Обычно не требует изменений. Если изменяется, могут возникать проблемы при миграции драйверов на будущие версии.
- Содержит потоки обработки прерываний (IST).
PDD имеют следующие характеристики:
- Состоит из специфического для платформы оборудования кода.
- Может требовать модификации для используемой платформы оборудования.
- Спроектированы для работы со специальными реализациями MDD.
- Предоставляет функции DDSI, которые вызывает MDD. Монолитные драйверы не предоставляют функции DDSI.
Следующий список содержит вопросы для рассмотрения при выборе между реализацией многоуровневого драйвера или монолитного драйвера:
- Многоуровневый драйвер может требовать модификации только в PDD. (т.е., устройство с несколькими одинаковыми портами COM)
- Многоуровневый драйвер добавляет накладные расходы к вызовам функций в драйвере устройства, так как MDD обращается к PDD.
- Монолитный драйвер улучшает производительность, так как он объединяет MDD и PDD в одном слое, что исключает вызовы функций MDD в PDD.
- Монолитный драйвер сложнее для миграции на будущие версии CE, так как большинство драйверов устройств, которые содержит CE, делятся на PDD и MDD.
- Монолитный драйвер может быть проще и более эффективным, если возможности устройства хорошо соответствуют задачам, которые выполняют функции в слое MDD.
В CE 6.0 предоставляются образцы исходного кода для ряда драйверов и предоставляется широкий ассортимент обычно используемых драйверов устройств. Более подробная информация о разработке драйверов устройств будет представлена в "Введение в драйверы устройств ввода/вывода" .
Менеджер устройств загружает все драйверы в пространство ядра как драйверы режима ядра, если только в реестре не задан флаг DEVFLAGS_LOAD_AS_USERPROC. Драйверы режима ядра предоставляют лучшую производительность, так как они могут вызывать API ядра непосредственно используя версию ядра coredll, называемую k.coredll.dll. Драйверы ядра могут синхронно обращаться к буферам пользователя очень быстро, так как память пользователя доступно непосредственно. Драйверы режима ядра должны быть надежными, так как они имеют неограниченный доступ к памяти. Ошибка в драйвере режима ядра может испортить память ядра, вызывая отказ системы.