jcxz 0 Posted January 10 · Report post У меня имеется своя очень лёгкая вытесняющая РТОС для Cortex-M. С минимумом функционала. Из объектов синхронизации - только семафоры и флажки (функционал подобный CreateEvent()/SetEvent()/ResetEvent() в WinAPI). Равноприоритетных задач нет (не надо). Для разделения доступа задач к ресурсам сейчас использую семафоры. Имеется проблема - инверсия приоритетов задач. Поэтому задумался о добавлении мьютексов в свой код. Мьютексов с динамическим изменением приоритетов задач. Но очень не хочется терять лёгкость ОС (малое время переключения задач, низкие траты времени на сам сервис ОС). Посмотрел как реализованы мьютексы: uCOS-II - не понравилось, не корректно и не удобно (для пользовательского кода); uCOS-III - очень громоздко, длительные запреты прерывания (обход свЯзных списков в цикле в критической секции и т.п.); scmRTOS - мьютексы не умеют динамического изменения приоритетов задач; FreeRTOS - глубоко не разбирался, но похоже тоже громоздко как в uCOS-III. Пока что-то не могу придумать лёгкий алгоритм работы с мьютексами. Может кто подскажет идею? Чтобы без циклов внутри критических секций. От мьютекса достаточно 2 функций: захват, освобождение. Задумался - а вообще в принципе это осуществимо ли? Или если делать дин.приоритеты задач, то с лёгкостью ОС по любому нужно прощаться? Если например брать реализацию как в uCOS-III (на свЯзных списках), то функции захвата/освобождения мьютекса становятся многократно(!) более длительными, чем существующие сейчас функции захвата/освобождения семафора (которые не умеют изменения дин.приоритета). Quote Ответить с цитированием Share this post Link to post Share on other sites
mantech 0 Posted January 10 · Report post 29 минут назад, jcxz сказал: Мьютексов с динамическим изменением приоритетов задач Для каких это может потребоваться целей? Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 10 · Report post 5 минут назад, mantech сказал: Для каких это может потребоваться целей? Написал же: Для борьбы с инверсией приоритетов задач. Читайте: https://ru.wikipedia.org/wiki/Мьютекс Quote Ответить с цитированием Share this post Link to post Share on other sites
AlexRayne 0 Posted January 10 · Report post Если мутексы тяжелы, то может стоит задуматься как обойтись без них? какова задача? Quote Ответить с цитированием Share this post Link to post Share on other sites
dxp 0 Posted January 11 · Report post 9 часов назад, jcxz сказал: scmRTOS - мьютексы не умеют динамического изменения приоритетов задач; именно потому что 9 часов назад, jcxz сказал: очень громоздко, длительные запреты прерывания (обход свЯзных списков в цикле в критической секции и т.п.); Мутексы с инверсией приоритетов подходят для толстых осей, где накладные и так большие, на их фоне пробег про объектам оси с целью корректного изменения приоритетов выглядит не так затратно. Для мелких осей накладные соизмеримы или больше затрат на непосредственную работу с объектом блокировки. Задача легко решается без таких мутексов путём "делегирования" работы потоку с достаточно высоким приоритетом. Например, к объекту лезут два потока с приоритетами 3 и 8 (меньше - выше приоритет), чтобы поток с приоритетом 8 не блокировал работу потока с приоритетом 3, можно работу с объектом в потоке 8 реализовать в отдельной функции, указатель на которую метнуть в очередь специального foreground потока с приоритетом, например 1. На простой иерархии классов с виртуальными функциями получается очень просто и эффективно: заводится абстрактный базовый класс TJob, от него рожается сколько надо производных классов-заданий, при постановке задания в очередь, которую читает поток с приоритетом 1, кидается указатель на объект-задание. Точно так же реализуется обработка фоновых заданий: если есть затратные по времени, но не приоритетные задачи (какие-нить вычисления длительные), потоки не выполняют их в своём цикле, а скидывают в очередь низкоприоритетного потока. Тут на стр. 125 подробно описано с примером. Quote Ответить с цитированием Share this post Link to post Share on other sites
mantech 0 Posted January 11 (edited) · Report post 10 часов назад, jcxz сказал: Для борьбы с инверсией приоритетов задач. Да уж, всегда использовал мьютексы только в очень редких случаях, стараясь планировать задачи так, чтобы не было перекрестного обращения к одним и тем же устройствам... 28 минут назад, dxp сказал: Например, к объекту лезут два потока с приоритетами 3 и 8 (меньше - выше приоритет), чтобы поток с приоритетом 8 не блокировал работу потока с приоритетом 3 Ну а если №8 уже занял устройство? №3 по любому будет ждать, хоть у него самый высший приоритет будет. Поэтому лучше метод каждому устройству свой поток. Медленное устройство прекрасно может работать по принципу мейлбокса, обслуживая фоновые запросы от неск. потоков и не блокируя их. 33 минуты назад, dxp сказал: Точно так же реализуется обработка фоновых заданий: если есть затратные по времени, но не приоритетные задачи (какие-нить вычисления длительные), потоки не выполняют их в своём цикле, а скидывают в очередь низкоприоритетного потока. Ну вот как раз это и имел ввиду. Edited January 11 by mantech Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 11 · Report post 13 часов назад, AlexRayne сказал: Если мутексы тяжелы, то может стоит задуматься как обойтись без них? какова задача? Задача - реализовать мьютексы. А как обходиться без них я и так знаю. Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 11 · Report post 5 часов назад, dxp сказал: Задача легко решается без таких мутексов путём "делегирования" работы потоку с достаточно высоким приоритетом. Например, к объекту лезут два потока с приоритетами 3 и 8 (меньше - выше приоритет), чтобы поток с приоритетом 8 не блокировал работу потока с приоритетом 3, можно работу с объектом в потоке 8 реализовать в отдельной функции, указатель на которую метнуть в очередь специального foreground потока с приоритетом, например 1. На простой иерархии классов с виртуальными функциями получается очень просто и эффективно: заводится абстрактный базовый класс TJob, от него рожается сколько надо производных классов-заданий, при постановке задания в очередь, которую читает поток с приоритетом 1, кидается указатель на объект-задание. Что можно обойтись без мьютексов, написав дополнительного кода - я и так знаю. Так как не первый день пишу под РТОС. И именно до сих пор и приходится реализовывать подобные костыли. Но хчется отбросить наконец-то костыли. Так как мьютексы реализуются один раз - в РТОС, а костыли - в каждом проекте по новой. И во-вторых: такой вариант как вы предлагаете, решая одни проблемы, создаёт кучу других. Например: если какой-то сервис выполнялся в пределах только одной задачи (контекста), то перенос части функционала в другую задачу, сразу порождает кучу дополнительных проблем с межзадачным взаимодействием в алгоритме работы этого сервиса. И вообще - подобное решение уже реализовано в uCOS-II (имеющее все те же достоинства, но гораздо меньше недостатков: лишённое недостатка работы из разных контекстов и необходимости в каждом проекте заново его прописывать). О чём я писал в самом начале. Там есть половинчатое решение проблемы: для каждого мьютекса резервируется уникальный задачный приоритет, и если занятый мьютекс пытается захватить какая-то задача, то приоритет владеющей мьютексом задачи, повышается до этого зарезервированного приоритета. Программист, при написании проекта, должен позаботиться о резервировании такому мьютексу задачного приоритета, с уровнем +1 или -1 к приоритету самой высокоприоритетной задачи, которая может владеть мьютексом. Плюсы решения: 1) не нужны списки и соответственно меньше время обслуживания; 2) простота реализации. Минусы: 1) при попытке захвата занятого мьютекса даже низкоприоритетной задачей, его приоритет повышается до максимального (фиксированного); 2) общее количество задачных приоритетов (32) делится между задачами и мьютексами (т.е. - ограничение числа мьютексов и задач). Цитата Точно так же реализуется обработка фоновых заданий: если есть затратные по времени, но не приоритетные задачи (какие-нить вычисления длительные), потоки не выполняют их в своём цикле, а скидывают в очередь низкоприоритетного потока. Как обходить проблему инверсии приоритетов - я знаю, так как много лет работаю под РТОС. И всегда приходится придумывать тот или иной механизм. Вопрос был в том, что хочется наконец-то решить раз и навсегда эту проблему и забыть, не ваять костыли в каждом проекте заново. И решить качественно. 4 часа назад, mantech сказал: Ну а если №8 уже занял устройство? №3 по любому будет ждать, хоть у него самый высший приоритет будет. Вопрос не о замене на мэйлбоксы. Там где они возможны - там они и должны использоваться. Назначение мьютексов и семафоров и область их применения иная нежели у мэйлбоксов. И не всегда Цитата Поэтому лучше метод каждому устройству свой поток. Попробуйте дать "свой поток" каждому устройству, висящему на общей шине. Например - каждому из устройств висящих на единой SPI или единой I2C. И к тому же - кроме устройств, разделяемыми ресурсами может быть ещё много чего. Например: у меня есть такой объект, как "поток отладочного вывода". Тоже разделяемый ресурс, разделяемый почти всеми задачами проекта. Quote Ответить с цитированием Share this post Link to post Share on other sites
arhiv6 0 Posted January 11 · Report post 32 минуты назад, jcxz сказал: Попробуйте дать "свой поток" каждому устройству, висящему на общей шине. Например - каждому из устройств висящих на единой SPI или единой I2C. И к тому же - кроме устройств, разделяемыми ресурсами может быть ещё много чего. Например: у меня есть такой объект, как "поток отладочного вывода". Тоже разделяемый ресурс, разделяемый почти всеми задачами проекта. Для разделения доступа не обязательно выделять отдельный процесс. Можно в ОС заложить примитивы синхронизации/обмена сообщениями, которые позволяют подобное делать без использования мьютексов или отдельного процесса. Например, в RExOS реализована безмьютекскная система. Для "потока отладочного вывода" применяется примитив stream. Описание тут: главы "2.3. Безмьютексная система" и "7. Синхронизация процессов" https://raw.githubusercontent.com/alexeyk13/rexos/master/doc/RExOS.0.4.4.pdf Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 11 · Report post 19 минут назад, arhiv6 сказал: Для разделения доступа не обязательно выделять отдельный процесс. Можно в ОС заложить примитивы синхронизации/обмена сообщениями, которые позволяют подобное делать без использования мьютексов или отдельного процесса. Да, конечно, можно решить это за счёт большого буфера в ОЗУ. Для винды - вполне себе решение, для МК - плохое решение (по крайней мере в моих задачах). PS: И вопрос вообще был не об этом. Это был только частный пример. Есть куча других случаев, где невозможно решение за счёт буфера в ОЗУ. Quote Ответить с цитированием Share this post Link to post Share on other sites
mantech 0 Posted January 11 · Report post 1 час назад, jcxz сказал: Попробуйте дать "свой поток" каждому устройству, висящему на общей шине. Например - каждому из устройств висящих на единой SPI или единой I2C. Да, есть такое в моем случае ФС. Долго мудрил с разделением доступа к ней, в итоге сделал драйвер со слотами доступа каждой задачи, а ФС работает в одном потоке. 1 час назад, jcxz сказал: Например: у меня есть такой объект, как "поток отладочного вывода". Ну тут решил, что кольцевой буфер лучше разделения между потоками. Хотя согласен - у каждого свои пристрастия, я вот очень не люблю критические секции))) Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 11 · Report post 13 минут назад, mantech сказал: Да, есть такое в моем случае ФС. Долго мудрил с разделением доступа к ней, в итоге сделал драйвер со слотами доступа каждой задачи, а ФС работает в одном потоке. Ну вот, когда какая-то из этих задач, обращается к вашей ТС с запросом блокирующего чтения, а в это время выполняется подобный запрос от другой задачи, то вот тут и потребуется мьютекс. Цитата Ну тут решил, что кольцевой буфер лучше разделения между потоками. Хотя согласен - у каждого свои пристрастия, я вот очень не люблю критические секции))) Когда у вас будет низкоприоритетная задача, которая может быстро выплюнуть несколько десятков КБ в отладочный поток (например HTTPS-клиент, логгирующий процесс TLS-рукопожатия со всеми ключами, паролями и т.п.), и при этом имеются реалтайм-задачи, которым иногда изредка нужно выплюнуть пару строк в лог. И в системе нет жирной SDRAM. Вот тогда и поймёте что кольцевой буфер - это очень плохое решение. Потому как низкоприоритетную HTTPS-задачу можно безболезненно заморозить даже на секунды, не тратя почти всю драгоценную ОЗУ в МК на второстепенную задачу... Quote Ответить с цитированием Share this post Link to post Share on other sites
mantech 0 Posted January 11 · Report post 2 минуты назад, jcxz сказал: Вот тогда и поймёте что кольцевой буфер - это очень плохое решение. Очень надеюсь, что всегда будет в загашнике неск. десятков килобайт, благо МК становятся жирнее))) 3 минуты назад, jcxz сказал: (например HTTPS-клиент, логгирующий процесс TLS-рукопожатия со всеми ключами, паролями и т.п.) 3 минуты назад, jcxz сказал: И в системе нет жирной SDRAM Ну это очень жесткие условия((( Quote Ответить с цитированием Share this post Link to post Share on other sites
jcxz 0 Posted January 11 · Report post 1 минуту назад, mantech сказал: Очень надеюсь, что всегда будет в загашнике неск. десятков килобайт, благо МК становятся жирнее))) Посмотрите на цены этих "жирных МК". И попробуйте потом обосновать, почему в простейшее устройство, для которого хватит МК за пару $, вы заложили в 10 раз более дорогой, только потому что ваше решение отладочного лога не лезло в память "не жирного" МК. И сделали конечный продукт из-за этого значительно дороже, чем можно было. Quote Ответить с цитированием Share this post Link to post Share on other sites
mantech 0 Posted January 11 · Report post 6 минут назад, jcxz сказал: и при этом имеются реалтайм-задачи, которым иногда изредка нужно выплюнуть пару строк в лог При вашей реализации лог будет представлять собой кашу-малашу, когда в процесс вывода ТLS будут случайным образом вклиниваться другие процессы и выплевывать что-то свое... 2 минуты назад, jcxz сказал: для которого хватит МК за пару $, вы заложили в 10 раз более дорогой, только потому что ваше решение отладочного лога не лезло в память "не жирного" МК. Сложный TLS можно отладить на жирном МК, потом готовый блок перенести на простой, сколько раз так делал выручало очень, ИМХО. Quote Ответить с цитированием Share this post Link to post Share on other sites