Перейти к содержанию
    

Что это? :) Мьютексы?

Работа с массивами - дело долгое. От многозадачности мало что останется, пмсм.

Да, мутексы. Тут ведь дело не в ОС/не ОС. А в логике работы. Если две разные задачи должны одновременно работать с одним массивом, то это проблемы в логике. Вероятнее, нужно так: одна задача готовит данные в массиве, вторая - обрабатывает. При таком подходе мутекс вполне оправдан - он позволяет второй задаче подождать, пока первая задача подготовит данные.

А в случае топикстартера всё похоже ещё проще - с массивом работает одна задача. Просто массив надо вынести из стека задачи. В этом случае можно и без мутекса.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Работа с массивами - дело долгое. От многозадачности мало что останется, пмсм.

 

Фактически дело обстоит так.

Имеется очередь (OS::channel) из элементов структур (в структуре массив 250 байт + еще несколько переменных).

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

Очередь нужна, чтобы "копить" команды, т. к., команды могут выполняться достаточно долго, а терять их очень нежелательно.

Ну и чтобы поместить элемент в очередь (push) и извлечь его оттуда (pop) приходится заводить временный элемент (тип и размер - выше), который и отжирает стек.

Сама очередь - глобальная.

 

Ну, может, архитектура не совсем нормальная... посоветуйте, пожалуйста, как лучше :)

 

А в случае топикстартера всё похоже ещё проще - с массивом работает одна задача. Просто массив надо вынести из стека задачи. В этом случае можно и без мутекса.

 

Да, с массивом работает одна задача. Но их как минимум два :)

Принцип действия следующий.

 

Процесс 1 (главный, дает команды): команда -> функция подготовки команды и помещения ее в очередь (помещает данные в свой локальный буфер 1, а из него в очередь) -> очередь.

Процесс 2 (подчиненный выполняет команды): функция извлечения команды из очереди в буфер 2 и обработки команды.

 

Команды занимают приличный объем и имеют разный размер. Буферы 1 и 2 - одного типа, под все команды (универсальные).

Проблема в том как поместить в очередь команду, не используя для этого промежуточный буфер 1.

 

Не надо так. Не только стеки, все внутренние структуры scmRTOS полетят во внешнюю память. Вообще всё. С соответствующим замедлением. Во внутренней останется только «стек main()», который и использовать-то толком тяжело.

 

Вот так вот изначально было сделано... фактически во внутренних 8 кБ остался только стек main().

Кстати, возвращаясь к вопросу об устройстве стеков при использовании ОС. После старта ОС стек main() не используется (планировщик при передаче управления заносит в указатель стека адрес стека требуемого процесса, который может быть расположен где угодно)?

 

А почему стек main() использовать тяжело?

 

Перенести только нужное во внешнюю память можно так http://electronix.ru/forum/lofiversion/index.php/t47714.html

 

Спасибо, почитаем.

Как я понял, надо осваивать скриптописание для линкера? :)

В двух словах, создаем специальные секции, в которые по необходимости переносим некоторые переменные, а эти секции линкуются куда надо (в данном случае во внешнюю память)?

 

В scmRTOS 4.0 появился механизм для вычисления используемого каждым процессом стека (см. пример 4-Debug). Включите его, погоняйте. Узнаете, сколько чего и где. Может быть, всё спокойно влезет во внутреннее ОЗУ.

 

Очень интересно :)

 

Почитайте про линкерные скрипты. Примеры используемых avr-gcc скриптов находятся в lib\ldscripts.

 

Спасибо, почитаем, главное - понять что там написано :) Пока линкерные скрипты для меня страшны :(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Во внутренней останется только «стек main()», который и использовать-то толком тяжело.

Можно наверное в нём что-то расположить, используя указатели: адрес ведь известен.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может, ляпну глупость, но, разве в очередь (message) обязательно пихать сам массив (структуру), а не указатель на него? В RTOS вроде, так делается?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Процесс 1 (главный, дает команды): команда -> функция подготовки команды и помещения ее в очередь (помещает данные в свой локальный буфер 1, а из него в очередь) -> очередь.

Процесс 2 (подчиненный выполняет команды): функция извлечения команды из очереди в буфер 2 и обработки команды.

Дык, помещайте в очередь не сам буфер, а его номер:) (Ну или как правильно сказал ViKo, указатель на него).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно наверное в нём что-то расположить, используя указатели: адрес ведь известен.

 

А, ну это да, можно...

 

Дык, помещайте в очередь не сам буфер, а его номер:) (Ну или как правильно сказал ViKo, указатель на него).

 

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

Процесс 1 помещает команду в массив, указатель на команду в массиве помещает в очередь команд (она теперь маленькая по объему).

Процесс 2 извлекает из очереди указатель на команду и по нему получает доступ к данным команды.

Так?

Но тогда придется как-то учитывать процессу 1, что в массиве по определенным адресам уже есть команды, чтобы не затереть ничего.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но тогда придется как-то учитывать процессу 1, что в массиве по определенным адресам уже есть команды, чтобы не затереть ничего.
Вот тут на помощь и приходит куча. Процесс 1 запрашивает у менеджера кучи область памяти нужного размера (а не максимального), получает указатель, заполняет память по этому указателю, после чего помещает указатель в очередь и забывает про него. Процесс 2 достает указетель из очереди, обрабатывает данные и дает менеджеру кучи команду освободить память. Поскольку команды у вас обрабатываются строго по порядку - проблема фрагментации кучи не возникнет. Забота "о том, чтобы ничего не затереть" ложится на менеджер кучи. Выкладывал тут на форуме переделанный под scmRTOS (завернутый в мутекс и переложенный на C++) легковесный менеджер кучи имени zltigo.

 

Кстати, при словах "Команды занимают приличный объем и имеют разный размер" приходят на ум умные слова "полиморфизм" и "кормление слона в примере 3-channel". Т.е. все команды можно сделать потомками одного абстрактного класса с виртуальной функцией execute(), которую и будет вызывать по извлеченному из канала указателю Процесс 2. И тогда команды очень легко и красиво создаются при помощи оператора new() и забирают из кучи ровно столько памяти, сколько необходимо. И заботу о необходимом каждой конкретной команде объеме памяти полностью берет на себя компилятор. А освобождается память после выполнения execute() оператором delete(). И при таком подходе компилятор же будет следить за тем, чтобы вы не забыли написать обработку для какой-либо команды.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот тут на помощь и приходит куча. Процесс 1 запрашивает у менеджера кучи область памяти нужного размера (а не максимального), получает указатель, заполняет память по этому указателю, после чего помещает указатель в очередь и забывает про него. Процесс 2 достает указетель из очереди, обрабатывает данные и дает менеджеру кучи команду освободить память. Поскольку команды у вас обрабатываются строго по порядку - проблема фрагментации кучи не возникнет. Забота "о том, чтобы ничего не затереть" ложится на менеджер кучи. Выкладывал тут на форуме переделанный под scmRTOS (завернутый в мутекс и переложенный на C++) легковесный менеджер кучи имени zltigo.

 

Кстати, при словах "Команды занимают приличный объем и имеют разный размер" приходят на ум умные слова "полиморфизм" и "кормление слона в примере 3-channel". Т.е. все команды можно сделать потомками одного абстрактного класса с виртуальной функцией execute(), которую и будет вызывать по извлеченному из канала указателю Процесс 2. И тогда команды очень легко и красиво создаются при помощи оператора new() и забирают из кучи ровно столько памяти, сколько необходимо. И заботу о необходимом каждой конкретной команде объеме памяти полностью берет на себя компилятор. А освобождается память после выполнения execute() оператором delete(). И при таком подходе компилятор же будет следить за тем, чтобы вы не забыли написать обработку для какой-либо команды.

 

Принцип понятен, спасибо большое за рекомендации.

Но как же мнение о том, что динамическая память для AVR - это слишком ресурсоемко и не выгодно?

Кстати, в WinAVR уже есть менеджер кучи и чем он отличается от выше Вами указанного?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но как же мнение о том, что динамическая память для AVR - это слишком ресурсоемко и не выгодно?

Опять мифы!!!

Аналогичные мифы (применительно к МК) о C++, printf/scanf, RTOS и прочая циркулируют в эмбеддерской среде с пугающей живучестью.

Подумайте над следующим. Чьи ресурсы занимаются? Контроллера или лично Ваши (рабочее время, деньги)? Ваши затраты будут единоразовыми и неизвестно в каком случае больше - применив кучу или изголяясь окольными путями. В случае контроллера, если он решает поставленную задачу и решает успешно, то какая Вам разница? Попробуйте лично и определитесь.

 

PS Когда не устраивает библиотечный вариант какой-либо фичи, напишите или используйте кем-нибудь написанный готовый - облегчённый. Что касается варианта кучи, то Вам уже предложили или возьмите из второй версии scmRTOS.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но как же мнение о том, что динамическая память для AVR - это слишком ресурсоемко и не выгодно?
А вы попробуйте. На первый взгляд ресурсоемким и невыгодным выглядит решение без менеджера кучи. Менеджеры бывают разные...

В вашем случае менеджер можно упростить до предела, сделав его на базе кольцевого буфера. Выделение памяти двигает указатель головы, освобождение - указатель хвоста. Вы обещаете освобождать память строго в том же порядке, в котором она была выделена (а в случае вашей очереди это так и получается) и за счет этого менеджеру не нужны поиск свободного блока нужного размера, "склеивание" освобожденных блоков и т.д. А чтобы оставить возможность в дальнейшем для каких-то других целей использовать "настоящий" менеджер - можно обращаться к этому через переопределенный оператор new.

 

Кстати, в WinAVR уже есть менеджер кучи и чем он отличается от выше Вами указанного?
Не знаю. Я этот менеджер использовал на ARM, где родной библиотечный тянет за собой мама дорогая сколько лишнего. Менеджер из avr-libc я не использовал. Сравните, будет интересно узнать результаты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Вряд ли такую строго упорядоченную конструкцию можно назвать кучей. Скорее уж, действительно, кольцевой буфер. Тогда к чему лишние процедуры выделения, освобождения...?

Я понимаю, работа с этими массивами идет постоянно. Вот и сделать их глобальными. Иметь некий индекс текущего рабочего буфера, им и манипулировать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кстати, насколько прожорлив sprintf? Сколько он потребляет стека? Использует ли он кучу?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кучу - нет. По стеку можно посмотреть его исходники, порядка 20-30 байт плюс на передачу параметров (это относится только к sprintf из avr-libc, естественно).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кучу - нет. По стеку можно посмотреть его исходники, порядка 20-30 байт плюс на передачу параметров (это относится только к sprintf из avr-libc, естественно).

 

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

Да, вопрос про avr-libc :)

Кстати, в WinAVR есть эти исходники (сейчас нет под рукой посмотреть, к сожалению)?

Наверное, в avr-libc sprintf жутко оптимизированный под AVRы? :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кстати, в WinAVR есть эти исходники (сейчас нет под рукой посмотреть, к сожалению)?
В WinAVR нет. Лежат отдельно в репозитории avr-libc.

Наверное, в avr-libc sprintf жутко оптимизированный под AVRы? :)
Да.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...