Цитата
Присоединюсь к интересующимся. Если я правильно понял то для более низкоприоритетной задачи высокоприоритетная по сути является прерыванием?
Да, совершенно верно.
Цитата
И соответственно равноприоритетные задачи не предусмотрены?
Конечно предусмотрены! И они выполняются в порядке очереди. Так же, как и прерывания. Закончилась одна задача - начинается выполнение другой. Или закончилось одно прерывание(а второе висело в пендинге) - начинается выполнение другого.
В дополнение - я считаю вечные циклы и мютексы - каменным веком. Необходимость мютексов возникает из за вечных циклов. Нет вечных циклов - нет и мютексов.
Зачем нужны вечные циклы? Какая их практическая цель? Везде, где я их встречаю - их запросто можно оттуда выкинуть. Зачастую это код вида:
Код
void Thread1(){
while(true){
if(must_i_exit)break;
if(is_user_clicked_helo_button){
messageBox("Hi dear user");
}
}
}
void Thread2(){
while(true){
if(must_i_exit)break;
if(mp3_frame_ready){
decodeMp3Frame(frame);
}
}
}
То есть и за вечных циклов наm понадобятся тяжеленные мютексы, семафоры - работа с которыми зачастую приводит к блокировке всех прерываний внутри кода самих семафоров; 2 довольно больших стека, заведомо неизвестного и трудно просчитываемого размера. И так под каждый тред - по сути под обычную функцию!
Теперь давайте попробуем откажемся от этих циклов:
Код
void on_helo_button_clicked(){
messageBox("Hi dear user");
}
void on_mp3_frame_received(const Mp3Frame* frame){
decodeMp3Frame(frame);
}
Код стал даже проще, теперь нам нужен один стек, теперь нам не нужны мютексы и блокировки. Мало того, сами функции, выполняющие работу остались прежние.
Что происходит при блокировке:
1. блокируются, как минимум все потоки. и как максимум - все прерывания
2. сохраняются все регистры в стеке(а это 16 4-байтовых слов для кортекса без FPU)
3. производится поиск готового к выполнению потока. очень тяжелая задача, особенно при наследовании приоритетов.
4. восстанавливаются из стека все регистры.
5. разблокируется все обратно
Теперь что происходит при добавлении задачи в очередь:
1. блокируется все потоки и прерывания, которые
могут работать с этой очередью. на кортексе это одна инструкция.
2. резервируется в очереди место под задачу. Фактически данные задачи состоят из
аргументов функции, то есть там п
ара-тройка 4-байтовых слов. В отличии от сохранения контекста, где их как минимум 16, а на практике больше.
Физически резервирование места это чтение двух указателей, простое условие, затем инкремент одного из них и запись в память. Это несколько простых инструкций.
3. блок снимается.
4. записывается в зарезервированное место данные задачи(наши аргументы функции). При этом все прерывания и все задачи продолжают работу
5. опять блокируемся
6. помечается зарезервированный участок очереди, как валидный - пара инструкций
7. снимаем блок
Все. И того операция очень простая и понятная, мало того, любое переполнение очереди нормально отлавливается программно и зачастую это recoverable error - прозрачный для пользователя, в то время как переполнение стека - это труба.
Код такой очереди занимает 240 строк вместе с комментами и C++ными приколами. Сколько строк
исполняемого кода занимает мютекс и переключение контекста в какой-нибудь FreeRTOS

?
Цитата
Интересно. Где можно посмотреть код, примеры?
Ну часть я уже показал. Если это все поняли - готовлю следующую. Чтобы понять почему работает аллокатор, надо разобраться как работают задачи и очереди.
Пока задавайте вопросы, если есть.
Выше я показал почему не нужны треды. А вот как можно без них обойтись - рассказано в этом материале
http://www.embedded.com/design/prototyping...r-Simple-TaskerЯ конечно пошел дальше, сделал некоторые моменты более оптимально с учетом конкретной архитектуры(архитектур) и реализовал все на c++ чтобы было просто и удобно и безопасно пользоваться. Об этом позже.