====== TNKernel : Очереди сообщений ====== ===== Введение ===== //Очередь сообщений// - это объект RTOS, предназначенный для передачи данных между задачами и между прерываниями и задачами. В **не** RTOS системе передача данных между различными подпрограммами, которые вызываются в бесконечном цикле (условно их можно назвать задачами), происходит, как правило, с помощью глобальных переменных. Однако в многозадачной системе использование глобальных переменных крайне не рекомендуется - так как вытеснение может произойти в любой момент, необходимо реализовать для доступа к глобальной переменной критическую секцию, что может привести к значительной трате ресурсов. Поэтому передача данных в RTOS как правило реализуется с помощью специальных объектов - очередей сообщений. Кроме защиты сообщений, объект реализует //очередь//, то есть возможность передать несколько сообщений, которые могут быть обработаны принимающей стороной позднее. //Очередь сообщений// ассоциируется со структурой управления и буфером сообщений. Буфер является очередью типа FIFO, т.е. первым будет получено сообщение, которое отправлено раньше. В TNKernel под сообщением (т.е. элементом, который хранится в буфере) понимается **указатель на данные**, а не сами данные. Другими словами - сообщение это не данные, а указатель на данные. Такой подход имеет как плюсы, так и минусы. __Плюсы:__ * объем передаваемых данных может быть сколь угодно большим - в качестве сообщения можно передавать указатель на структуру или массив * маленький размер буфера, так как в нем хранятся только указатели на данные __Минусы:__ * данные по передаваемому указателю не могут быть изменены до тех пор, пока сообщение не будет принято и обработано. Это можно обойти, используя пересылку с подтверждением, либо блоки памяти фиксированного размера. Тем не менее, в качестве сообщения можно передавать не указатель, а например, код команды - для этого необходимо явно привести типы при передаче параметров в сервисы отсылки и приема сообщения. Основными сервисами управления очередями сообщений являются отсылка сообщения и прием сообщения. Если задача вызывает сервис приема сообщения, а в буфер очереди пуст, задача переводится в состояние ожидания, до тех пор, пока сообщение не будет отправлено другой задачей или пока не истечет таймаут. В TNKernel будет запущена задача, которая стоит первой в очереди задач ожидающих сообщение, вне зависимости от приоритетов задач в очереди. Если задача вызывает сервис передачи сообщения, а буфер сообщения полон, то она переводится в состояние ожидания до тех пор, пока хотя бы одно сообщение не будет принято. ~~UP~~ ===== Структура управления очередью сообщений ===== Каждая //очередь сообщений// ассоциируется со структурой управления: typedef struct _TN_DQUE_S { CDLL_QUEUE_S wait_send_list; CDLL_QUEUE_S wait_receive_list; void ** data_fifo; TN_UWORD num_entries; TN_UWORD tail_cnt; TN_UWORD header_cnt; TN_OBJ_ID id_dque; } TN_DQUE_S; В состав структуры очереди входят следующие элементы: {| class = "fpl" |- | ''wait_send_list'' | Очередь задач, посылающих сообщение |- | ''wait_receive_list'' | Очередь задач, принимающих сообщение |- | ''data_fifo'' | Указатель на буфер сообщений. Буфер сообщений это массив указателей на ''void'' |- | ''num_entries'' | Объем буфера (максимальное количество сообщений в очереди) |- | ''tail_cnt'' | Индекс принимаемого сообщения |- | ''header_cnt'' | Индекс отсылаемого сообщения |- | ''id_dque'' | Поле идентификации объекта как очереди сообщений |} Структура //очереди сообщений// доступна только при определении ''TN_DEBUG''. Тем не менее, прямой доступ к элементам структуры крайне не рекомендуется, так как это является вмешательством в работу планировщика и других сервисов RTOS. ~~UP~~ ===== Сервисы управления очередями сообщений ===== TNKernel имеет следующий набор функций (сервисов) для управления очередями сообщений: ^ Сервис ^ Описание ^ Свойства ^ | **Создание и удаление очереди сообщений** ||| | \ \ [[tnkernel:ref:dqueue:tn_queue_create|tn_queue_create()]] | Создание очереди | {{tnkernel:ref:attr_call_task_and_int.png|Разрешен вызов в контексте задачи и в прерывании}} | | \ \ [[tnkernel:ref:dqueue:tn_queue_delete|tn_queue_delete()]] | Удаление очереди | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Отсылка сообшения** ||| | \ \ [[tnkernel:ref:dqueue:tn_queue_send|tn_queue_send()]] | Отсылка сообшения | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} {{tnkernel:ref:attr_call_to.png|Сервис использует таймаут}} | | \ \ [[tnkernel:ref:dqueue:tn_queue_send_polling|tn_queue_send_polling()]] | Отсылка сообщения без блокировки задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:dqueue:tn_queue_isend_polling|tn_queue_isend_polling()]] | Отсылка сообщения из прерывания| {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Прием сообщения** ||| | \ \ [[tnkernel:ref:dqueue:tn_queue_receive|tn_queue_receive()]] | Прием сообщения | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} {{tnkernel:ref:attr_call_to.png|Сервис использует таймаут}} | | \ \ [[tnkernel:ref:dqueue:tn_queue_receive_polling|tn_queue_receive_polling()]] | Прием сообщения без блокировки задачи| {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:dqueue:tn_queue_ireceive|tn_queue_ireceive()]] | Прием сообщения в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | ~~UP~~