fk0, легенда (13.08.2011 15:54, просмотров: 23885)
Тут очень любят рассуждать о RTOS и всём таком. Но как-то массово замалчивается, что стандартная C-библиотека для неопределённого ряда своих функций не допускает рекурсивных (вложенных) вызовов. Классический пример -- strtok(). Но на самом деле проблему могут вызвать и математические функции и что угодно -- заранее не сказать. Проблему вызовет любая статическая переменная в libc. Проблема широко известна в unix-программировании (какие функции можно вызывать в обработчике сигнала, что в принципе, эквиэвалентно прерыванию в микроконтроллере, или переключению контекста в RTOS).
Причём, если в обратботчике сигнала или прерывания можно как-то отказаться от библиотечных функций, то при использовании нескольких потоков в RTOS, получается, библиотечные функции можно использовать только в одном потоке. При вытесняющей многозадачности. Абсурд!
Разумеется есть RTOS, где библиотека C собственная, отличная от поставляемой с компилятором, и где эти проблемы решены, но это отдельная история. Есть также отдельные библиотеки C, расчитанные на работу в многопоточной среде и где эта проблема решена (но не в связке с конкретной ОС). Но глядя на множество OS с вытесняющей многозадачностью для мелких МК, где libc только из комплекта компилятора (FreeRTOS, tnkernel, scmrtos...... список может быть очень большой) возникает вопрос -- а какой в этом смысл, если на базе такой rtos, без "многопоточной" libc и без обёртки её опасных функций механизмами синхронизации ОС, практически, невозможно использовать библиотеку C вовсе (кроме одного потока) -- какой тогда смысл вообще в такой "многозадачности"?
Если принципиально необходимо вытеснение, если есть несколько задач активно использующих C-библиотеку -- такие RTOS принципиально не применимы и кооперативная псевдомногозадачность (да хоть простейший big loop) -- смотрится более реально. Либо использование "тяжёлых" ОС, где все эти недостатки решены, но и контроллеры там другие.
Взять практический пример. PIC24 и компилятор C30. Или PIC32 и C32. Где гарантия, что например, функция atan() не будет вычислена неправильно при параллельной работе? Практически действительно "нереентрантных" функций в языке мало и они все известны (asctime, ctime, gmtime, localtime, strerror rand, srand, strtok... и ещё переменная errno). Но практически неизвестно (об этом нигде не пишется), что реализация остальных функций "реентрантна". Например printf запросто может быть нереентрантным из-за буферизации (статические переменные). Кроме того, errno в любом случае нужна, в основном для math.h. Функция malloc тоже. Ещё функции работающие с UTF-8. Может ещё что-то забытое. Функции printf, atan, strtok и malloc можно не использовать (и нужно) в прерываниях. Но как быть в многозадачной вытесняющей ОС?
Вывод из этого можно сделать неутешительный. Большинство простых вытесняющих RTOS на самом деле бесполезно. Ибо вынуждают программировать без использования библиотечных функций. Что годится, чаще, только для маленьких ограниченных программ. Которые также часто можно реализовать без ОС вовсе.
Другое дело кооперативные ОС. Там библиотеку C можно использовать полноценно. Но расчитывать не вытеснение не приходится, что затрудняет программирования некоторых задач. Среди кооперативных ОС особняком стоят те, что реализуют некоторый механизм сопрограмм или т.н. protothreads. Возможно они упрощали бы программирование, но на удивление все обладают одним фатальным недостатком -- процесс не может ни ожидать более чем одного события одновременно, ни не обладает возможностью асинхронной коммуникации (вроде сигналов в unix). В "больших" ОС обычно есть и какой-то вариант waitformultipleevents или select() и сигналы. Невозможность ожидания более чем одного события ведёт опять же к ручной проверке событий (например, по-таймеру -- чем это отличается от проверки флагов в big loop?), либо к нагромождению (псевдо)параллельных задач решающих, в общем-то одну задачу -- и их между собой ещё нужно как-то синхронизировать. В итоге применение такой ОС может больше порождать проблем, чем решать. Тем более, если изначально программа проектируется на основе конечных автоматов, например. Каждый автомат обычно в каждом состоянии реагирует более чем на одно событие. И разбивать реализацию автомата на несколько задача в protothreads исключает все плюсы и применения protothreads и автоматного подхода к проектированию.
Big loop не так уж и плох? Если нужно вытеснение, то серьёзной альтернативой может служить что-то вроде ecos (где в newlib вопрос с реентрантностью решен). С маленькие контроллерами с объёмами ПЗУ до 128-256кБайт и ОЗУ до 8-64КБайт тут делать скорей нечего.
[ZX]