Ниже FreeRTOS я буду называть всетаки планировщиком задач а не ОС. Сами знаете почему.
Вчера завершил этап написания софта для бортовой ЭВМ которую хочу поставить на авиамодель. Процессор LPC2103. В качестве основы использую FreeRTOS 4.0.5. С целью жесткой временной синхронизации всех веременных событий - прем пакета с радиомодема, отработка системной команды, вычисление требуемых управляющих воздействий, обмен по I2С c исполнительными устройствами, и выдача телеметрии назад радиомодему , плюс возможные асинхронные события.
Все построено на взаимодействии задач - собственно планировщики для этого и придуманы. Очень удобно но! МЕДЛЕННО. Почему? Потому что в FreeRTOS нет специализированных средств синхронизации задач. Единственно доступный способ - через очереди сообщенй, семафоры сделаны как макросы их использующие. В результате все медленно.
За критерий производительности средств синхронизации буду понимать задержку между моментами вызова разблокирующй функции в ведущей задаче до момента выхода из блокирующей функции в синхонизируемой ведомой задаче.
Проблема - сделать синхронизацию задач быстрее.
1. В простейшем случае можно использовать vTaskResume/xTaskResumeFromISR/vTaskSuspend
но сами понимаете вариант органически УБОГИЙ но БЫСТРЫЙ. Если вам прийдется передвать информацию, то нада использовать глобальные переменные. Основной недостаток это отсутствие в таком подходе понятий "задача выполняется но ждет сигнала = реально не выполняется" или "задача заблокирована и ждет сигнала= реально не выполняется" - суть совершенно разные состояния задачи. И тд и тп с вытекающими.
2. Использование очередей - вариант НЕУБОГИЙ но МЕДЛЕННЫЙ. Как говорили мои учителя этот вариант соответствует забиванию гвоздей соток видеомагнитафоном. С пляжа....
3. Предлагается третий вариант - подход класический : отбросить плохие качества и соеденить хорошие. Сложная реализация этой простоий и гениальной ничего неговорящей по сути идеи:
а) Быстее чем vTaskResume/xTaskResumeFromISR/vTaskSuspend сделать не удастся по причине того что любая синхронизация - суть переключение контекстов. А vTaskResume/xTaskResumeFromISR/vTaskSuspend это иесть оное переключение в ПРАВИЛЬНОМ и ЛЫСОМ виде. Отсюда вывод - будем использовать vTaskResume/xTaskResumeFromISR/vTaskSuspend как ядро объектов синхронизации, а нативную логику обектов - в БЫСТРОЙ оболочке вызывающей vTaskResume/xTaskResumeFromISR/
vTaskSuspend.
б) После изучения исходников линукса и того что есть по виндам "объекты синхронизации" можно разложитьб на две большие кучи:
1. Сигналы - суть есть програмные прерывания. Ососбенность - "приемник" сигналов есть ЗАДАЧА. Тоесть сигнал послать можно именно задаче. В МОМЕНТ ПРИЕМА ПРОИСХОДИТ ПРЕРЫВАНИЕ ЗАДАЧИ И ВЫПОЛНЕТСЯ ОБРАБОТЧИК СИГНАЛА , далее задача продолжает свое выполнение с места прерывания.
2. События/семафоры/так далее - суть отдельные от ЗАДАЧИ объекты которые живут в памяти и во времени ОТДЕЛНО. Особенность - "приемник" события - есть обект, которым может владеть любая ЗАДАЧА (пример - использование очередей в FreeRTOS для синхронизации ЗАДАЧ) В МОМЕНТ ПРИЕМА ПРОИСХОДИТ РАЗБЛОКИРОВАНЕ ЗАДАЧИ, далее задача продолжает свое выполнение.
У меня получился ГИБРИД - семантически всетаки это реализация сигналов(СИГНАЛЫ ПОСЫЛАЮТСЯ ЗАДАЧАМ И ИМ ПРЕНАДЛЕЖАТ), синтаксически - события(ФУНКЦИОНАЛЬН РЕАЛИЗОВАНО КАК БЛОКИРОВАНИЕ/РАЗБЛОКИРОВАНИЕ ЗАДАЧ). Как назвать эту поронграфию я не придумал - и сигналы и события не правльно, поэтому командирским решение буду назыать это СИГНАЛЫ всетаки.
Теперь собственно ближе к телу.
Добавлено к ядру файл signal.с и signal.h в которых реализованы
// функция устанавливает сигнал Signal (сигнальное битовое поле) для задачи pxTask
// если это позволяет сделать сигнальная маска задачи, устанавливает флаг сброса события по приему SignalReset
void vSignalSet ( xTaskHandle pxTask , // дескриптор задачи-приемника сигнала
portBASE_TYPE Signal , // битовое поле сигнала
portBASE_TYPE SignalReset // флаг сброса битов сигнала после приема ожидающей задачей
);
// аналогично предыдущей но для вызовов из обработчиков прерывания
void vSignalSetFromISR
( xTaskHandle pxTask , // дескриптор задачи-приемника события
portBASE_TYPE Signal , // битовая маска события
portBASE_TYPE SignalReset // флаг сброса события после приема ожидающей задачей
);
// блокирующая функция ожидания сигнала
// если сигнал установлен то функция немедленно возвращает управление ,
// при установленом для задачи флаге SignalReset сбрасывает сигнал и флаг сброса
// в буффер помещается принятый сигнал
void vSignalWait
( portBASE_TYPE *Signal // указатель на буффер-приемник битового поля сигнала
) ;
// функция выполняет прямое чтение битового поля сигнала задачи ее вызвавшей
void vSignalTest
( portBASE_TYPE *Signal // указатель на буффер-приемник битового поля сигнала
) ;
// функция установки сигнальной маски SignalMask задачи pxTask
void vSignalMaskSet
( xTaskHandle pxTask , // дескриптор задачи-приемника маски сигнала
portBASE_TYPE SignalMask // сигнальная маска
) ;
// функция чтения сигнальной маски в *SignalMask задачи pxTask
void vSignalMaskGet
(
xTaskHandle pxTask , // дескриптор задачи-источника маски сигнала
portBASE_TYPE *SignalMask // буффер сигнальная маска
); vTaskResume/xTaskResumeFromISR/vTaskSuspend перепилены для реализации переключения не двух а четырех состояний ЗАДАЧИ.
a)Каждая задача содержит управлющую событиями структуру (расширина структура taskTCB)
б)Посылать сигналы можгут другие задачи
в)Задача имеет сигнальную маску фильтрующую посылаймые сигналы
г) "Сигнал" - битовое 32-битное поле, каждый разрад которого соответствует определенному типу сигнала, тоесть одновремено можно посылать комбинацию сигнальчиков которые и состовляют сигнал.
д)Реализованы состояния "задача выполняется но ждет сигнала = реально не выполняется" или "задача заблокирована и ждет сигнала = реально не выполняется" путем рашснирения состояний ЗАДАЧ SUSPEND и RESUME. Это означает что если ЗАДАЧА вызовет vSignalWait и перейдет врежим блоировани она будет в состоянии "задача ВЫПОЛНЯЕТЯ и ждет сигнала = реально не выполняется" вызов vSignalSet разблокирует ее. Если в состоянии блоктровки какой либо ЗАДАЧЕЙ она была заблокирована xTaskSuspend то вызов vSignalSet не разблокирует ее а только переведет в состояне "задача ЗАБЛОКИРОВАНА и не ждет сигнала = реально не выполняется"
е) Задача может находится в 4 состояниях (Pending я тут не считаю за состояние, так как это больше состояне процесора и относится к аппаратным прерываниям)
Внижеприведенных рисунках показыны циклограммы перехода ЗАДАЧИ из ожидающего состояния в активное. Момент подняти пина соответствует команда перед вызвом разблокирующей функции, опускание пина - выход синхронизируймой ЗАДАЧИ из блокирующей функции - тоесть окончание акта синхронизации. 1 рисунок - сделано на очередях 2 - на сигналах 3- на ЛЫСЫХ vTaskResume/xTaskResumeFromISR/vTaskSuspend
В итоге пеерключение на очередях - 42,0мкс, на сигналах 12,8 мкс, на лысых 10,4 мкс на частоте 88МГц ядра. Тоесть я получил частично что хотел. В архиве демо-проект под CrossWorks. там можно посмотреть ИМПЛЕМЕНТАЦИЮ СЕГО УЖАСА НАШЕГО ГОРОДЖКА
Предлагаю обсудить куда двигатся дальше, мож у кого еще иде и есть. Давайте дискутировать. Вопрос быстрой синхронизации - как говорят подонки ЗЫЖОВОТРИПЫХАЮЩИСЯ !!! тоесть очень важный.
Сообщение отредактировал klen - Aug 23 2006, 18:35
Эскизы прикрепленных изображений