Jump to content

    
jcxz

Лёгкие мьютексы. Возможно ли?

Recommended Posts

У меня имеется своя очень лёгкая вытесняющая РТОС для Cortex-M. С минимумом функционала. Из объектов синхронизации - только семафоры и флажки (функционал подобный CreateEvent()/SetEvent()/ResetEvent() в WinAPI). Равноприоритетных задач нет (не надо). Для разделения доступа задач к ресурсам сейчас использую семафоры. Имеется проблема - инверсия приоритетов задач. Поэтому задумался о добавлении мьютексов в свой код. Мьютексов с динамическим изменением приоритетов задач. Но очень не хочется терять лёгкость ОС (малое время переключения задач, низкие траты времени на сам сервис ОС).

Посмотрел как реализованы мьютексы:

uCOS-II - не понравилось, не корректно и не удобно (для пользовательского кода);

uCOS-III - очень громоздко, длительные запреты прерывания (обход свЯзных списков в цикле в критической секции и т.п.);

scmRTOS - мьютексы не умеют динамического изменения приоритетов задач;

FreeRTOS - глубоко не разбирался, но похоже тоже громоздко как в uCOS-III.

 

Пока что-то не могу придумать лёгкий алгоритм работы с мьютексами. Может кто подскажет идею? Чтобы без циклов внутри критических секций. От мьютекса достаточно 2 функций: захват, освобождение.

Задумался - а вообще в принципе это осуществимо ли? Или если делать дин.приоритеты задач, то с лёгкостью ОС по любому нужно прощаться?

Если например брать реализацию как в uCOS-III (на свЯзных списках), то функции захвата/освобождения мьютекса становятся многократно(!) более длительными, чем существующие сейчас функции захвата/освобождения семафора (которые не умеют изменения дин.приоритета).

Share this post


Link to post
Share on other sites
5 минут назад, mantech сказал:

Для каких это может потребоваться целей? 

Написал же: Для борьбы с инверсией приоритетов задач. Читайте: https://ru.wikipedia.org/wiki/Мьютекс

Share this post


Link to post
Share on other sites
9 часов назад, jcxz сказал:

scmRTOS - мьютексы не умеют динамического изменения приоритетов задач;

именно потому что

9 часов назад, jcxz сказал:

очень громоздко, длительные запреты прерывания (обход свЯзных списков в цикле в критической секции и т.п.);

Мутексы с инверсией приоритетов подходят для толстых осей, где накладные и так большие, на их фоне пробег про объектам оси с целью корректного изменения приоритетов выглядит не так затратно. Для мелких осей накладные соизмеримы или больше затрат на непосредственную работу с объектом блокировки.

 

Задача легко решается без таких мутексов путём "делегирования" работы потоку с достаточно высоким приоритетом. Например, к объекту лезут два потока с приоритетами 3 и 8 (меньше - выше приоритет), чтобы поток с приоритетом 8 не блокировал работу потока с приоритетом 3, можно работу с объектом в потоке 8 реализовать в отдельной функции, указатель на которую метнуть в очередь специального foreground потока с приоритетом, например 1. На простой иерархии классов с виртуальными функциями получается очень просто и эффективно: заводится абстрактный базовый класс TJob, от него рожается сколько надо производных классов-заданий, при постановке задания в очередь, которую читает поток с приоритетом 1, кидается указатель на объект-задание. 

 

Точно так же реализуется обработка фоновых заданий: если есть затратные по времени, но не приоритетные задачи (какие-нить вычисления длительные), потоки не выполняют их в своём цикле, а скидывают в очередь низкоприоритетного потока.

 

Тут на стр. 125 подробно описано с примером.

Share this post


Link to post
Share on other sites
10 часов назад, jcxz сказал:

Для борьбы с инверсией приоритетов задач.

Да уж, всегда использовал мьютексы только в очень редких случаях, стараясь планировать задачи так, чтобы не было перекрестного обращения к одним и тем же устройствам...

28 минут назад, dxp сказал:

Например, к объекту лезут два потока с приоритетами 3 и 8 (меньше - выше приоритет), чтобы поток с приоритетом 8 не блокировал работу потока с приоритетом 3

Ну а если №8 уже занял устройство? №3 по любому будет ждать, хоть у него самый высший приоритет будет. Поэтому лучше метод каждому устройству свой поток. Медленное устройство прекрасно может работать по принципу мейлбокса, обслуживая фоновые запросы от неск. потоков и не блокируя их.

33 минуты назад, dxp сказал:

Точно так же реализуется обработка фоновых заданий: если есть затратные по времени, но не приоритетные задачи (какие-нить вычисления длительные), потоки не выполняют их в своём цикле, а скидывают в очередь низкоприоритетного потока.

Ну вот как раз это и имел ввиду.

Edited by mantech

Share this post


Link to post
Share on other sites
13 часов назад, AlexRayne сказал:

Если мутексы тяжелы, то может стоит задуматься как обойтись без них? какова задача?

Задача - реализовать мьютексы.

А как обходиться без них я и так знаю.

Share this post


Link to post
Share on other sites
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.

И к тому же - кроме устройств, разделяемыми ресурсами может быть ещё много чего.

Например: у меня есть такой объект, как "поток отладочного вывода". Тоже разделяемый ресурс, разделяемый почти всеми задачами проекта.

Share this post


Link to post
Share on other sites
32 минуты назад, jcxz сказал:

Попробуйте дать "свой поток" каждому устройству, висящему на общей шине. Например - каждому из устройств висящих на единой SPI или единой I2C. И к тому же - кроме устройств, разделяемыми ресурсами может быть ещё много чего. Например: у меня есть такой объект, как "поток отладочного вывода". Тоже разделяемый ресурс, разделяемый почти всеми задачами проекта.

Для разделения доступа не обязательно выделять отдельный процесс. Можно в ОС заложить примитивы синхронизации/обмена сообщениями, которые позволяют подобное делать без использования мьютексов или отдельного процесса. Например, в RExOS реализована безмьютекскная система. Для "потока отладочного вывода" применяется примитив stream. Описание тут: главы "2.3. Безмьютексная система" и "7. Синхронизация процессов"  https://raw.githubusercontent.com/alexeyk13/rexos/master/doc/RExOS.0.4.4.pdf

Share this post


Link to post
Share on other sites
19 минут назад, arhiv6 сказал:

Для разделения доступа не обязательно выделять отдельный процесс. Можно в ОС заложить примитивы синхронизации/обмена сообщениями, которые позволяют подобное делать без использования мьютексов или отдельного процесса.

Да, конечно, можно решить это за счёт большого буфера в ОЗУ. Для винды - вполне себе решение, для МК - плохое решение (по крайней мере в моих задачах).

PS: И вопрос вообще был не об этом. Это был только частный пример. Есть куча других случаев, где невозможно решение за счёт буфера в ОЗУ.

Share this post


Link to post
Share on other sites
1 час назад, jcxz сказал:

Попробуйте дать "свой поток" каждому устройству, висящему на общей шине. Например - каждому из устройств висящих на единой SPI или единой I2C.

Да, есть такое в моем случае ФС. Долго мудрил с разделением доступа к ней, в итоге сделал драйвер со слотами доступа каждой задачи, а ФС работает в одном потоке.

1 час назад, jcxz сказал:

Например: у меня есть такой объект, как "поток отладочного вывода".

Ну тут решил, что кольцевой буфер лучше разделения между потоками. Хотя согласен - у каждого свои пристрастия, я вот очень не люблю критические секции)))

Share this post


Link to post
Share on other sites
13 минут назад, mantech сказал:

Да, есть такое в моем случае ФС. Долго мудрил с разделением доступа к ней, в итоге сделал драйвер со слотами доступа каждой задачи, а ФС работает в одном потоке.

Ну вот, когда какая-то из этих задач, обращается к вашей ТС с запросом блокирующего чтения, а в это время выполняется подобный запрос от другой задачи, то вот тут и потребуется мьютекс.

Цитата

Ну тут решил, что кольцевой буфер лучше разделения между потоками. Хотя согласен - у каждого свои пристрастия, я вот очень не люблю критические секции)))

Когда у вас будет низкоприоритетная задача, которая может быстро выплюнуть несколько десятков КБ в отладочный поток (например HTTPS-клиент, логгирующий процесс TLS-рукопожатия со всеми ключами, паролями и т.п.), и при этом имеются реалтайм-задачи, которым иногда изредка нужно выплюнуть пару строк в лог. И в системе нет жирной SDRAM. Вот тогда и поймёте что кольцевой буфер - это очень плохое решение. Потому как низкоприоритетную HTTPS-задачу можно безболезненно заморозить даже на секунды, не тратя почти всю драгоценную ОЗУ в МК на второстепенную задачу...  :unknw:

Share this post


Link to post
Share on other sites
2 минуты назад, jcxz сказал:

Вот тогда и поймёте что кольцевой буфер - это очень плохое решение.

Очень надеюсь, что всегда будет в загашнике неск. десятков килобайт, благо МК становятся жирнее)))

3 минуты назад, jcxz сказал:

(например HTTPS-клиент, логгирующий процесс TLS-рукопожатия со всеми ключами, паролями и т.п.)

 

3 минуты назад, jcxz сказал:

И в системе нет жирной SDRAM

Ну это очень жесткие условия(((

Share this post


Link to post
Share on other sites
1 минуту назад, mantech сказал:

Очень надеюсь, что всегда будет в загашнике неск. десятков килобайт, благо МК становятся жирнее)))

Посмотрите на цены этих "жирных МК". И попробуйте потом обосновать, почему в простейшее устройство, для которого хватит МК за пару $, вы заложили в 10 раз более дорогой, только потому что ваше решение отладочного лога не лезло в память "не жирного" МК. И сделали конечный продукт из-за этого значительно дороже, чем можно было.

Share this post


Link to post
Share on other sites
6 минут назад, jcxz сказал:

и при этом имеются реалтайм-задачи, которым иногда изредка нужно выплюнуть пару строк в лог

При вашей реализации лог будет представлять собой кашу-малашу, когда в процесс вывода ТLS будут случайным образом вклиниваться другие процессы и выплевывать что-то свое...

2 минуты назад, jcxz сказал:

для которого хватит МК за пару $, вы заложили в 10 раз более дорогой, только потому что ваше решение отладочного лога не лезло в память "не жирного" МК.

Сложный TLS можно отладить на жирном МК, потом готовый блок перенести на простой, сколько раз так делал выручало очень, ИМХО.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.