Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Небольшая проблема с обработкой сообщений
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
sonycman
Доброго времени суток!

Организую взаимодействие между двумя процессами с помощью сообщений.
Один процесс отправляет сообщение, другой - принимает.

Почему то казалось, что после считывания сообщения оператором = его статус автоматически устанавливается на "пусто".
Поэтому делал так:
CODE

//thread 1
command_message.send();
response_message.wait();
...

//thread 2
if (command_message.is_non_empty())
{
message_placeholder = command_message;
...
response_message.send();
...
}

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

Но, может быть, удобнее было бы сделать автоматический сброс флага NonEmpty после отрабатывания оператора присваивания?
dxp
Цитата(sonycman @ Jan 27 2011, 01:33) *
Доброго времени суток!

Организую взаимодействие между двумя процессами с помощью сообщений.
Один процесс отправляет сообщение, другой - принимает.

Почему то казалось, что после считывания сообщения оператором = его статус автоматически устанавливается на "пусто".
Поэтому делал так:

...

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

Но, может быть, удобнее было бы сделать автоматический сброс флага NonEmpty после отрабатывания оператора присваивания?

Не очень понял, что не получалось. Логика работы OS::message (как и OS::TEventFlag) такова, что внутренний флаг NonEmpty взводится только в случае, если посылается сообщение, которое еще никто не ждет. В этом случае, когда какой-то процесс встанет на ожидание сообщения, он сразу обнаружит, что оно уже послано - по этому внутреннему флагу, в этом случае флаг NonEmpty автоматически сбрасывается. Если при вызове OS::message::send уже были ожидающие этого сообщения процессы, то эти процессы переводятся в состояние готовых к выполнению, а внутренний этот флаг NonEmpty не устанавливается - в этом просто нет необходимости - кто ждал, те и так получат управление в очередности, соответствующей их приоритетам.

Таким образом, вам достаточно было дождаться сообщения с помощью функции OS::message::wait, после этого спокойно можно читать тело сообщения. Никаких проверок (is_non_empty и т.п.) не нужно. Это штатный способ использования.
sonycman
Цитата(dxp @ Jan 27 2011, 07:52) *
Таким образом, вам достаточно было дождаться сообщения с помощью функции OS::message::wait, после этого спокойно можно читать тело сообщения. Никаких проверок (is_non_empty и т.п.) не нужно. Это штатный способ использования.

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

Поэтому делается проверка на наличие сообщения функцией is_non_empty(), и далее обработка сообщения только в случае его наличия.

То есть получается такая ситуация:
1. низкоприоритетный процесс отправляет сообщение высокоприоритетному. Последний в это время ожидает другое событие и не может обработать сообщение, поэтому внутри send() устанавливается флаг NonEmpty.
2. процесс получатель высвобождается и проверяет наличие сообщения, а затем вычитывает его оператором =.
3. Приходится вызовом reset() сбрасывать установленный флаг.

Можно ли избежать третьего пункта, добавив в обработчик оператора = сброс NonEmpty?
dxp
Цитата(sonycman @ Jan 27 2011, 11:41) *
Да, как работает функция wait() я понял, и к ней претензий нет.
Но дело в том, что в моём случае обрабатывающий сообщение процесс - высокоприоритетный и весьма загруженный, и вставать на ожидание функцией wait() нет возможности.

Поэтому делается проверка на наличие сообщения функцией is_non_empty(), и далее обработка сообщения только в случае его наличия.

То есть получается такая ситуация:
1. низкоприоритетный процесс отправляет сообщение высокоприоритетному. Последний в это время ожидает другое событие и не может обработать сообщение, поэтому внутри send() устанавливается флаг NonEmpty.
2. процесс получатель высвобождается и проверяет наличие сообщения, а затем вычитывает его оператором =.
3. Приходится вызовом reset() сбрасывать установленный флаг.

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

Цитата(sonycman @ Jan 27 2011, 11:41) *
Можно ли избежать третьего пункта, добавив в обработчик оператора = сброс NonEmpty?

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

Мне кажется, что у вас тут на уровне проектирования не совсем ладно. У вас получается, что процесс может одновременно ждать несколько сообщений, тогда в этом случае целесообразно применять не message, а channel. Тогда процесс просто ждет любого сообщения из канала, как только оно пришло, вычерпывает его оттуда и обрабатывает. А источники сообщений в своем темпе пихают сообщения в канал. А message хорошо подходит для оповещения и для передачи по схеме источник-приемник, в обоих случаях приемники должны ожидать сообщения. message менее функциональный, но и менее накладный способ коммуникации.
sonycman
Цитата(dxp @ Jan 27 2011, 11:50) *
Мне кажется, что у вас тут на уровне проектирования не совсем ладно. У вас получается, что процесс может одновременно ждать несколько сообщений, тогда в этом случае целесообразно применять не message, а channel. Тогда процесс просто ждет любого сообщения из канала, как только оно пришло, вычерпывает его оттуда и обрабатывает. А источники сообщений в своем темпе пихают сообщения в канал.

Ну что же, Вам виднее.

А мой процесс занимается загрузкой данных в память и передачей их по DMA.
В качестве сигнала готовности DMA использую event flag. Не дай бог сильно затянуть с реакцией на загрузку DMA, а если тут под ногами ещё будет путаться второстепенное сообщение...
Думаю, что канал в этом случае лишний.
dxp
Цитата(sonycman @ Jan 27 2011, 17:03) *
Ну что же, Вам виднее.
...
Думаю, что канал в этом случае лишний.

Если напрягает ручной вызов двух функций, то ничего не мешает написать короткую встраиваемую функцию, которая будет объединять в себе проверку на наличие уже принятого сообщения и сброс внутреннего флага. И эту функцию одну в коде и вызывать. Весь интерфейс сервисы предоставляют, пользователь волен комбинировать как ему желается под свои конкретные цели и предпочтения. По реализации будет совершенно без разницы, как сбрасывается внутренний флаг - внутри функции-оператора или снаружи инлайновой функцией reset. А побочного эффекта во втором случае не будет.
Сергей Борщ
Если не используется ожидание сообщения, то какой смысл в применении сообщения вообще?
sonycman
Цитата(Сергей Борщ @ Jan 28 2011, 12:23) *
Если не используется ожидание сообщения, то какой смысл в применении сообщения вообще?

Управление процессом. Передача комманд\ответов управляющему процессу.
Очень удобно, так как формат сообщений произвольный, и может содержать сложную структуру.

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

По моему вполне логично.
dxp
Цитата(sonycman @ Jan 28 2011, 17:56) *
Управление процессом. Передача комманд\ответов управляющему процессу.
Очень удобно, так как формат сообщений произвольный, и может содержать сложную структуру.

Кстати, да, мне вот такая простая мысль в голову не пришла. Действительно, если мы сообщения не ждем, а опрашиваем, то для этого достаточно просто завести просто структуру, которая является телом сообщения, и флажок готовности (можно прямо в теле структуры). И поллить периодически этот флажок. Использование сервисов ОС тут вообще не нужно. Такое решение будет еще эффективнее во всех смыслах.
sonycman
Цитата(dxp @ Jan 28 2011, 15:48) *
Кстати, да, мне вот такая простая мысль в голову не пришла. Действительно, если мы сообщения не ждем, а опрашиваем, то для этого достаточно просто завести просто структуру, которая является телом сообщения, и флажок готовности (можно прямо в теле структуры). И поллить периодически этот флажок. Использование сервисов ОС тут вообще не нужно. Такое решение будет еще эффективнее во всех смыслах.

Да, можно сделать общение процессов и не используя ОС.
Если самому позаботиться о синхронизации и совместном доступе.

Но, осваивая свою первую в жизни ОС, всё таки хочется пользоваться её сервисами sm.gif
dxp
Цитата(sonycman @ Jan 28 2011, 19:41) *
Но, осваивая свою первую в жизни ОС, всё таки хочется пользоваться её сервисами sm.gif

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

Цитата(sonycman @ Jan 28 2011, 19:41) *
Да, можно сделать общение процессов и не используя ОС.
Если самому позаботиться о синхронизации и совместном доступе.

Без ОС организация поллинга будет совсем не такой простой и не такой эффективной. Тут-то у вас есть отдельный процесс, который по кругу выполняет ряд задач, одна из которых - опрос. Опросил, если есть, работаем, если нет, пошли дальше - то, что вам, как я понял, и надо. Без ОС придется повозиться с организацией как общего потока управления, так и частного кода, осуществляющего поллинг.
sonycman
Цитата(dxp @ Jan 28 2011, 16:55) *
Без ОС придется повозиться с организацией как общего потока управления, так и частного кода, осуществляющего поллинг.

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

А вот нелогичность с вычитыванием сообщения и несбросом флага "пусто", как Вы объяснили, это жертва того, что одно сообщение могут ожидать несколько процессов одновременно.
Тут бы, в идеале, для каждого процесса должно быть своё собственное сообщение, так как отдельные процессы знать не знают и не хотят про другие...
Сергей Борщ
QUOTE (sonycman @ Jan 28 2011, 16:09) *
Тут бы, в идеале, для каждого процесса должно быть своё собственное сообщение, так как отдельные процессы знать не знают и не хотят про другие...
В следующей версии вы сможете написать такой сервис самостоятельно.
dxp
Я все-таки не понимаю, почему нельзя просто дописать свой код, который нужен. Или функцию, которая делает опрос и сброс, или, если уж так хочется работать непосредственно с объектом, отнаследоваться от message, добавить там свой operator=(), который будет сбрасывать внутренний флаг и выдавать наружу тело сообщения, как штатный оператор.

Сервисы ОС должны иметь минимальный функционал и интерфейс, позволяющий пользователю реализовать то, что тому нужно.
sonycman
Мне просто было интересно, почему авторы не предусмотрели сброс флага.
Теперь это понятно.
Спасибо за оперативную помощь.

Что касается модификации - к сожалению, я не смог с наскоку добавить сброс NonEmpty прямо в тело функции-оператора "=" так, чтобы не вызвать ошибки компилятора.
А дальше пока не стал копать, в принципе, вполне устраивает и так sm.gif
dxp
Цитата(sonycman @ Jan 29 2011, 15:15) *
Что касается модификации - к сожалению, я не смог с наскоку добавить сброс NonEmpty прямо в тело функции-оператора "=" так, чтобы не вызвать ошибки компилятора.
А дальше пока не стал копать, в принципе, вполне устраивает и так sm.gif

Можно попробовать как-то так:
Код
template <typename T>
class my_message : public OS::message<T>
{
public:
    INLINE const T& operator=(const T& msg) { return OS::message<T>::operator=(msg); }
    INLINE operator T()
    {
        reset();
        return OS::message<T>::operator T();
    }  
};
...
my_message<int> Slonick;

...
    // process 1
    Slonick = 10;
    Slonick.send();

...
    // process 2
    if(Slonick.is_non_empty())
    {
        A = Slonick; // чтение тела сообщения со сбросом внутреннего флага
    }
sonycman
Цитата(dxp @ Jan 29 2011, 14:36) *
Можно попробовать как-то так:

Спасибо! a14.gif

А я пробовал просто вставить функцию внутрь класса message вот так:
Код
INLINE operator     T() const       { TCritSect cs; reset(); return Msg; }

на что получаю такую ошибку:
Цитата
Error[Pe1086]: the object has cv-qualifiers that are not compatible with the member function "OS::message<T>::reset"

Вроде бы наследующий класс должен получать доступ ко всем public членам наследуемого?

ЗЫ: в Вашем примере будет дважды формироваться критическая секция? Сначала внутри reset(), а затем самим оператором Т ?
dxp
Цитата(sonycman @ Jan 29 2011, 21:16) *
А я пробовал просто вставить функцию внутрь класса message вот так:
Код
INLINE operator     T() const       { TCritSect cs; reset(); return Msg; }

на что получаю такую ошибку:

Это потому, что фукнция объявлена с квалификатором const - в этой функции все члены класса рассматриваются как константные и изменять представление класса нельзя. А функция reset его изменяет. Надо const тут убрать.

Цитата(sonycman @ Jan 29 2011, 21:16) *
Вроде бы наследующий класс должен получать доступ ко всем public членам наследуемого?

К пабликам имеют доступ все. Производные имеют доступ еще к защищенным (protected).

Цитата(sonycman @ Jan 29 2011, 21:16) *
ЗЫ: в Вашем примере будет дважды формироваться критическая секция? Сначала внутри reset(), а затем самим оператором Т ?

Да. Лишние секции можно, очевидно, убрать.

Upd. Не сразу вник в вопрос. Критические секции будут формироваться внутри функций, и это необходимо для их корректной работы. Вложенных секций нет (я сначала про это подумал - протупил), две секции, каждая в своем коде (функции), это нормально.
sonycman
Цитата(dxp @ Jan 31 2011, 11:13) *
Upd. Не сразу вник в вопрос. Критические секции будут формироваться внутри функций, и это необходимо для их корректной работы. Вложенных секций нет (я сначала про это подумал - протупил), две секции, каждая в своем коде (функции), это нормально.

А, может быть, тогда сменить статус NonEmpty на protected, убрать из функции квалификатор const и обнулять эту переменную напрямую, а не с помощью reset().

Это наиболее оптимальный способ, с генерированием только одной критич. секции.
dxp
Цитата(sonycman @ Jan 31 2011, 19:19) *
А, может быть, тогда сменить статус NonEmpty на protected, убрать из функции квалификатор const и обнулять эту переменную напрямую, а не с помощью reset().

Это наиболее оптимальный способ, с генерированием только одной критич. секции.

Секция все равно останется - просто она выйдет на уровень этого оператора (определенного в наследнике), сбрасывающего NonEmpty. При обращении к представлению объектов сервисов должна быть обеспечена атомарность доступа, поэтому без критической секции не обойтись.
sonycman
Цитата(dxp @ Jan 31 2011, 17:53) *
Секция все равно останется - просто она выйдет на уровень этого оператора (определенного в наследнике), сбрасывающего NonEmpty. При обращении к представлению объектов сервисов должна быть обеспечена атомарность доступа, поэтому без критической секции не обойтись.

Конечно, останется, но только одна - которая определяется в операторе.
А в случае с использованием функции reset() - добавится вторая, совершенно не нужная, внутри этой функции.
dxp
Цитата(sonycman @ Jan 31 2011, 22:16) *
Конечно, останется, но только одна - которая определяется в операторе.
А в случае с использованием функции reset() - добавится вторая, совершенно не нужная, внутри этой функции.

Я так вижу, что мы про разное говорим. Я про свой пример:
Код
    INLINE operator T()
    {
        reset();
        return OS::message<T>::operator T();
    }  
};

Где тут дополнительная секция?
sonycman
Цитата(dxp @ Feb 1 2011, 06:21) *
Я так вижу, что мы про разное говорим. Я про свой пример:

В вашем примере критическая секция сначала формируется внутри reset(), а затем внутри оператора.
Всего два раза.
Но ведь можно сделать оптимальнее:
Код
INLINE operator     T()     { TCritSect cs; NonEmpty = false; return Msg; }

всего с одной критической секцией.
dxp
Цитата(sonycman @ Feb 1 2011, 14:10) *
В вашем примере критическая секция сначала формируется внутри reset(), а затем внутри оператора.
Всего два раза.
Но ведь можно сделать оптимальнее:
Код
INLINE operator     T()     { TCritSect cs; NonEmpty = false; return Msg; }

всего с одной критической секцией.

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

Если вас не устраивает такое решение, подождите некоторое время, как сказал Сергей, скоро будет новый релиз, в нем можно создавать пользовательские сервисы (для этого там будет документированный API), вы сможете сделать свою версию сообщения так, как вам нравится.
sonycman
Цитата(dxp @ Feb 1 2011, 15:02) *
...скоро будет новый релиз, в нем можно создавать пользовательские сервисы (для этого там будет документированный API), вы сможете сделать свою версию сообщения так, как вам нравится.

"Будем ждать, будем настороже!" (с) Капитан Смолетт. sm.gif

Особенно интересна фича по мониторингу стёка.
jorikdima
Цитата(dxp @ Jan 29 2011, 09:31) *
Я все-таки не понимаю, почему нельзя просто дописать свой код, который нужен. Или функцию, которая делает опрос и сброс, или, если уж так хочется работать непосредственно с объектом, отнаследоваться от message, добавить там свой operator=(), который будет сбрасывать внутренний флаг и выдавать наружу тело сообщения, как штатный оператор.

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

Цитата(dxp @ Feb 1 2011, 15:02) *
Если вас не устраивает такое решение, подождите некоторое время, как сказал Сергей, скоро будет новый релиз, в нем можно создавать пользовательские сервисы (для этого там будет документированный API), вы сможете сделать свою версию сообщения так, как вам нравится.

Сорри, не дочитал до конца. Судя по всему в этом направлении есть изменения! Здорово, буду ждать!
dxp
Цитата(jorikdima @ Feb 1 2011, 20:56) *
Кстати помните наш диалог про то, что неплохо было бы сделать все (большинство) методов и полей под квалификатором protected а не private, для того, чтоб наследоваться и расширять функционал класса.

Большинство функций-членов там вообще public. Приватные функции по пальцам можно пересчитать.

Цитата(jorikdima @ Feb 1 2011, 20:56) *
Вот пример, когда это было бы весьма кстати. А так товарищу формально нужно будет менять код ОС, чтоб иметь возможность отнаследоваться.

В данном случае это ничего бы не дало. Все представление, к которому нужен доступ, досягаемо через открытые функции-члены. В этом контексте имело бы смысл делать данные protected, но это как-то уже не совсем хорошо. Основной идеологический посыл в том, что классы сервисов не предназначены для непосредственного расширения. Для расширения будет предоставлен специальный механизм, на основе которого будут реализованы и штaные классы сервисов.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.