ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Пятница
29 марта
556482 Топик полностью
fk01234 (30.10.2014 12:13, просмотров: 1) ответил Скрипач на Это улучшение имеет смысл только для "батареечных" решений. Но там имеет смысл идти дальше - выворачивать все автоматы на изнанку и выстраивать все вокруг "очереди сообщений". Нет сообщений - спим.
Я щас как раз работаю с проектом, не на микроконтроллерах, в котором есть очередь... это нечто. Если быстро жать кнопки на клаве (а они тоже попадают в эту очередь), то можно получить дедлок (ждёт не полной очереди на засписи заняв мьютекс) на ровном месте. А по другому и не сделаешь! Или нужно гарантировать, что обработка сообщений из очереди ведётся _всегда_ быстрей, чем поступают события. В большинстве случаев это не гарантировать Я против очередей, особенно для настоящего ембеддеда. Нужен не шаблон подписчика, а шаблон наблюдателя. Когда обработчик вызывается не по факту каждой клавиши, а по факту, что вообще клавишу нажали (хоть 1, хоть 1000 раз -- вызван будет один раз, если после вызова ещё раз нажмут -- тогда следующий раз). Т.е. есть сигнал о том, что состояние наблюдаемого объекта изменилось, но не говориться ни ничеого о новом состоянии, ни о том, что оно могло многократно измениться. А наблюдатель должен сам, после вызова обработчика, проверить состояние и принять меры. На примере клавиатуры -- должно для неё быть отдельное fifo (а не общая для всех очередь сообщений), переполнение которого не проблема. Кому-то часто и не нужно fifo, т.к. актуально только текущее состояние, или если предполагается, что обработка событий гарантированно быстрей скорости поступления только одного данного события (а не всех событий в системе, как в примере с очередью). Вот что надо. По этой же причине мне не нравится state-machines.com (бывший quantum leaps). Решение через очередь -- это решение задачи в лоб, совершенно неподходящим и "привычным" средством, то чем обычно занимаются windows-программисты привыкшие к бесконечным ресурсам компьютера. А для предлагаемого мною варианта обычно в ОС (не виндовс, RTOS) какого-то прямого аналога. Event Flags разве что. Но для последнего нет механизма вызова обработчиков (делай вручную) и число флагов весьма лимитировано. В голову приходит только libevent и сокеты или eventfd на линуксе -- там именно об изменении соостояния (пришли данные в сокет) идёт нотификация, а не каждый байт или пакет отдельным сообщением. Вот что на мой взгляд нужно в embedded. Только с той разницей, что файл-дескрипторы и тем более сокеты слишком тяжёлая штука (fifo нужно, но не всегда), кроме eventfd. И в embedded их нужно сотни и тысячи, на каждое мельчайшее дискретное событие. И как-то нужно идентифицировать события. В случае linux это open() и далее передать всем интересующимся номер файла. В той системе, с которой я сейчас работаю -- blablabla.get_unique_event_id() и то же самое -- передать. Это ад! (поэтому для ряда событий есть фиксированные номера). Это очень неудобно, это нереально, это значит отправитель события должен знать получателя, чтоб передать ему event id. Архитектура ПО превращается в дикий монолит, где выпилить отдельный компонент почти нереально -- всё развалится. Хотя казалось бы зачем это всё в embedded, где вся задача ограничена одним адресным пространством. Для которого Страуструп гарантирует, что все различные объекты как минимум имеют различные же адреса. Если источник события создаётся статически, а для embedded это так на 99%, то его и идентифицировать можно в момент компиляции -- по адресу. И таким образом наблюдатель может сказать планировщику (что-то наподобии libevent), мол хочу нотификации этих событий. Увы и нет, нет сейчас никаких аналогов. Нужно делать самому. Идею я описал. Даже могу сказать, как примерно делать: нужна priority queue которой будут храниться нотификации о произошедших событиях (дубли не допускаются). Первое идёт в обработку (исключается из списка). Нужны списки обработчиков -- планировщик по ним вызывает каждый обработчик (и в это время может получить кучу новых событий в очередь, впрочем и из прерываний может). Списки прикреплены к источнику события изначально (какой-то статический объект в памяти, его адрес -- id события). Дескриптор каждого обработчика помимо адреса функции и void* содержит значение счётчика событий данного типа -- само событие (его объект) имеет такой счётчик и он увеличивается каждый раз при добавлении события в очередь. А при запуске обработчика переписывается в его дескриптор. Для чего это нужно: планировщик должен пропускать (при проходе по списку) обработчики чей счётчик равен таковому для события (это возникает в случае, когда планировщик обрабатывает список и кто-то подписывается на события -- при подписке счётчик копируется из события) -- чтоб не получать события произошедшие до "подписки". Внутри списка обработчики отсортированы по приоритету (двухсвязный список, чтоб быстро включать/исключать), максимальный приоритет для списка хранится в объекте события и таким образом, в priority queue события отсортированы по приоритету обработчика (после вызова обработчика, если у остальных приоритет ниже -- можно не исключать из очереди событий, а исключить из головы и тут же вставить согласно новому, более низкому приоритету). Как-то так. Для реализации нужны только критические секции или мьютексы (если в рамках ОС). Возможно неплохо, чтоб планировщик умел вытеснение и запуск обработчиков с более высоким приоритетом. Тогда стек один (на чём экономия) и масса проблем с синхронизацией в многозадачности -- по-моему это не очень нужно и для того следует использовать обычные потоки RTOS. А событийно ориентированную систему уместить в отдельном потоке (и в ней 99% логики ПО, другие потоки -- для каких-то очень узких задач, где очень нужен отдельный поток, а не поток на каждый чих). Надеюсь понятно объяснил...