====== 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~~