RTX - real-time extension для Windows NT от компании VenturCom
Одним из возможных решений является использование совместно с Windows NT подсистемы реального времени, исполняющейся на том же процессоре (если процессор один) или на выделенном процессоре(-ах) (если их несколько). Этот подход использован фирмой VenturCom в продукте RTX (Real Time Extension). Сущность подхода заключается в использовании модифицированного HAL (Hardware Abstraction Level). Изменять kernel Microsoft не разрешает, а исходный код HAL предоставляет своим партнерам, одним из которых является VenturCom.
После установки RTX стандартная NT Workstation или Server превращается в операционную систему реального времени с жестким детерминизмом (hard real-time). Сама NT об этом, правда, не подозревает. Ни ядро, ни исполняющая подсистема NT не были изменены. Подсистема реального времени видна из Windows NT, как еще один драйвер устройства. Схема функционирования RTX в составе Windows NT представлена на рисунке.
В системе появляется планировщик задач реального времени. Новый HAL по прерыванию от часов реального времени передает управление этому планировщику, который следит за очередью задач реального времени, находящихся в системе и выделяет им время в соответствии с приоритетом. В RTX существует 128 фиксированных приоритетов для задач реального времени. Если в очереди таких задач нет, то управление передается стандартному планировщику NT. При этом времена исполнения кодов модифицированного и оригинального HAL совпадают, т.е. присутствие RTX не сказывается на производительности Windows в отсутствие real-time процессов. Однако, если real-time процессы будут занимать все процессорное время, то Windows NT не получит управления и окажется подвешенной. В принципе, если данная ситуация не входит в планы разработчика, ее можно предупредить, установив в опциях настройки RTX "сторожевой" таймер, срабатывающий, если один поток занимает CPU больше определенного времени.
Все программы, исполнявшиеся на машине до установки RTX, будут по-прежнему на ней исполняться, но, к сожалению, не в real-time режиме. Такие программы нужно писать и компилировать самому с помощью поставляемых VenturCom библиотек, содержащих функции Real-Time API (RTAPI).
Создание приложений реального времени с помощью RTAPI
Одним из преимуществ RTX, является возможность использования стандартных компиляторов для создания real-time программ и стандартного софта (commercial off the shelf, COTS), взаимодействующего с ними и работающего на той же машине. Действительно, для создания real-time программ Вы можете использовать Visual C++ 4.2 или 5.0. Кроме того, при создании Win32-части приложения можно использовать любой компилятор, понимающий вызовы функций Win32 API (например C++ Builder). Один и тот же код можно откомпилировать как Win32 и как RTSS приложение с расширениями .exe и .rtss соответственно. Exe-вариант программы является приложением "мягкого" реального времени.
Функции, входящие в RTAPI на данный момент, можно разделить на 4 группы. К первой относятся функции, чья семантика и/или поведение отличается от функций Win32 API. В названии всех функций этой группы присутствует префикс Rt (например RtAttachInterruptVector) Во вторую входят функции, чья семантика и функциональность совпадают с функциями из Win32 API, но они работают в RTSS подсистеме. В третью группу входят функции runtime C библиотеки. К последней (но не по важности) группе относятся функции т.н. Windows NT Driver IPC API, позволяющие rtss и Win32 потокам взаимодействовать с обычными драйверами, работающими в режиме ядра.
Программы, скомпилированные как rtss, могут содержать только функции, входящие в RTAPI, На программы,скомпилированные как exe, такое ограничение не распространяется.
Разработку любого RTSS приложения удобно начинать с exe-варианта. Это позволяет использовать отладочные средства Visual Studio (или другого используемого компилятора). Например, если Вы пишете rtss-драйвер, обслуживающий прерывания, то скомпилировав его как Win32 программу, можно установить точку останова в процедуре обработки прерывания (в RTX обработка прерываний не разделяется на ISR и DPC, а выполняется одним потоком (см. ниже)) и сделать пошаговый прогон всей процедуры. Программы, скомпилированные как rtss, при необходимости отлаживаются с помощью WinDBG (на двух компьютерах) или SoftIce (на одном компьютере).
Обычно (хотя и не обязательно) приложение состоит минимум из двух частей - real-time процесса, исполняющегося в RTSS подсистеме и исполняющего критическую по времени работу и exe-процесса, исполняющегося в Win32, взаимодействующего с rtss-частью с помощью средств межпроцессного взаимодействия. Exe-часть может взаимодействовать с сервисами NT, другими программами, пользователем.
| |
Возможность использования Windows NT в качестве ОС реального времени
В последнее время приобретают популярность расширения реального времени для Windows NT. Это обусловлено, с одной стороны, расширением областей применения компьютерного управления, с другой стороны - сравнительно малой известностью и высокой стоимостью специализированных операционных систем реального времени. Интерфейс Win32 является стандартным и хорошо знакомым большому числу программистов. Под NT существует огромное число готовых приложений (в том числе коммуникационных), а также популярные средства разработки. К сожалению, Windows NT "в чистом виде" нельзя отнести к операционным системам реального времени. Обсуждению причин этого посвящены статьи Martin Timmerman и Jean-Christophe Monfret в Real-Time Magazine Q21997 []. Вот некоторые из них: недостаточное количество real-time приоритетов, отсутствие наследования приоритетов, как средства борьбы с инверсией приоритетов, неподходящая для RTOS система обработки прерываний. В Windows NT доступ к прерываниям осуществляется из драйвера ядра, а сами прерывания обрабатываются в два этапа: сначала вызывается очень короткая Interrupt Service Routine (ISR), осуществляющая критическую обработку, основная обработка прерывания происходит в Deferred Procedure Call (DPC). Все DPC выполняются с одинаковым уровнем приоритета в порядке поступления (FIFO).
Таким образом, время окончания обработки вашей DPC оказывается зависимым непредсказуемым образом от наличия в системе других драйверов и их активности. Для систем с жестким детерминизмом необходимо точно знать максимальное время от момента возникновения прерывания до входа в процедуру обработки и гарантировать его непревышение.
Блокировка системной памяти
Для предотвращения ошибок страниц и, следовательно, непредсказуемых задержек в критическом по времени участке кода, real-time приложения должны блокировать данные и код в памяти, включая код и данные самой операционной системы.
API блокирования процессов
Следующие функции служат для блокировки памяти RTX процесса:
RtLockProcess –
блокирует все pageable секции процесса в физической памяти.
RtUnlockProcess – разблокирует секции виртуального адресного пространства процесса, блокированные ранее в физической памяти.
RtCommitLockProcessHeap – блокирует кучу процесса.
RtCommitLockHeap – блокирует указанную при вызове кучу.
RtCommitLockStack - блокирует указанный при вызове стек.
Замечания по программированию в RTSS подсистеме
По умолчанию, все процессы и объекты в RTSS подсистеме блокированы в физической памяти. Следовательно, выполнение RTSS процесса никогда не повлечет ошибку страницы. Вызов функций Rt*Lock (Process, Heap, Stack) в RTSS подсистеме всегда завершается успешно, но не влечет за собой никаких реальных действий.
Замечания по программированию в Win32 подсистеме
Любые процессы и службы Windows NT могут сбрасывать страницы на диск, если только явным образом не блокированы в физической памяти. Для того, чтобы real-time процесс не осуществлял такой сброс страниц, надо использовать функцию RtLockProcess.
В данном
демонстрируется использование функций блокирования процесса.
API блокирования ядра
Следующие функции служат для доступа к RTX-службам блокирования ядра:
RtLockKernel –блокирует pageable участки ядра в физической памяти.
RtUnlockKernel – разблокирует предварительно блокированные участки ядра Windows NT.
Замечания по программированию в RTSS подсистеме
По умолчанию, все процессы и объекты в RTSS подсистеме блокированы в физической памяти. Блокирование ядра Windows NT не повлияет на производительность RTSS процесса.
Замечания по программированию в Win32 подсистеме
Определенные компоненты Windows NT подвержены пэйджингу (pageable), включая большую часть ядра и подсистемы Win32. Для предотвращения задержек в работе real-time процесса, связанных с ошибками страниц, надо использовать функцию RtLockKernel. Драйверы устройств в Windows NT обычно не осуществляют пэйджинг. Драйверы устройств, загружаемые во время загрузки системы, никогда не делают пэйджинг. Для того, чтобы система могла сбрасывать страницы драйвера, разработчик такого драйвера должен тщательно продумать его структуру и явным образом указать участки кода, которые можно сбрасывать. Из-за своей сложности такие драйверы встречаются редко. Блокирование ядра Windows NT и процессов уменьшает пул доступной физической памяти. Это может кардинальным образом сказаться на производительности не real-time части системы. При необходимости для обеспечения желаемой производительности не real-time операций можно просто добавить физической памяти.
В данном
демонстрируется использование функций блокирования ядра..
Часы
Часы в RTX отсчитывают прохождение определенных интервалов времени.
API
Следующие функции служат для доступа к RTX-службам часов:
RtGetClockTime – возвращает текущее значение указанных часов.
RtSetClockTime – устанавливает значение указанных часов.
RtGetClockResolution -возвращает разрешение указанных часов.
RtGetClockTimerPeriod – возвращает минимальный период таймера для указанных часов.
Значения часов возвращаются и устанавливаются в единицах 100 наносекунд и соответствуют числу прошедших единиц с 12:00 1 Января 1600.
Типы часов
Часы RTX в Win32 и RTSS подсистемах могут быть:
CLOCK_1 (или CLOCK_SYSTEM) CLOCK_2 (или CLOCK_FASTEST)
Часы CLOCK_1 обеспечиваются real-time HAL и имеют разрешение 1 миллисекунду. Потоки в Win32 и RTSS подсистемах могут, основываясь на этих часах, использовать таймеры с инкрементом в 1 миллисекунду.
Часы CLOCK_2 также обеспечиваются real-time HAL и имеют разрешение 1 микросекунду. Период таймеров, основывающихся на этих часах, переменный и может составлять 100, 200, 500 и 1000 микросекунд.
Период таймера HAL можно установить либо в контрольной панели “RTX settings”, либо в ключе реестра HKLM\System\CurrentControlSet\Control\RTX\HalTimerPeriod (и перезагрузиться).
Имена объектов
Именованные объекты позволяют разным процессам иметь доступ к общим объектам. Максимальная длина имени, указанного при создании процесса, равна RTX_MAX_PATH символов и может включать любые символы за исключением обратной косой черты (\). Если процесс создал именованное событие, мьютекс, семафор или разделяемую память, другие объекты могут использовать это имя для вызова соответствующей функции (RtOpenEvent, RtOpenMutex, RtOpenSemaphore или RtOpenSharedMemory) для получения хэндла объекта. Имена объектов различают регистр символов.
Имена событий, мьютексов, семафоров и разделяемой памяти разделяют общее пространство имен. Если при создании объекта задать имя, которое уже использовано объектом другого типа, функция успешно выполнится, но GetLastError вернет ERROR_ALREADY_EXISTS. Следовательно, при создании именованных объектов надо использовать уникальные имена и проверять возвращаемые функцией значения на предмет ошибок, связанных с повторным именованием.
Например, если имя, указанное в вызове RtCreateMutex, совпадает с именем существующего объекта mutex, функция вернет хэндл существующего объекта. В этом случае вызов RtCreateMutex эквивалентен вызову RtOpenMutex. Ситуация, когда несколько процессов используют RtCreateMutex с одним и тем же именем именем эквивалентна ситуации, когда один процесс вызывает RtCreateMutex, а остальные RtOpenMutex, за исключением того, что в последнем случае нет необходимости следить за тем, что создающий mutex процесс стартовал первым. При использовании такого способа с объектами mutex ни один из вызывающих процессов не должен требовать немедленного владения этим объектом. В противном случае трудно предсказать, какой из процессов действительно будет владеть объектом.
Использование арифметики с плавающей точкой
Потоки, исполняющиеся в RTSS подсистеме, всегда исполняются в режиме ядра. В то время, как ядро Windows NT не поддерживает операции с плавающей точкой, исполняющиеся в режиме ядра (такой код приводит к краху системы), RTX позволяет RTSS-потокам использовать FPU для операций с плавающей точкой.
Разрешение поддержки плавающей точки в RTSS программах
Если Вы строите RTSS-программу с использованием NMAKE, Вы должны добавить следующую строчку к make-файлу перед строкой включения rtx.mak:
RTSS_CRT = 1
Эта строка разрешает использование FPU, включая математические функции, использующие плавающую точку и printf с поддержкой плавающей точки.
Выполнение RTSS-программ, использующих плавающую точку
Не нужно никаких специальных процедур для выполнения RTSS-программ, использующих плавающую точку. Программа может просто выдавать команды FPU и вызывать математические функции, использующие плавающую точку так же, как обычная Win32 программа.
Использование часов и таймеров
В системах реального времени активно используются различные службы времени. Операционная система должна быть способна отслеживать текущее время, в точно определенное время запускать потоки на исполнение и приостанавливать потоки на точно определенный интервал.
Следующие части описывают:
Часы Таймеры Приостановку процессов (sleep)
Использование мьютексов
RTSS мьютекс – это объект синхронизации, чье состояние является свободным (signalled), когда им не владеет ни один поток, в противном случае он считается занятым (mutex signalled). Мьютекс используется для поочередного доступа к разделяемым ресурсам.
Владение мьютексом
Поток владеет мьютексом от момента выхода из wait функции до вызова RtReleaseMutex. Ни один другой поток не может владеть мьютексом в этом промежутке. Если другой поток вызывает wait функцию в то время, когда мьютекс занят (not signalled), wait функция не вернет управление до тех пор, пока владелец мьютекса его не освободит. Когда владеющий поток заканчивает исполнение, мьютекс переходит в свободное состояние и теряет владельца. Ожидающий поток узнает об “осиротевшем” мьютексе по результату, возвращаемому wait функцией.
Если более одного потока ждут мьютекс, то владение мьютексом получит поток с максимальным приоритетом и он же первым получит управление от wait функции.
Если мьютекс ждут процессы с одинаковым приоритетом, управление получит поток, запросивший владение раньше других.
Межпроцессное взаимодействие с использованием мьютексов
Для синхронизации потоков, исполняющихся в разных процессах, включая Win32 и RTSS процессы, поток в каждом процессе должен иметь свой, уникальный для процесса хэндл RTSS- мьютекса. Эти хэндлы могут быть получены с помощью вызовов функций RtCreateMutex и RtOpenMutex..
Использование RtCreateMutex
Наиболее простой способ заключается в том, чтобы один поток в каждом процессе вызвал RtCreateMutex,, передавая идентичную строку-имя мьютекса в качестве параметра. Первый поток, вызвавший RtCreateMutex, заставит систему создать RTSS мьютекс. Когда остальные потоки будут вызывать RtCreateMutex, система определит, что мьютекс с указанным именем уже существует; в результате новый мьютекс не создастся, а функция возвратит уникальный для процесса хэндл, идентифицирующий существующий мьютекс.
Поток может определить, создал ли вызов RtCreateMutex новый мьютекс с помощью вызова функции GetLastError сразу после вызова RtCreateMutex. Если GetLastError вернет ERROR_ALREADY_EXISTS, то новый мьютекс не был создан. Если планируется разделять RTSS мьютекс c другими процессами, последний шаг можно игнорировать.
Использование RtOpenMutex
RtOpenMutex возвращает хэндл RTSS мьютекса. Параметр dwDesiredAccess может принимать значения SEMAPHORE_ALL_ACCESS и SYNCHRONIZE. Параметр lpName является оканчивающейся 0 строкой с именем мьютекса. При вызове RtOpenSemaphore система сканирует все существующие RTSS мьютексы на предмет совпадения с именем, указанным в lpName. Если таковой обнаружится, система создаст уникальный для процесса хэндл этого мьютекса и вернет его вызвавшему потоку. Любой поток в вызывавшем процессе может теперь использовать этот хэндл в любой функции, принимающей в качестве параметра хэндл RTSS мьютекса. Если мьютекс с искомым именем не обнаружится, возвращается NULL.
Хотя объект RTSS мьютекс всегда поддерживается RTSS подсистемой, Win32 программы могут создавать, открывать и ждать RTSS мьютексы. Это позволяет осуществлять взаимодействие RTSS и Win32 процессов. Пространство имен мьютексов отлично от пространства имен Win32 мьютексов.
| |
Использование потоков
Функция CreateThread создает либо RTSS, либо Win32 поток, в зависимости от текущей исполняющей подсистемы процесса. Возвращаемый хэндл и ID потока действителен только в окружении, вызвавшем CreateThread. Например, Win32 процесс не может управлять приоритетом RTSS потока, потому что хэндл этого потока действителен только в RTSS подсистеме. Вы можете, однако, использовать средства межпроцессного взаимодействия (Inter-Process Communication,IPC), предоставляемые RTX – такие как mutex, semaphore, events и shared memory – для синхронизации и коммуникации между Win32 и RTSS процессами и потоками. См. раздел “Межпроцессное взаимодействие”.
Объекты таймеры и прерывания
Объекты-таймеры и прерывания происходят от потоков, следовательно, хэндлы этих объектов действительны только в их собственной (Win32 или RTSS) среде. Аналогично, этими объектами можно управлять только из процессов в их собственной среде.
RTSS подсистема
Единицей исполнения в подсистеме RTSS является поток. Готовый к исполнению RTSS-поток получает приоритет над всеми Win32 потоками. RTSS-поток исполняется до тех пор, пока он не освободит CPU. Поток освобождает CPU, когда он:
Ожидает объекта синхронизации Понижает собственный приоритет или повышает приоритет другого потока Подвешивает (suspend) себя Возвращается из процедуры обработки таймера или прерывания (относится только к потокам таймера и прерывания) Вызывает функцию Sleep c аргументом 0
RTSS-потоки не могут быть использованы как объекты синхронизации. Первичный поток процесса имеет стек размером 8 KB. Можно указать размер стека созданных впоследствии потоков с помощью функции CreateThread.
Использование процессов
Ниже кратко описывается работа процессов в RTSS и Win32 среде.
Процесс, исполняющийся в RTSS, состоит из набора хэндлов объектов, адресного пространства процесса, по меньшей мере одного потока и исполняемого файла. При создании процесса RTSS выполняет следующие действия:
Загружает исполняемый файл, как драйвер Выделяет процессу heap из non-paged kernel page pool Создает основной поток
Процесс может быть запущен одним из следующих способов:
Загрузка в качестве драйвера устройства во время загрузки системы (используя утилиту RTSSrun) Выполнение RTSS-программы из Win32
Процесс прекращает работы при одном из следующих условий:
Последний поток закончил работу Один из потоков вызвал ExitProcess Процесс убит утилитой RTSSkill
Максимальное число процессов, одновременно существующих в RTSS, равняется числу слотов RTSS-процессов, заданному в регистре (по умолчанию 10). RTSS-процесс не может использоваться в качестве объекта синхронизации.
Процессы, выполняющиеся подсистеме Win32, начинают взаимодействовать с RTX, когда они вызывают функции RTAPI. Такой вызов связывает процесс с rtapi_w32.dll. После этого RTX может выделять ресурсы для этого процесса, изменять его приоритет и т.д. Число Win32 процессов, которое может взаимодействовать с RTX, непостоянно – оно зависит от конфигурации системы и свободных ресурсов.
Использование процессов и потоков
Понятие процесса включает в себя адресное пространство, хэндлы объектов и один или несколько исполняемых потоков. Потоки используются, например, в качестве обработчиков прерываний и для обработки асинхронных, связанных с процессом событий, в контексте потока. Функционирование потоков и процессов в RTSS во многом аналогично Win32.
Использование разделяемой памяти
RTSS-объект разделяемая память представляет собой область непэйджируемой (non-paged) физической памяти, которая может быть спроецирована на виртуальное адресное пространство процесса. Когда известно имя разделяемой памяти, другой процесс может спроецировать ее на свое адресное пространство. Для доступа к объекту разделяемая память надо знать его хэндл и виртуальный адрес. Для того, чтобы отсоединить процесс от разделяемой памяти, процесс должен закрыть хэндл объекта. Когда все процессы отсоединятся от объекта разделяемая память, произойдет следующее:
Участок памяти вернется в область непэйджируемой (non-paged) физической памяти Объект прекратит существование
Обмен данными между процессами посредством Shared Memory
RTSS-объекты разделяемая память позволяет разделять блоки данных между несколькими процессами, включая RTSS и Win32 процессы. Для этого поток в каждом процессе должен иметь собственный уникальный для процесса хэндл RTSS-объекта разделяемая память и собственный уникальный для процесса указатель на адрес проекции разделяемой памяти на адресное пространство процесса. Эти хэндлы и адреса могут быть получены путем вызова функций RtCreateSharedMemory и RtOpenSharedMemory.
Использование RtCreateSharedMemory
Для того, чтобы несколько процессов использовали объект разделяемая память, он сначала должен быть создан вызовом RtCreateSharedMemory. Другие процессы могут использовать разделяемую память, если им известно ее имя. RtCreateSharedMemory возвращает хэндл и адрес разделяемой памяти.
Вызов RtCreateSharedMemory заканчивается неудачей, если затребовано памяти больше, чем может быть выделено или если объект разделяемая память с таким именем уже существует. Тогда память не проецируется и хэндл не возвращается. RtCreateSharedMemory не преобразуется автоматически в RtOpenSharedMemory, если разделяемая память с таким именем уже существует. Только один процесс может успешно создать объект разделяемая память.
Использование RtOpenSharedMemory
RtOpenSharedMemory проецирует уже созданный объект разделяемая память на адресное пространство вызывающего процесса. Вызов RtOpenSharedMemory заканчивается неудачей, если такого именованного объекта разделяемая память не существует.
RtOpenSharedMemory возвращает хэндл разделяемой памяти и ее базовый адрес. Когда процесс заканчивает работу с объектом разделяемая память, он должен закрыть его хэндл.
Объект разделяемая память всегда принадлежит RTSS подсистеме. Однако, Win32 процесс может создавать и открывать RTSS-объекты разделяемая память.
При создании объекта разделяемая память RTSS обеспечивает его блокированной памятью соответствующего размера.
Использование семафоров
RTSS-объект семафор является объектом синхронизации, который поддерживает счетчик, значение которого может находиться между нулем и определенным максимальным числом. Счетчик уменьшается на 1 каждый раз, когда функция ожидания объекта семафор (WaitForSingleObject) возвращает управление потоку. Счетчик увеличивается на переменное число при освобождении семафора. Когда счетчик достигает 0, семафор больше не считается свободным и ни один вызов WaitForSingleObject не может вернуть управление потоку, пока какой-либо поток не увеличит счетчик.
Как и обычные Win32 семафоры, RTSS-семафоры используются для учета ресурсов. Они дают возможность потоку запросить свободные ресурсы. Если один или более ресурсов доступны, счетчик свободных ресурсов уменьшается. RTSS-семафоры выполняют эту операцию автоматически, т.е. когда запрашивается ресурс у RTSS-семафора, операционная система проверяет наличие свободных ресурсов и уменьшает их счетчик не позволяя вмешиваться другим потокам. Только после того, как счетчик ресурсов был уменьшен, система позволяет другому потоку затребовать ресурс.
Т.к. несколько потоков могут влиять на счетчик, RTSS-семафором, в отличие от мьютекса, поток владеть не может. Это означает, что WaitForSingleObject, ожидающая семафор:
Никогда не вернет WAIT_ABANDONED Может потерять ресурсы семафора, если потоки прекращают выполнение Может позволить произойти инверсии приоритетов
Однако, в отличие от Win32 семафоров, RTSS-семафоры поддерживают “приоритетное ожидание”. Это означает, что когда несколько потоков с различными приоритетами ожидают семафор, система гарантирует, что порядок, в котором потоки будут получать доступ к семафору, соответствует распределению приоритетов процессов в момент освобождения семафора.
Использование событий
Событие является объектом синхронизации, чье состояние может быть явным образом установлено посредством вызовов RtSetEvent или RtPulseEvent. Существует два типа событий:
Событие со сбросом вручную (manual-reset) – событие, чье состояние остается свободным (signalled) до тех пор, пока не переведется в занятое (nonsignalled) состояние вызовом RtResetEvent. В свободном состоянии любое количество ждущих потоков или потоков, вызывающих WaitForSingleObject, могут продолжать исполнение.
Событие с автосбросом (auto-reset) – объект, чье состояние остается свободным до тех пор, пока единственный ждущий поток не продолжит выполнение. В этот момент система автоматически переводит объект в занятое состояние. Если нет ожидающих потоков, объект остается свободным.
События полезны при посылке сигналов потокам, уведомляя их о наступлении определенного события.
Использование RtCreateEvent
Поток использует RtCreateEvent для создание объекта событие. Создающий объект поток определяет начальное состояние события и его тип (с автосбросом или сбросом вручную). Этот поток может также задавать имя события (а может и не задавать). Потоки других процессов могут открывать хэндлы существующих событий указывая их имя в вызове RtOpenEvent.
Использование RtPulseEvent
Поток может использовать RtPulseEvent для того, чтобы установить объект событие в свободное состояние и затем снова в занятое после освобождения соответствующего количества ждущих потоков. Для событий с ручным сбросом высвобождаются все ждущие потоки. Для событий с автосбросом высвобождается только один ждущий поток, даже если ожидает несколько. Если ожидающие потоки отсутствуют, RtPulseEvent устанавливает событие в занятое состояние и возвращает управление.
Межпроцессное взаимодействие
В следующих разделах обсуждаются:
Имена объектов Использование разделяемой памяти Использование семафоров Использование событий Использование мьютексов
Межпроцессное взаимодействие с использованием семафоров
Для синхронизации потоков, исполняющихся в разных процессах, включая Win32 и RTSS процессы, поток в каждом процессе должен иметь свой, уникальный для процесса хэндл RTSS-семафора. Эти хэндлы могут быть получены с помощью вызовов функций RtCreateSemaphore и RtOpenSemaphore.
Использование RtCreateSemaphore
Наиболее простой способ заключается в том, чтобы один поток в каждом процессе вызвал RtCreateSemaphore, передавая идентичную строку-имя семафора в качестве параметра. Первый поток, вызвавший RtCreateSemaphore, заставит систему создать RTSS-объект семафор. Когда остальные потоки будут вызывать RtCreateSemaphore, система определит, что семафор с указанным именем уже существует; в результате новый семафор не создастся, а функция возвратит уникальный для процесса хэндл, идентифицирующий существующий семафор.
Поток может определить, создал ли вызов RtCreateSemaphore новый семафор с помощью вызова функции GetLastError сразу после вызова RtCreateSemaphore. Если GetLastError вернет ERROR_ALREADY_EXISTS,, то новый семафор не был создан.
Использование RtOpenSemaphore
RtOpenSemaphore возвращает хэндл RTSS семафора. Параметр dwDesiredAccess может принимать значения SEMAPHORE_ALL_ACCESS и SYNCHRONIZE. Параметр lpName является оканчивающейся 0 строкой с именем семафора. При вызове RtOpenSemaphore система сканирует все существующие RTSS семафоры на предмет совпадения с именем, указанным в lpName. Если таковой обнаружится, система создаст уникальный для процесса хэндл этого семафора и вернет его вызвавшему потоку. Любой поток в вызывавшем процессе может теперь использовать этот хэндл в любой функции, принимающей в качестве параметра хэндл RTSS семафора. Если семафор с искомым именем не обнаружится, возвращается NULL.
Хотя объект RTSS семафор всегда поддерживается RTSS подсистемой, Win32 программы могут создавать, открывать и ждать RTSS семафоры. Это позволяет осуществлять взаимодействие RTSS и Win32 процессов. Пространство имен семафоров отлично от пространства имен Win32 семафоров.
Порты ввода/вывода
Системы реального времени должны иметь возможность читать и писать данные в устройства. RTX-интерфейсы портов ввода/вывода позволяют осуществлять это без необходимости переключения в режим ядра. Отпадает необходимость написания драйвера для каждого устройства, к которому необходимо получить доступ. Кроме того, устраняются задержки, связанные с запросом обслуживания драйвером при каждом обращении к устройству.
Порт ввода/вывода предоставляет альтернативный метод прямого общения с аппаратурой. В адресном пространстве ввода/вывода у процессоров Intel каждый адрес представляет собой 8-ми битный “порт”, который обычно соответствует 8-ми битному управляющему регистру устройства. Хотя последовательные адреса могут представлять байты в многобайтовом порту, разработчики обычно используют подход, при котором существует однобайтовый порт, а многобайтовые величины обычно вводятся, как последовательные однобайтовые записи в порт.
Перед любым вводом/выводом из портов необходимо разрешить доступ к портам. Это осуществляется посредством функции RtEnablePortIo, параметром которой является диапазон портов ввода/вывода, к которым надо получить доступ. После этого для передачи данных можно использовать функции RtWrite* и RtRead*.
API управления портами ввода/вывода
Следующие функции служат для управления портами ввода/вывода:
RtEnablePortIo - разрешает прямой доступ к портам ввода/вывода для указанного диапазона адресов
RtDisablePortIo - запрещает прямой доступ к портам ввода/вывода для указанного диапазона адресов
API передачи данных
Следующие функции служат для передачи данных из/в портов ввода/вывода:
RtReadPortUchar,RtReadPortUshort,RtReadPortUlong - напрямую читают одно-, двух-, четырехбайтовые данные из указанного порта
RtWritePortUchar,RtWritePortUshort,RtWritePortUlong - напрямую пишут одно-, двух-, четырехбайтовые данные в указанный порт
RtReadPortBufferUchar,RtReadPortBufferUshort,RtReadPortBufferUlong -
копируют одно-, двух-, четырехбайтовые данные из указанного порта ввода/вывода в буфер
RtWritePortBufferUchar,RtWritePortBufferUshort,RtWritePortBufferUlong - копируют одно-, двух-, четырехбайтовые данные из буфера в указанный порт ввода/вывода
Общие замечания по программированию
Интерфейсы RTX написаны на ассемблере и используют вызов _ _stdcall. Это означает, что подпрограмма ответственна за очистку стека. Вы не должны использовать другие другие соглашения о вызове (это может вызвать проблемы с компилятором).
Прерывания
Системы реального времени должны уметь работать с внешними устройствами. Часто желательно, чтобы real-time процесс непосредственно обрабатывал прерывания, исходящие от устройства. Эта возможность, наряду с прямым вводом-выводом и функциями проецирования памяти, позволяет пользовательскому процессу полностью контролировать устройство, исключая, таким образом необходимость написания драйвера, работающего в режиме ядра.
Управление прерываниями
Интерфейсы RTX-функций управления прерываниями позволяют приложению обслужить прерывания от устройств, соединенных с компьютером. Как и в случае процедуры-обработчика срабатывания таймера, Вы можете назначить прерыванию процедуру-обработчик.
Win32 и RTSS процессы могут назначить такую процедуру-обработчик с помощью функции RtAttachInterruptVector. Приоритет, присвоенный потоку-обработчику, определяет приоритет исполнения обработчика прерывания в Win32 и RTSS подсистемах.
Когда обработчик прерывания присоединяется к прерыванию, создается поток, который будет исполняться при возникновении прерывания. Процедура-обработчик прерывания в RTX аналогична Interrupt Service Routine (ISR) в драйвере устройства. Когда возникает прерывание, его источник маскируется, и, если приоритет поток-обработчик прерывания выше, чем у потока, исполняющегося в настоящий момент, начинает выполняться поток-обработчик. После возврата из обработчика источник прерываний демаскируется и поток-обработчик подвешивается (suspended).
API
Следующие функции служат для доступа к RTX-службам прерываний:
RtAttachInterruptVector – назначает поток – обработчик указанному прерыванию
RtReleaseInterruptVector - “отсоединяет” обработчик от прерывания, освобождая таким образом прерывание
RtEnableInterrupts - разрешает обработку всех прерываний, которые присоединены к процессу
RtDisableInterrupts - запрещает обработку всех прерываний, которые присоединены к процессу
Общие замечания по программированию
Поток-обработчик может быть в любой момент вытеснен потоком с большим приоритетом Можно запретить обработку некоторых прерываний, присоединенных к RTX процессу подняв приоритет исполняющегося в настоящий момент потока. Обработчик прерывания, освобождающий объект, может удовлетворить условиям wait-функции потока с более высоким приоритетом. В этом случае возобновится выполнение потока с более высоким приоритетом и завершение обработчика отложится, все это время прерывание будет маскировано Прерывания от таймера никогда не маскируются прерываниями с более высоким приоритетом, поэтому они являются по существу, прерываниями с самым высоким приоритетом. Но эти прерывания маскируются с помощью RtDisableInterrupts.
Замечания по программированию в Win32 подсистеме
Все процессы RTSS подсистемы имеют приоритет над всеми процессами Win32 подсистемы, поэтому любая критичная по времени обработка прерываний должна осуществляться обработчиком, исполняющимся как RTSS, а не Win32 процесс. RTX-потоки прерываний в Win32 подсистеме ставятся в очередь планировщиком Windows NT. Это может привести к недетерминистичным задержкам в исполнении обработчика прерываний.
Использование RtAttachInterruptVector
Для присоединения вектора прерывания к обработчику надо воспользоваться функцией RtAttachInterruptVector. При вызове надо указать параметры шины. Для шин с автоматической конфигурацией, таких как PCI, можно получить информацию о параметрах из регистра Windows NT, в котором находятся присвоенные устройству значения ресурсов шины. Для ISA или PC internal bus параметры необходимо указывать вручную.
Использование RtReleaseInterruptVector
В отличие от выгрузки драйвера, RTX-приложение может завершить работу и оставить работающее устройство, пытающееся передать прерывания потоку-обработчику. Хотя библиотека RTX обычно производит очистку за завершившимся приложением, случается, что завершающееся приложение может завершиться незаметно для библиотеки. Во избежании таких ситуаций надо использовать функцию RtReleaseInterruptVector, которая отсоединяет ранее присоединенное прерывание.
демонстрирует работу с прерываниями и портами ввода/вывода.
Приоритеты потоков
В этом разделе описывается система приоритетов потоков в RTSS и Win32 подсистемах.
RTSS подсистема
RTSS подсистема не имеет понятия классов приоритетов, поэтому потоки всех RTSS-процессов состязаются за процессорное время только на основании приоритетов потоков. Поток в RTSS может иметь один из 128 уровней приоритета. Потоки выполняются в соответствии с приоритетом, а потоки одного приоритета – по принципу FIFO (first in-first out). Потоки не квантуются по времени. Это означает, что исполняющийся поток будет выполняться, до тех пор, пока не освободит CPU или внешнее событие не подготовит поток с более высоким приоритетом. Планировщик RTSS использует протокол продвижения приоритета для предотвращения инверсии приоритетов.
Win32 подсистема
Win32 RTX программа начинает исполняться в real-time классе приоритета. RTX обеспечивает соответствие между приоритетами в Win32 и RTSS. Однако, планировщик Win32 не исключает инверсии приоритетов.
В таблице 1 показано, как имена приоритетов RTSS транслируются в приоритеты Win32 при вызове функции RtSetThreadPriority из Win32 программы.
Таблица 1.
RTSS Symbolic Priority Name | RTSS Value | Windows NT Symbolic Priority Name for Real-Time Priority Class | Win32 Value |
RT_PRIORITY_MIN | 0 | THREAD_PRIORITY_IDLE | 16 |
RT_PRIORITY_MIN + 1 | 1 | THREAD_PRIORITY_LOWEST | 22 |
RT_PRIORITY_MIN + 2 | 2 | THREAD_PRIORITY_BELOW_NORMAL | 23 |
RT_PRIORITY_MIN + 3 | 3 | THREAD_PRIORITY_NORMAL | 24 |
RT_PRIORITY_MIN + 4 | 4 | THREAD_PRIORITY_ABOVE_NORMAL | 25 |
RT_PRIORITY_MIN+ 5 …+ 126 | 5…126 | THREAD_PRIORITY_HIGHEST | 26 |
RT_PRIORITY_MAX | 127 | THREAD_PRIORITY_TIME_CRITICAL | 31 |
Например, вызов RtSetThreadPriority(Thread,RT_PRIORITY_MIN+1) преобразуется в SetThreadPriority(Thread, THREAD_PRIORITY_LOWEST)
Если Win32 программа вызывает RtGetThreadPriority(), возвращается real-time приоритет, указанный в вызове RtSetThreadPriority().
В таблице 2 показано, как преобразуются вызовы Win32 “set” и “get” в RTSS подсистеме.
Таблица 2.
Windows NT Symbolic Priority Name for Real-Time Priority Class | Win32 Value | RTSS Symbolic Priority Name | Value |
THREAD_PRIORITY_IDLE | 16 | RT_PRIORITY_MIN | 0 |
THREAD_PRIORITY_LOWEST | 22 | RT_PRIORITY_MIN + 1 | 1 |
THREAD_PRIORITY_BELOW_NORMAL | 23 | RT_PRIORITY_MIN + 2 | 2 |
THREAD_PRIORITY_NORMAL | 24 | RT_PRIORITY_MIN + 3 | 3 |
THREAD_PRIORITY_ABOVE_NORMAL | 25 | RT_PRIORITY_MIN + 4 | 4 |
THREAD_PRIORITY_HIGHEST | 26 | RT_PRIORITY_MIN + 5 | 5 |
THREAD_PRIORITY_TIME_CRITICAL | 31 | RT_PRIORITY_MAX | 127 |
| |
Приостановка потока (Sleep)
API
Sleep – приостанавливает текущий поток на указанное число миллисекунд
SleepEx - приостанавливает текущий поток на указанное число миллисекунд. Поток может быть возобновлен досрочно для завершения ввода/вывода.
RtSleepFt – приостанавливает текущий поток на заданный в единицах 100 наносекунд интервал времени
RtSleepFtEx – приостанавливает текущий поток на заданный в единицах 100 наносекунд интервал времени. Поток может быть возобновлен досрочно для завершения ввода/вывода.
демонтрирует использование таймеров и приостановки потока.
Проецирование физической памяти
Существование адресного пространства ввода/вывода зависит от архитектуры процессора. Некоторые архитектуры, поддерживаемые Windows NT, основаны на процессорах, не имеющих отдельного пространства ввода/вывода. Их порты проецируются в адреса памяти. Эти архитектуры могут использовать функции проецирования памяти для обеспечения доступа к физической памяти контроллеров и другой аппаратуры.
Проецирование физической памяти может также использоваться для предоставления процессам доступа к физическим участкам памяти в адресном пространстве CPU.
Интерфейс проецирования физической памяти проецирует участок физической памяти на виртуальное адресное пространство процесса. Это позволяет приложению получать доступ к участкам физической памяти непосредственно, как если бы они были буферами в приложении. Эти интерфейсы полезны в программах, которым нужен доступ к памяти устройств или регистрам, которые спроецированы на физическре адресное пространство CPU.
API
Следующие функции служат для проецирования памяти:
RtMapMemory – проецирует участок адресов физической памяти на виртуальное адресное пространство процесса.
RtUnmapMemory - убирает ранее спроецированнный участок адресов физической памяти из виртуального адресного пространства процесса.
Общие замечания по программированию
Не существует ограничений или запретов на спроецированный участок памяти. В случае удачного проецирования обеспечивается указанные базовый адрес и длина области. Не следует проецировать и изменять адресное пространство Windows NT, т.к. это может привести к ошибкам в операционной системе.
Замечания по программированию в Win32 подсистеме
Попытка доступа к области, выходящей за пределы спроецированного адресного пространства, вызовет исключение.
демонстрирует проецирование физической памяти.
Проецирование непрерыного участка памяти
Некоторые устройства, особенно работающие с DMA, требуют, чтобы их буферы находились в физически непрерывном участке памяти в адресном пространстве CPU. Кроме того, эти устройства должны получать доступ к буферам памяти используя настоящий физический адрес, а не виртуальный адрес, используемый Win32 или RTSS процессом.
Для выделения физически непрерывного участка памяти и перевода виртуального адреса в физический используются функции RtAllocateContiguousMemory и RtGetPhysicalAddress соответственно.
API
Следующие функции служат для работы с физически непрерывным участком памяти:
RtAllocateContiguousMemory - выделяет физически непрерывный участок памяти и проецирует эту память в виртуальное адресное пространство процесса
RtFreeContiguousMemory - высвобождает ранее выделенный физически непрерывный участок памяти
RtGetPhysicalAddress - возвращает физический адрес по виртуальному адресу ранее выделенного физически непрерывного участка памяти
Замечания по программированию
Непрерывный участок памяти всегда выделяется из non-paged пула памяти Windows NT. Этот пул памяти сравнительно мал и вскоре после загрузки системы становится фрагментированным в результате выделения памяти драйверам и другим подсистемам Windows NT. Чтобы избежать неудач при попытках выделить большой участок памяти, надо делать это сразу после загрузки и/или вообще минимизировать такие выделения В Win32 подсистеме функция RtGetPhysicalAddress работает только с адресами, полученными в результате вызова RtAllocateContiguousMemory.
Данный
демонстрирует выделение непрерывного участка памяти.
Сторожевой таймер
Ситуация, когда единственный RTSS-поток полностью владеет CPU, чаще всего возникает в результате логической ошибки в программе (что бывает на стадии разработки). Когда единственный RTSS-поток полностью владеет CPU на однопроцессорной системе функционирование Windows NT прекращается. В системе с несколькими процессорами Windows NT продолжает работь, но не может остановить RTSS-поток, владеющий CPU, из-за того, что потоки Service Request Interrupt (SRI) менеджера, имеющие более низкий приоритет, не запустятся.
Работа со сторожевым таймером
Для вышеописанной ситуации можно использовать сторожевой таймер RTX (watchdog timer). При каждом прерывании от таймера (обычно такие прерывания разрешены) этот таймер проверяет, не исполняется ли один и тот же поток (за исключением Windows NT idle thread). При срабатывании сторожевого таймера поток-нарушитель будет остановлен, появится окошко с сообщением о возникшей ошибке, и возобновится нормальная работа Windows NT. Для выгрузки остановленного процесса можно использовать утилиту RTSSkill.
Установка периода сторожевого таймера
Можно устанавливать т.н. Starvation Time Out период (период сторожевого таймера) из контрольной панели RTX или с помощью ключа
HKLM\System\CurrentControlSet\Control\RTX\StarvationTimeOut
в регистре и последующей перезагрузки.
Значения для ключа:
0 – запрет сторожевого таймера
N – (миллисекунд) разрешает сторожевой таймер и устанавливает период (обычно период составляет несколько секунд или десятков секунд).
Замечания по программированию
Программа может вести себя правильно, но все же иметь один или несколько потоков, которые на определенные промежутки времени занимают CPU. В таких случаях, в этих промежутках Вы можете периодически выделять небольшие отрезки времени для работы Windows NT. Это может потребоваться, например, для того, чтобы Windows NT очистила буфер RS232 UART, обслужила сетевые запросы, совершила дисковые операции, переместила указатель мыши или отобразила что-либо в GUI. Т.к. реально требуемое поведение может быть очень сложным, бессмысленно требовать, чтобы RTX непосредственно управлял выделением Windows NT временных отрезков.
Можно легко справиться с этой ситуацией, создав обработчик таймера, который периодически подвешивает владеющие CPU поток(и) на короткий промежуток времени. Рассиотрим, например, приложение, запускающее каждую секунду поток продолжительностью 100 миллисекунд. Можно подвешивать этот поток на 0.5 миллисекунд каждые 10 миллисекунд. Вообще, можно создать сколь угодно сложный или простой алгоритм для предотвращения перегрузки системы. Обычно пять процентов времени на работу системы является разумной начальной величиной. Затем можно измерять перегрузку и изменять алгоритм.
Другой подход заключается в простом включении вызовов Sleep, что должно дать небольшое количество времени Windows NT в процессе работы Вашего приложения.
Таймеры
Таймеры RTX являются скрытыми потоками-обработчиками, которые получают уведомление от RTSS о прохождении определенного интервала времени и вызывают процедуру-обработчик таймера, указанную при его создании.
При создании таймер ассоциируется с определенными часами в системе, по их отсчету срабатывает таймер. Таймер начинает отсчет после того, как был установлен. После того, как таймер сработал и процедура-обработчик возвратила управление, таймер может быть автоматически переустановлен. Таймеры, у которых интервал повторения установлен в 0, являются “одноразовыми” и выполняют процедуру-обработчик один раз. Таймеры, у которых установлен допустимый интервал повторения, будут выполнять процедуру-обработчик с заданным интервалом.
API
Следующие функции служат для доступа к RTX-службам таймеров:
RtCreateTimer –создает таймер, связанный с указанными часами. RtDeleteTimer – уничтожает ранее созданный таймер
RtCancelTimer – прекращает срабатывание указанного таймера
RtSetTimer – устанавливает абсолютное время срабатывания и интервал повтора для указанного таймера
RtSetTimerRelative - устанавливает относительное время срабатывания и интервал повтора для указанного таймера
Отношение к таймерам в Windows NT
Таймеры в RTX не являются объектами синхронизации. Это значит, что потоки не могут использовать Waifor.. функции с хэндлами RTX таймеров. Это отличает их от таймеров Windows NT, являющихся объектами, которые поток может ожидать или получать уведомление.
Программирование в Win32 и RTSS подсистемах
Если приложение должно информировать другие потоки о срабатывании таймера, оно должно использовать соответствующий объект уведомления (notification object) в процедуре-обработчике таймера.
| |
Управление перегрузками (starvation)
В этом разделе рассматривается сторожевой таймер RTX (watchdog timer) и техника программирования, которая позволит избежать ситуации перегрузки.
Ввод/вывод по шине
Функции RtGetBusDataByOffset, RtTranslateBusAddress и RtSetBusDataByOffset облегчают разработку RTSS-драйверов устройств. Каждая из функций упрощает сбор информации и/или установку устройства.
RtGetBusDataByOffset используется преимущественно для поддержки привязывания драйвера к устройству. Она получает информацию об устройстве (такую как номер шины, interrupt level, interrupt vector), которая затем используется при вызове RtAttachInterruptVector. Например, с помощью вызова RtGetBusDataByOffset можно сканировать PCI шину, находить определенное устройство и затем возвращать номер шины, на которой находится устройство, interrupt level и interrupt vector для этой шины.
Установка параметров устройства осуществляется посредством вызова функции RtSetBusDataByOffset. Эта функция полезна при очистке или установке статусных регистров, слотов и т.п. при инициализации устройства.
RtTranslateBusAddress используется при трансляции диапазона адресов устройства в логическое адресное пространство драйвера.
| |
Выделение системной памяти
Часто процессу требуется выделить дополнительную память. Функции выделения памяти, существующие в RTX, всегда выделяют блокированную (locked) память, исключая, таким образом, задержки, связанные с ошибками страниц (page faults).
API выделения памяти
Следующие функции служат для доступа к системе выделения памяти RTX:
RtAllocateLockedMemory – выделяет блокированную память, которой соответствует физическая память, затем проецирует память в виртуальное адресное пространство процесса.
RtFreeLockedMemory – высвобождает предварительно выделенный блокированный участок памяти.
Замечания по выделению памяти
Блокированная память всегда выделяется из non-paged пула памяти Windows NT. Этот пул памяти сравнительно мал и вскоре после загрузки системы становится фрагментированным в результате выделения памяти драйверам и другим подсистемам Windows NT. Чтобы избежать неудач при попытках выделить большой участок памяти, надо делать это сразу после загрузки и/или вообще минимизировать такие выделения.
В данном
демонстрируется использование функций выделения блокированной памяти.
Взаимодействие с драйверами ядра
В этом разделе рассматривается т.н. RTKAPI - Windows NT Driver Inter-Process Communication API. Функции, содержащиеся в нем, предназначены для доступа к механизму межпроцессного взаимодействия RTX из драйверов ядра Windows NT. Они похожи на функции, содержащиеся в RTAPI (например, RtkOpenSemaphore аналогична RtOpenSemaphore). Вы используете вызовы функций RTKAPI также, как функций RTAPI, но из драйверов NT. Все функции RTKAPI имеют префикс Rtk.
API
Ниже будут перечислены функции RTKAPI. Действие практически всех функций понятно из названий, поэтому описание не дается.
RtkCloseHandle RtkCreateEvent RtkCreateMutex RtkCreateSemaphore RtkCreateSharedMemory RtOpenEvent RtOpenMutex RtkOpenSemaphore RtkOpenSharedMemory RtkPulseEvent RtkReleaseMutex RtkReleaseSemaphore RtkResetEvent RtkRtssAttach – “присоединяет” драйвер ядра NT к подсистеме RTSS RtkRtssAttach -“отсоединяет” драйвер ядра NT от RTSS подсистемы RtkSetEvent RtkWaitForSingleObject
| |
в данный обзор либо по
Некоторые темы не попали в данный обзор либо по причине большого объема, либо из-за своего “технического” характера. Так, не рассматривалась обработка исключенй, поддерживаемая в RTX (в частности, при критических ошибках в rtss-потоках, например переполнении стека, RTX, по аналогии с blue screen, генерирует green screen с информацией об ошибке). Не рассматривалась настройка производительности, осуществляемая с помощью поставляемых с RTX утилит. Также не рассматривались: настройка компиляторов, необходимая для создания real-time программ, написание DLL, функционирование обработчиков blue screen и shutdown.
Подробную информацию на русском языке о RTX и CI можно найти на WWW сервере компании "PLC Systems" -
| |
Allocate.shtml
#include <windows.h>
#include <stdio.h>
#include <rtapi.h>
static PVOID vAddress; // virtual memory address returned by RtAllocateLockedMemory void main(void) { // Allocate memory vAddress = RtAllocateLockedMemory(size); if (!vAddress) { printf("\nFailure on RtAllocateLockedMemory\t"); printf("Error=%d\n", GetLastError()); break; // if this fails - no use to continue } else { printf("\nSuccess on RtAllocateLockedMemory\n"); printf("Virtual memory address = 0x%08X\n", vAddress); // Write to the memory *vAddress= 0; } // Free memory if (!RtFreeLockedMemory(vAddress)) { printf("\nFailure on RtFreeLockedMemory(0x%08X).\t", vAddress); printf("Error Code = %d\n", GetLastError()); } else { printf("Success on RtFreeLockedMemory(0x%X).\n", vAddress); } ExitProcess(0); }
Interrup.shtml
// Generate an interrupt // with the digital outs // Jumper any digital out [3-10] to IR INPUT [1] // Jumper GND [11] to IR ENABLE [2] #include <windows.h>
#include <rtapi.h>
#define CT_PORT ((unsigned char *) 0x340) #define CT_PORT_9513DATA (CT_PORT + 0) #define CT_PORT_9513CMD (CT_PORT + 1) #define CT_PORT_9513STATUS (CT_PORT + 1) #define CT_PORT_DI (CT_PORT + 2) #define CT_PORT_DO (CT_PORT + 3) #define CT_PORT_SPACE (CT_PORT_DO - CT_PORT_9513DATA + 1) #define CT_BASE_PORT_9513DATA(BASE) ((unsigned char *)((BASE) 0)) #define CT_BASE_PORT_9513CMD(BASE) ((unsigned char *)((BASE) + 1)) #define CT_BASE_PORT_9513STATUS(BASE) ((unsigned char *)((BASE) + 1)) #define CT_BASE_PORT_DI(BASE) ((unsigned char *)((BASE) + 2)) #define CT_BASE_PORT_DO(BASE) ((unsigned char *)((BASE) + 3)) int ihc = 0 ; int ihc2 = 0 ; int limit = 30 ;
void RTFCNDCL InterruptHandler(void * unused) { RtWritePortUchar(CT_BASE_PORT_DO(CT_PORT), 0x00) ; ihc + +; } TestIncrease = 1 ;
main() { HANDLE hInterrupt ; int i ; printf("Simple Interrupt Attach Handler Functional Test") ; // Enable CT range of ports { if ( !RtEnablePortIo(CT_PORT, CT_PORT_SPACE)) { (void)printf("RtEnablePortIo(0x%x, 0x%x) failed\n", CT_PORT, CT_PORT_SPACE) ; return 1 ; } // Attach the interupt vector to the interrupt handler hInterrupt = RtAttachInterruptVector(NULL, 0, InterruptHandler, (void *)&TestIncrease, 1, 1, 0, 7, 7) ; if ( NULL == hInterrupt ) { printf("Could not register interrupt handler (%d)\n", GetLastError()) ; return 2 ; } for(i=0; i<limit; i++) { Sleep(1*1000) ; //Pulse the interrupt RtWritePortUchar(CT_BASE_PORT_DO(CT_PORT), 0xff) ; } // Determine what happened. if ( limit != ihc ) { printf("FAIL\n") ; else { printf("Got %d interrupts\n", limit) ; } RtReleaseInterruptVector(hInterrupt) ; RtWritePortUchar(CT_BASE_PORT_DO(CT_PORT), 0) ; RtWritePortUchar(CT_BASE_PORT_DO(CT_PORT), 0) ; }
Литература
Использованная:
Windows NT as Real-Time OS ? Real-Time Magazine, 97Q2, Dr. ir. Martin Timmerman, Managing Director, Jean-Christophe Monfret, Project Manager, Real-Time Consult. Windows NT Real-Time Extensions : an Overview, Real-Time Magazine, 97Q2, Dr. ir. Martin Timmerman, Managing Director, Jean-Christophe Monfret, Project Manager, Real-Time Consult. RTX 4.2 for Windows NT User’s Guide
Рекомендуемая:
Рихтер Дж. Windows для профессионалов. Третье издание. Microsoft Press /Русская редакция. 1997 Custer H. Inside Windows NT. Second Edition. Microsoft Press. 1998. А. Калядин Windows NT для встраиваемых приложений. Открытые системы. N 2(28) 1998. Стр.15-18. А. Жданов Продолжая разговор о расширениях реального времени для Windows NT. Мир компьютерной автоматизации. N 2 1998. стр. 83-87 А. Рыбаков, А. Жданов Windows NT во встраиваемых, промышленных и коммуникационных приложениях. PCWEEK, Russian Edition N 27-28(151-152) 14-27 июля 1998, стр. 28-29.
|
Memory.shtml
#include <windows.h>
#include <stdio.h>
#include <rtapi.h>
void main(void) { BOOL success=0; static PVOID vAddress;// virtual memory address returned LARGE_INTEGER physAddr; // base physical address ULONG length; // length to map in BOOLEAN cacheEnable; // cache accesses to memory ? physAddr.QuadPart = 0x0000FFFFF; length = 1024; cacheEnable = 0; vAddress=RtMapMemory( physAddr, length, cacheEnable); if (vAddress==NULL) { printf("Failure on RtMapMemory( 0x%08X, %d, %d ).\n", physAddr.LowPart, length, cacheEnable ); } else { printf("Success on RtMapMemory( 0x%08X, %d, %d ).\n", physAddr.LowPart, length, cacheEnable ); printf("Virtual memory address = 0x%08X \n", vAddress); success = RtUnmapMemory( vAddress); if (!success) { printf("Failure on RtUnmapMemory( 0x%08X)\t", vAddress); } else { printf("Success on RtUnmapMemory( 0x%08X)\n\n", vAddress); } } ExitProcess(0); }
Rtalloca.shtml
#include <windows.h>
#include <stdio.h>
#include <rtapi.h>
static PVOID vAddress; // virtual memory address returned by RtAllocateContiguousMemory static LARGE_INTEGER pAddress; // physical memory address returned by RtGetPhysicalAddress void main(void) { LARGE_INTEGER maxPhyAddr; //highest physical memory address ULONG size=0; //bytes to allocate maxPhyAddr.QuadPart = 0xFFFFFF; size= 0x1000; // Size in KB. // Allocate memory vAddress = RtAllocateContiguousMemory( size, maxPhyAddr); if (!vAddress) { printf("\nFailure on RtAllocateContiguousMemory\t"); printf("Error=%d\n", GetLastError()); break; } else { printf("\nSuccess on RtAllocateContiguousMemory\n"); printf("Virtual memory address = 0x%08X\n", vAddress); // Get the physical address pAddress = RtGetPhysicalAddress(vAddress); if (!pAddress.QuadPart) { printf("\nFailure on RtGetPhysicalAddress(0x%08X).\t, vAddress"); printf("Error=%d\n", GetLastError()); } else { printf("Success on RtGetPhysicalAddress(0x%08X).\n", vAddress); } // Free memory if (!RtFreeContiguousMemory(vAddress)) { printf("\nFailure on RtFreeContiguousMemory(0x%08X).\t", vAddress); printf("Error Code = %d\n", GetLastError()); } else { printf("Success on RtFreeContiguousMemory(0x%X).\n", vAddress); } } ExitProcess(0); }
Rtlockke.shtml
#include <windows.h>
#include <stdio.h>
#include <rtapi.h>
void main(void) { DWORD dwSections = RT_KLOCK_ALL; if (!RtLockKernel( dwSections)) { printf("\nFailure on RtLockKernel.\t"); printf("Error=%d\n", RtGetLastError()); } else { printf("\nSuccess on RtLockKernel\n"); if (!RtUnlockKernel( dwSections)){ printf("\nFailure on RtUnlockKernel.\t"); printf("Error=%d\n", RtGetLastError()); } else { printf("\nSuccess on RtUnlockKernel\n"); } } ExitProcess(0); }
Rtlockpr.shtml
#include <windows.h>
#include <stdio.h>
#include <rtapi.h>
void main(void) { DWORD dwSections = RT_PLOCK_ALL; if (!RtLockProcess( dwSections)) { printf("\nFailure on RtLockProcess.\t"); printf("Error=%d\n", RtGetLastError()); } else { printf("\nSuccess on RtLockProcess\n"); if (!RtUnlockProcess( dwSections)) { printf("\nFailure on RtUnlockProcess.\t"); printf("Error=%d\n", RtGetLastError()); } else { printf("\nSuccess on RtUnlockProcess\n", i); } } ExitProcess(0); }
Timer.shtml
#include "windows.h" #include "stdio.h" #include "rtapi.h" // // Local data. // LARGE_INTEGER Period; // Timer period LARGE_INTEGER StartTime; // Start time of sampling run ULONG TimerCounter = 0; // Counts entries to timer handler // // VOID main(int argc, char **argv) { LARGE_INTEGER x; HANDLE hTimer; // // Set default timer period to 500 micro seconds // Period.QuadPart = 5000; // // Set to the next to highest real-time priority. // if (!RtSetThreadPriority( GetCurrentThread(), RT_PRIORITY_MAX-1)) printf("WARNING: Can't set to highest RTAPI priority.\n"); // // Setup and start the periodic timer. // if (!(hTimer = RtCreateTimer( NULL, // Security - NULL is none 0, // Stack size - 0 is use default TimerHandler,// Timer handler NULL, // NULL context (argument to handler) RT_PRIORITY_MAX, // Priority CLOCK_2))) // RTX HAL Timer { printf("ERROR: Could not create the RTAPI timer.\n"); RtExitProcess(2); } if (!RtSetTimerRelative( hTimer, &Period, &Period)) { printf("ERROR: Could not set and start the RTAPI timer.\n"); ExitProcess(2); } // // Wait for the sampling time. // Sleep(1000); // // Stop and delete the timer. // RtCancelTimer( hTimer, &x); RtDeleteTimer( hTimer); printf("Value of number of timers counter is %d\n",TimerCounter); ExitProcess(0); } // // timer handler function //- increment the tick counter // int RTFCNDCL TimerHandler( PVOID unused ) { TimerCounter++; return 0 ; }