• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Нужна помощь МНОГОПОТОЧНОСТЬ

Grem_line

New member
Без ОСРВ можно сделать ещё проще, использовав всего один таймер и переключая состояния лампочек в ISR.
Да можно, конечно. Но Вам надо будет писать ISR, настраивать таймер - т.е. делать некое подобие своей ОСРВ. Рыться в документации - вспоминать как это делается. А если обработчик не просто блинкает, а делает что-то миллисекунд 40? В ISR это приведет к потере каких-нибудь прерываний...
Но это извечный спор. Давайте на этом остановимся. :)
 

Grem_line

New member
Какие такие очереди? Что за фантазии? Приведите ссылки/доказательства.
Пользовательский процесс не имеет доступа к прерываниям. Как по-вашему он может получить информацию о том, что это прерывание произошло? Единственное, что может сделать системный драйвер - генерировать событие по этому прерыванию. Это событие помещается в очередь событий. Когда нужный процесс получит свой квант времени, он возьмет это событие из этой очереди. Если другие процессы к этому времени успеют разобрать из этой очереди все ранние события. Линукс использует для этого сигналы - но суть та же.
 

rst

Member
Да можно, конечно. Но Вам надо будет писать ISR, настраивать таймер
Не более, чем при портировании ОСРВ: для неё тоже нужно таймер настраивать. И для без_ОСРВ можно эту настройку и взять. И кода, в сумме, будет примерно столько же. Только ресурсов использовано в разы меньше.
А если обработчик не просто блинкает, а делает что-то миллисекунд 40? В ISR это приведет к потере каких-нибудь прерываний...
Не приведёт если выставить данному ISR приоритет ниже прочих прерываний. На ISR-ах (по-крайней мере - в стиле Cortex-M) можно сделать почти всё то же, что и задачами ОС, кроме разве что блокирующего доступа к разделяемому ресурсу.
 

rst

Member
Это событие помещается в очередь событий. Когда нужный процесс получит свой квант времени, он возьмет это событие из этой очереди.
Вы как-то странно понимаете управление исполнением задач....
Зачем в указанном случае какие-то очереди?
Скажем: задаче нужно дождаться некоего события от драйвера (окончания IO-операции). При возникновении данного события драйвер может генерить событие через SetEvent (переводя объект синхронизации в сигнальное состояние).
Задача, которой нужно дождаться события от драйвера, вызывает WaitForSingleObject() или WaitForMultipleObjects() (или их производные). Если объект в несигнальном состоянии, то thread переходит из состояния RUN, в состояние WAIT для данного объекта и более не участвует в выделении времени от диспетчера ОС (диспетчер отдаёт время другому thread-у, если таковой есть). Когда объект переходит в сигнальное состояние, то все thread-ы, бывшие в состоянии WAIT на данном объекте синхронизации, переходят в состояние READY (без всяких очередей! просто диспетчер потоков ОС помечает данный thread как READY, а не WAIT) и, если приоритет одного из данных thread-ов выше чем приоритет одного из сейчас выполняемых thread-ов, то тот thread переходит в состояние READY, а данный - в состояние RUN (т.е. - время данного ядра отдаётся ему).
Никакие очереди здесь не нужны, да и не могут использоваться т.к. при переходе объекта в сигнальное состояние все ждущие на нём thread-ы переходят в состояние RUN.
Примерно такой же механизм (с подобными состояниями задач и объектами синхронизации) используется и в ОСРВ на МК. И если Вы, как утверждаете, разрабатываете свою ОС, то должны это знать. И в ОСРВ на МК и в винде можно найти однотипные функции и объекты синхронизации выполняющие примерно одни и те же роли.
Сообщения в винде используются для общения между окнами (оконными процедурами). И механизм их работы наверняка построен на event-ах или других подобных объектах синхронизации (мьютексах или семафорах): наверняка с каждой оконной очередью связан объект синхронизации: пока очередь пуста - он в несигнальном состоянии и если оконная процедура вычитала и обработала все сообщения из очереди, то она ложится в состояние ожидания event-объекта данной очереди (WaitForSingleObject()), а когда очередная PostMessage() помещает новое сообщение в очередь, то после этого она переводит event в сигнальное состояние, давая оконной процедуре возможность обработать данную очередь.
 

nikolz

Well-known member
:):):)
Вот простейшая задача: мигать тремя светодиодами каждым со своей некратной частотой.
На некоей ОСРВ такая задача выглядит примерно так:
Код:
static int Blink(int N, int M)
{
 for(;;)
 {
  toggle(N);
  taskDelay(M);
 }
}

int main()
{
taskSpawn("Blink1",Blink,1, 50); // Мигать 1 светодиодом с периодом 50 мс
taskSpawn("Blink2",Blink,2, 70); // Вторым с 70
taskSpawn("Blink3",Blink,3, 90); // Третьим с 90
return 0;
}
Где тут замедление и попробуйте проще без ОСРВ?
задача решается ПРОСТО так:
--------------------
1)таймер - на 10 мс с автозагрузкой
----------------
2) в колбеке таймера три условия if по которым мигает каждый из диодов.
и ВСЕ
------------------------
У Вас такой же таймер внутри ОС
В его колбеке выполняется определение очередности задач и переход в очередную.
-----------------------
При этом надо сохранить окружение задачи т е все регистры процессора стек и т д (В моем варианте этого нет)
В задачах у вас исполняется управление миганием т е тоже самое что у меня в if.
-------------------------------------
В результате с ОС будет лишнее - это сохранение окружения для каждой задачи и потом его восстановление.
------------------
В моем варианте этого нет вообще.
 

nikolz

Well-known member
Безусловно OC универсально и упрощает работу с устройствами и исполнение множества задач.
Но часто встраиваемая система это например датчик температуры и все или датчик температуры и пид регулятор.
--------------------
Т е задача вполне конкретная и всего одна или две.
В этом случае она пишется полностью и не требует универсальности ОС. Как говорят делается на металле.
----------------------------
С учетом ограничений железа малого объема памяти, решение без ОС в разы быстрее и компактнее. т е дешевле и менее энергоемкое. А это даст возможность снизить требования к источникам питания охлаждению и т д
------------------------
Пример тому ESP8266 на которой на металле без ОС можно снизить энергопотребление в 5-30 раз и повысить быстродействие от 2 до 10 раз.
 

Grem_line

New member
надо сохранить окружение задачи т е все регистры процессора стек и т д (В моем варианте этого нет)
А как по-Вашему строится обработчик прерывания? Вы считаете, что при входе в прерывание не требуется сохранение всех регистров процессора? Когда Вы пишете обработчик на языке высокого уровня, то за Вас это делается автоматически. Попробуйте поработать на Ассемблере и обработать прерывание самостоятельно. Стек, конечно, можно не сохранять, а работать в том же стеке, что и прерываемая задача, но переключение стека - это перезапись одного регистра SP. А переполнение стека - вещь неприятная и трудно отлавливаемая.
Так что в Вашем варианте это тоже есть.
Конечно, можно обойтись и без ОС, работать типа BareMetal - на голом железе. Для простых задач это вполне допустимо. Но разработка потребует хороших знаний аппаратуры (или использование дополнительных библиотек). Так что дело вкуса.
Еще замечу, что при самостоятельном конструировании "многозадачности" могут быть проблемы одновременного доступа к ресурсам из прерываемой и прерывающей задач. В ОСРВ для этого есть специальные средства: такие как семафоры (мьютексы). Вам придется это все придумывать и делать вручную. А зачем изобретать велосипед?
По поводу объемов: многозадачное ядро RTOS Multex для процессора Intel x86 занимает около 60 КБайт. Для однокристаллок есть специальные операционки, занимающие какие-то сотни байт.
К слову сказать, любая среда разработки на языках С или С++ подключает к коду библиотечные функции, играющие роль небольшой упрощенной ОС. Не вижу ничего плохого в том, что она может иметь функции многозадачности.
 
Последнее редактирование:

Grem_line

New member
Сообщения в винде используются для общения между окнами (оконными процедурами). И механизм их работы наверняка построен на event-ах или других подобных объектах синхронизации
Равно как и для общения между драйверами и пользовательскими приложениями.
Всякая очередь строится на основе целочисленного семафора и буферного массива.
Я как-то раз пытался работать с аппаратурой в винде. Драйвер, поставляемый с устройством передавал события прерываний, а приложение работало через waitForSingleObject. Так и не удалось работать при плотной загрузке - периодически события терялись. В итоге разработчик аппаратуры заявил, что в винде это только демонстрация возможностей. Работа была продолжена на RTOS vxWorks - там все работало четко. Не уверен, что в Windows есть механизмы активации пользовательских процессов до наступления кванта времени, отведенного планировщиком для этого процесса. Да и приоритетов там всего несколько.
 

nikolz

Well-known member
А как по-Вашему строится обработчик прерывания? Вы считаете, что при входе в прерывание не требуется сохранение всех регистров процессора? Когда Вы пишете обработчик на языке высокого уровня, то за Вас это делается автоматически. Попробуйте поработать на Ассемблере и обработать прерывание самостоятельно. Стек, конечно, можно не сохранять, а работать в том же стеке, что и прерываемая задача, но переключение стека - это перезапись одного регистра SP. А переполнение стека - вещь неприятная и трудно отлавливаемая.
Так что в Вашем варианте это тоже есть.
Конечно, можно обойтись и без ОС, работать типа BareMetal - на голом железе. Для простых задач это вполне допустимо. Но разработка потребует хороших знаний аппаратуры (или использование дополнительных библиотек). Так что дело вкуса.
Еще замечу, что при самостоятельном конструировании "многозадачности" могут быть проблемы одновременного доступа к ресурсам из прерываемой и прерывающей задач. В ОСРВ для этого есть специальные средства: такие как семафоры (мьютексы). Вам придется это все придумывать и делать вручную. А зачем изобретать велосипед?
По поводу объемов: многозадачное ядро RTOS Multex для процессора Intel x86 занимает около 60 КБайт. Для однокристаллок есть специальные операционки, занимающие какие-то сотни байт.
К слову сказать, любая среда разработки на языках С или С++ подключает к коду библиотечные функции, играющие роль небольшой упрощенной ОС. Не вижу ничего плохого в том, что она может иметь функции многозадачности.
Вы немного путаете
Есть аппаратное сохранение счетчика команд при переходе по аппаратному прерыванию
А есть программное сохранение окружения задачи (это в ОС) Вот это сохранение при одном ядре и одной задаче(как в моем примере) не требуется
Например в ESP
мы имеем функцию колбека в виде:
void gpio_intr_cb(int *dummy){
uint32 status=GPIO_REG_READ(GPIO_STATUS_ADDRESS);
....
}
 

rst

Member
Равно как и для общения между драйверами и пользовательскими приложениями.
Могут использоваться != всегда используются.
Для каких-то случаев взаимодействия драйвер<->пользовательский уровень могут использоваться очереди. Но в целом: это не обязательно.
Это то же самое что утверждать, что автомобиль для перевозки груза использует прицеп. Да, прицеп может использоваться, если не хватает места в автомобиле.
Прочитайте про overlapped ввод-вывод в винде чтобы понять как происходит синхронизация драйверов с уровнем пользователя на основе event-ов.

Всякая очередь строится на основе целочисленного семафора и буферного массива.
Вот, наконец-то! :)
Я это и писал выше: объект синхронизации (семафор в данном случае) здесь как раз и используется для межпроцессной синхронизации (уведомляет другой thread о событии в данном thread-е), а буфер - опционален, так как данных в этом событии может и не быть или они передаются другим способом.

Я как-то раз пытался работать с аппаратурой в винде. Драйвер, поставляемый с устройством передавал события прерываний, а приложение работало через waitForSingleObject. Так и не удалось работать при плотной загрузке - периодически события терялись.
Значит у Вас там был баг, который Вы так и не нашли. o_O
Я написал кучу многопоточных приложений под виндой, взаимодействующих с драйверами посредством WaitForSingleObject()/WaitForMultipleObject() или их производных и всё работает без каких-либо потерь. Вот и сейчас как раз пишу очередную утилиту, в которой взаимодействие моих thread-ов с Winsock API и межпоточное взаимодействие: всё построено на WaitForSingleObject()/WaitForMultipleObjects() и EnterCriticalSection()/LeaveCriticalSection().
 

rst

Member
Вы считаете, что при входе в прерывание не требуется сохранение всех регистров процессора?
Я думаю, что nikolz хотел сказать, что для ISR компилятор может определить какие регистры используются внутри функции ISR и сохранить только те, что ISR портит. В то время как переключатель контекста ОС не может определить какие регистры использованы в текущей задаче (с некоторыми оговорками) и, соответственно, вынужден сохранять все регистры.
Кроме этого он делает ещё кучу действий, а не только перезапись SP. Можете убедиться в этом, посмотрев код любого переключателя контекста ОС.
И кроме переключателя контекста для работы механизмов ОС нужно ещё много чего периодически выполнять: один только запуск решедулера чего стоит. А без его вызова иногда никак не обойтись.
При реализации псевдо-задач в виде ISR ничего этого не нужно.
Так что естественно накладных расходов в случае RTOS гораздо больше чем без неё. И они увеличиваются с ростом необходимой частоты переключения задач.
 

=AK=

New member
Удивительно читать все эти "аргументы" против RTOS. Какое-то дремучее крохоборство, как будто в 80-е годы прошлого века живем и высчитываем время исполнения по тактам, а расход памяти по байтам. Ребята, проснитесь! Все это давно кончилось как кошмарный сон. Такие вещи сейчас заботят только разве что тех китайцев, кто клепает свердешевые гаджеты миллионными сериями. А вам на это начхать и забыть, особенно для ESP8266 и иже с ним.

Реальную ценность представляет только человеческое время. При использовании RTOS времени на разработку тратится меньше, поэтому вопрос "использовать или нет" не стоИт: да, надо использовать. Ровно по той же причине надо программы писать не на ассемблере, а на ЯВУ.
 

nikolz

Well-known member
Удивительно читать все эти "аргументы" против RTOS. Какое-то дремучее крохоборство, как будто в 80-е годы прошлого века живем и высчитываем время исполнения по тактам, а расход памяти по байтам. Ребята, проснитесь! Все это давно кончилось как кошмарный сон. Такие вещи сейчас заботят только разве что тех китайцев, кто клепает свердешевые гаджеты миллионными сериями. А вам на это начхать и забыть, особенно для ESP8266 и иже с ним.

Реальную ценность представляет только человеческое время. При использовании RTOS времени на разработку тратится меньше, поэтому вопрос "использовать или нет" не стоИт: да, надо использовать. Ровно по той же причине надо программы писать не на ассемблере, а на ЯВУ.
А кто Вас заставляет отказываться от RTOS?
Без обид, Но заставь дурака богу молится он и лоб расшибет.
Что же касается ESP8266 то тут как раз без RTOS на голом металле все в разы быстрее и менее прожорливее.
Более того, написав один раз шаблон далее просто вставляем в него нашу задачу также как и в RTOS.
--------------------------
Возможно у Вас уходит много времени чтобы в дурине написать термометр и измеритель влажности и двумя колбеками , но это не значит что у других также.
Ваше заявление, что собственное непонимание можно заменить большим объемом памяти у контроллера - это отговорка.
А про экономию времени путем применения OC -это старые отговорки тех кто эти ОС сделал.
Вообще же никто не утверждает что надо от чего то отказываться, просто надо знать что и когда позволяет решить задачу оптимально и быстро.
---------------------------
Учиться, учиться и еще раз учиться...
 

=AK=

New member
А кто Вас заставляет отказываться от RTOS?
Без обид, Но заставь дурака богу молится он и лоб расшибет.
Что же касается ESP8266 то тут как раз без RTOS на голом металле все в разы быстрее и менее прожорливее.
Более того, написав один раз шаблон далее просто вставляем в него нашу задачу также как и в RTOS.
--------------------------
Возможно у Вас уходит много времени чтобы в дурине написать термометр и измеритель влажности и двумя колбеками , но это не значит что у других также.
Ваше заявление, что собственное непонимание можно заменить большим объемом памяти у контроллера - это отговорка.
А про экономию времени путем применения OC -это старые отговорки тех кто эти ОС сделал.
Вообще же никто не утверждает что надо от чего то отказываться, просто надо знать что и когда позволяет решить задачу оптимально и быстро.
---------------------------
Учиться, учиться и еще раз учиться...
Вам надо быстрее и памяти вам мало в ESP8266, для термометрa? Вы вдумайтесь, что вы пишите. Вы еще на ассемблер перейдите для пущей экономии ресурсов.
 

nikolz

Well-known member
Вам надо быстрее и памяти вам мало в ESP8266, для термометрa? Вы вдумайтесь, что вы пишите. Вы еще на ассемблер перейдите для пущей экономии ресурсов.
Я вообще-то сначала думаю а потом пишу.
Попробуйте сделать на ESP8266 на RTOS измерение температуры с интервалом 1 сек с передачей по WIFI и средним потребляемым током за секунду не более 20 ма.
Если используем программирование на голом металле (работа в boot) то ток потребления при работе в boot от 2.5 до 17 ма время работы любое
и передача по WIFI (работа в NONOS) время работы 0.2 с .
После этого можем конкретно обсудить достоинство вашего решения по сравнению с моим.
 

Grem_line

New member
Я думаю, что nikolz хотел сказать, что для ISR компилятор может определить какие регистры используются внутри функции ISR и сохранить только те, что ISR портит.
А я думаю, что Вы слишком высокого мнения о возможностях компилятора. :)
В обработчике прерывания никто не мешает использовать вызовы процедур, в том числе и библиотечных.
Поэтому компилятор тупо вставляет пролог и эпилог для сохранения/восстановления всех регистров.
Кроме этого он делает ещё кучу действий, а не только перезапись SP. Можете убедиться в этом, посмотрев код любого переключателя контекста ОС.
Вот код для переключения контекста RTOS Multex для процессоров ARMv6:
Код:
.global lowSwitchTo, lowJumpTo
.align 5
lowSwitchTo:
    stmfd sp!, {lr}
    stmfd sp!, {r0-r12, lr}
    mrs   r0, cpsr
    stmfd sp!, {r0}
    ldr   r0, WorkTask
    str   sp, [r0, #SP_OFFSET]
lowJumpTo:   
    ldr   r0, NextTask 
    str   r0, WorkTask
    ldr   sp, [r0, #SP_OFFSET]
    ldmfd sp!, {r0}
    msr   spsr, r0
    ldmfd sp!, {r0-r12, lr, pc}^
Вы правы - "куча действий";)
 

nikolz

Well-known member
А я думаю, что Вы слишком высокого мнения о возможностях компилятора. :)
В обработчике прерывания никто не мешает использовать вызовы процедур, в том числе и библиотечных.
Поэтому компилятор тупо вставляет пролог и эпилог для сохранения/восстановления всех регистров.

Вот код для переключения контекста RTOS Multex для процессоров ARMv6:
Код:
.global lowSwitchTo, lowJumpTo
.align 5
lowSwitchTo:
    stmfd sp!, {lr}
    stmfd sp!, {r0-r12, lr}
    mrs   r0, cpsr
    stmfd sp!, {r0}
    ldr   r0, WorkTask
    str   sp, [r0, #SP_OFFSET]
lowJumpTo:  
    ldr   r0, NextTask
    str   r0, WorkTask
    ldr   sp, [r0, #SP_OFFSET]
    ldmfd sp!, {r0}
    msr   spsr, r0
    ldmfd sp!, {r0-r12, lr, pc}^
Вы правы - "куча действий";)
А как это связано с ESP8266? Можно на примере кода для ESP8266 показать? Спасибо
 

Grem_line

New member
Если используем программирование на голом металле (работа в boot) то ток потребления при работе в boot от 2.5 до 17 ма время работы любое
и передача по WIFI (работа в NONOS) время работы 0.2 с .
Никто не спорит с тем, что для простых задач можно и даже нужно обходиться без ОС.
Но вопрос-то был в другом: Если вычислитель уже работает под RTOS, и при этом запущен только один поток (как это сделано на ESP32), то есть ли смысл заморачиваться с самопальной обработкой?
Не проще ли будет воспользоваться теми возможностями, которые эта RTOS предоставляет?
Например, запустить процедуру в отдельном потоке с помощью xTaskCreate().
Объем кода это только уменьшит, ибо все механизмы многозадачности в нем уже и так содержатся, нужно только их вызвать.
 

Grem_line

New member
Можно на примере кода для ESP8266 показать?
Вы думаете, что ESP8266 не содержит проприетарных частей, скрытых от пользователя?
Если Вы подставляете callback-функцию для обработки какого-либо прерывания, то считаете, что эта функция будет аппаратно вызвана при наступлении прерывания?
Ничего подобного! При наступлении прерывания происходит переход по вектору, который указывает на внутреннюю процедуру, Вам не доступную. В этой процедуре первым делом сохраняется контекст прерванной задачи, производится аппаратный отбой прерывания, подготовка и вызов по указателю Вашей callback-функции. Выход из функции возвращает в системную процедуру ISR, в которой производится восстановление контекста прерываемой задачи и возврат в нее.
Те, кто пишут здесь, что программируют на голом железе, глубоко ошибаются: к их собственному коду в обязательном порядке подключаются некие библиотечные функции, обеспечивающие правильный запуск процессора, настройку всей аппаратной части и обработку прерываний, драйверы устройств ввода/вывода и многое другое. Подумайте, если Вы пишете код на C++, кто, когда и где вызовет конструкторы глобальных объектов, которые Вы вполне можете использовать? А кто производит обработку исключительных ситуаций?
Когда разработчик предоставляет Вам IDE, то оно уже включает в себя некую операционную систему, о существовании которой многие даже не подозревают!
 

nikolz

Well-known member
Вы думаете, что ESP8266 не содержит проприетарных частей, скрытых от пользователя?
Если Вы подставляете callback-функцию для обработки какого-либо прерывания, то считаете, что эта функция будет аппаратно вызвана при наступлении прерывания?
Ничего подобного! При наступлении прерывания происходит переход по вектору, который указывает на внутреннюю процедуру, Вам не доступную. В этой процедуре первым делом сохраняется контекст прерванной задачи, производится аппаратный отбой прерывания, подготовка и вызов по указателю Вашей callback-функции. Выход из функции возвращает в системную процедуру ISR, в которой производится восстановление контекста прерываемой задачи и возврат в нее.
Те, кто пишут здесь, что программируют на голом железе, глубоко ошибаются: к их собственному коду в обязательном порядке подключаются некие библиотечные функции, обеспечивающие правильный запуск процессора, настройку всей аппаратной части и обработку прерываний, драйверы устройств ввода/вывода и многое другое. Подумайте, если Вы пишете код на C++, кто, когда и где вызовет конструкторы глобальных объектов, которые Вы вполне можете использовать? А кто производит обработку исключительных ситуаций?
Когда разработчик предоставляет Вам IDE, то оно уже включает в себя некую операционную систему, о существовании которой многие даже не подозревают!
Зачем это гадание на кофейной гуще и риторические вопросы?
Вы просто напишите код который даст лучше результаты тем те, которые получаются при работе на металле в ESP8266 . И я с Вами соглашусь.
 
Сверху Снизу