Threadx提供了消息隊列進行線程間通信。
消息隊列中消息通常按照先進先出規則傳遞,同時提供了把消息直接存儲到隊列頭部的API。每個線程可以創建多個消息隊列,並且可以使用多個消息隊列和多個線程通信。
消息隊列不支持擁有者屬性,也就是任何線程可以向消息隊列發送或接收消息。應用開發者保證消息隊列使用的合理性。
消息傳遞規則
1,任何線程可以向一個消息隊列發送或接收消息,消息隊列不支持擁有者屬性。
2,消息隊列支持先進先出規則,函數_tx_queue_send發送消息都隊列尾部。
3,消息隊列也支持發送消息到消息頭部功能,函數_tx_queue_front_send發送消息到消息頭部。
4,如果消息隊列有掛起的接收線程,發送消息時,可以直接把消息放到接收線程的緩衝中,這可以降低消息傳遞延時。
TX_THREAD線程控制塊中tx_additional_suspend_info域用於存儲接收線程緩衝區地址。
5,如果消息隊列爲空,接收線程調用_tx_queue_receive(wait_option不爲0)讀取消息時,線程會被掛起到隊列tx_queue_suspension_list。其它線程發送消息後會恢復掛起的線程
6,如果消息隊列已滿,發送線程調用_tx_queue_send(wait_option不爲0)發送消息時,線程會被掛起到隊列tx_queue_suspension_list。其它線程接收消息時,會恢復掛起的線程。
消息大小
消息隊列中消息大小支持是1個,2個,4個,8個和16個32位字,一個消息隊列消息大小隻能是其中一種,在創建消息隊列時指定。
一般消息內容比較多時,使用指針來傳遞。創建一個消息大小爲一個字的隊列,存儲傳遞的指針,線程發送或接受消息指針,而不是整個消息。
消息隊列中存儲消息個數有消息大小和分配的存儲空間大小決定。消息個數=存儲空間字節數/單個消息字節數。
存儲空間由應用開發人員分配好,是在消息創建時把存儲空間地址作爲入參。這塊存儲空間可以指定在高速ram中,提供系統性能。
消息隊列控制塊
消息隊列控制塊(QCB)是用來保持運行時消息隊列狀態的數據結構。
typedef struct TX_QUEUE_STRUCT
{
/* Define the queue ID used for error checking. */
ULONG tx_queue_id;
/* Define the queue's name. */
CHAR_PTR tx_queue_name;
/* Define the message size that was specified in queue creation. */
UINT tx_queue_message_size;
/* Define the total number of messages in the queue. */
ULONG tx_queue_capacity;
/* Define the current number of messages enqueue and the available
queue storage space. */
ULONG tx_queue_enqueued;
ULONG tx_queue_available_storage;
/* Define pointers that represent the start and end for the queue's
message area. */
ULONG_PTR tx_queue_start;
ULONG_PTR tx_queue_end;
/* Define the queue read and write pointers. Send requests use the write
pointer while receive requests use the read pointer. */
ULONG_PTR tx_queue_read;
ULONG_PTR tx_queue_write;
/* Define the queue suspension list head along with a count of
how many threads are suspended. */
struct TX_THREAD_STRUCT *tx_queue_suspension_list;
ULONG tx_queue_suspended_count;
/* Define the created list next and previous pointers. */
struct TX_QUEUE_STRUCT
*tx_queue_created_next,
*tx_queue_created_previous;
} TX_QUEUE;
域 | 意義 |
---|---|
tx_queue_id | 隊列id標誌 |
tx_queue_name | 隊列名字,創建者指定 |
tx_queue_message_size | 消息大小 |
tx_queue_capacity | 隊列容量(消息個數最大值) |
tx_queue_enqueued | |
tx_queue_available_storage | 隊列中可用存儲空間 |
tx_queue_start | 指向消息隊列頭部指針 |
tx_queue_end | 指向消息隊列尾部指針 |
tx_queue_read | 讀指針,指向消息隊列中第一個可讀消息地址 |
tx_queue_write | 寫指針,指向消息隊列中第一個可寫消息地址 |
tx_queue_suspension_list | 掛起隊列指針,掛起讀線程或發送線程 |
tx_queue_suspended_count | 掛起隊列中線程個數 |
tx_queue_created_next | 指向下一個消息隊列指針 |
tx_queue_created_previous | 指向前一個消息隊列指針 |
消息隊列list
系統中所有信號量控制塊掛載一個雙向鏈表_tx_queue_created_ptr中,tx_queue_created_next指向下一個消息隊列指針,tx_queue_created_previous指向前一個消息隊列指針。
消息隊列API
函數 | 描述 |
---|---|
_tx_queue_create | 創建消息隊列 |
_tx_queue_delete | 刪除消息隊列 |
_tx_queue_flush | 清空消息隊列中消息或掛起隊列中線程 |
_tx_queue_info_get | 獲取消息隊列信息 |
_tx_queue_receive | 讀取消息隊列消息 |
_tx_queue_send | 發送消息到隊列尾部 |
_tx_queue_front_send | 發送消息到隊列頭部 |
_tx_queue_prioritize | 調整掛起隊列頭部爲最高優先級線程 |
創建消息隊列_tx_queue_create
message_size當個消息佔用幾個 32位字
queue_start消息隊列存儲空間首地址
queue_size存儲空間大小
UINT _tx_queue_create(TX_QUEUE *queue_ptr, CHAR *name_ptr, UINT message_size,
VOID *queue_start, ULONG queue_size)
{
TX_INTERRUPT_SAVE_AREA
TX_QUEUE *tail_ptr; /* Working queue pointer */
REG_1 UINT capacity; /* Queue's message capacity */
REG_2 UINT used_words; /* Number of words used */
/* Setup the basic queue fields. */
queue_ptr -> tx_queue_name = name_ptr;
queue_ptr -> tx_queue_suspension_list = TX_NULL;
queue_ptr -> tx_queue_suspended_count = 0;
/* Save the message size in the control block. */
queue_ptr -> tx_queue_message_size = message_size;
/* Determine how many messages will fit in the queue area and the number
of ULONGs used. */
#def 計算消息隊列容量,message_size表示佔用幾個sizeof(ULONG),queue_size 表示總空間大小(字節),容量爲總的空間字節/單個消息字節,capacity存儲了計算後消息隊列能夠存儲消息個數,used_words 爲消息隊列容量,單位是字節
if (message_size == TX_1_ULONG)
{
capacity = queue_size / (TX_1_ULONG * sizeof(ULONG));
used_words = capacity;
}
else if (message_size == TX_2_ULONG)
{
capacity = queue_size / (TX_2_ULONG * sizeof(ULONG));
used_words = capacity * TX_2_ULONG;
}
else if (message_size == TX_4_ULONG)
{
capacity = queue_size / (TX_4_ULONG * sizeof(ULONG));
used_words = capacity * TX_4_ULONG;
}
else if (message_size == TX_8_ULONG)
{
capacity = queue_size / (TX_8_ULONG * sizeof(ULONG));
used_words = capacity * TX_8_ULONG;
}
else
{
capacity = queue_size / (TX_16_ULONG * sizeof(ULONG));
used_words = capacity * TX_16_ULONG;
}
/* Save the starting address and calculate the ending address of
the queue. Note that the ending address is really one past the
end! */
#def 設置消息隊列存儲空間起始地址和終止地址
queue_ptr -> tx_queue_start = (ULONG_PTR) queue_start;
queue_ptr -> tx_queue_end = ((ULONG_PTR) queue_start) + used_words;
/* Set the read and write pointers to the beginning of the queue
area. */
#def 初始消息隊列讀寫指針爲存儲空間起始地址
queue_ptr -> tx_queue_read = (ULONG_PTR) queue_start;
queue_ptr -> tx_queue_write = (ULONG_PTR) queue_start;
/* Setup the number of enqueued messages and the number of message
slots available in the queue. */
#def 初始空間容量和當前消息個數,剩餘空間(單位都是消息個數,不是字節)
queue_ptr -> tx_queue_enqueued = 0;
queue_ptr -> tx_queue_available_storage = capacity;
queue_ptr -> tx_queue_capacity = capacity;
/* Disable interrupts to put the queue on the created list. */
#def 禁止中斷,也就是禁止中斷處理或其他線程打斷本線程。處理全局變量
TX_DISABLE
/* Setup the queue ID to make it valid. */
#def 表示消息隊列有效標誌
queue_ptr -> tx_queue_id = TX_QUEUE_ID;
/* Place the queue on the list of created queues. First,
check for an empty list. */
#def 插入_tx_queue_created_ptr list
if (_tx_queue_created_ptr)
{
/* Pickup tail pointer. */
tail_ptr = _tx_queue_created_ptr -> tx_queue_created_previous;
/* Place the new queue in the list. */
_tx_queue_created_ptr -> tx_queue_created_previous = queue_ptr;
tail_ptr -> tx_queue_created_next = queue_ptr;
/* Setup this queues's created links. */
queue_ptr -> tx_queue_created_previous = tail_ptr;
queue_ptr -> tx_queue_created_next = _tx_queue_created_ptr;
}
else
{
/* The created queue list is empty. Add queue to empty list. */
_tx_queue_created_ptr = queue_ptr;
queue_ptr -> tx_queue_created_next = queue_ptr;
queue_ptr -> tx_queue_created_previous = queue_ptr;
}
/* Increment the number of queues created counter. */
_tx_queue_created_count++;
/* Restore interrupts. */
TX_RESTORE
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}
刪除隊列_tx_queue_delete
UINT _tx_queue_delete(TX_QUEUE *queue_ptr)
{
TX_INTERRUPT_SAVE_AREA
TX_THREAD *thread_ptr; /* Working thread pointer */
/* Disable interrupts to remove the queue from the created list. */
#def 禁止中斷,防止處理被打斷,處理全局變量
TX_DISABLE
/* Decrement the number of queues created. */
_tx_queue_created_count--;
/* Clear the queue ID to make it invalid. */
#def 標記爲無效
queue_ptr -> tx_queue_id = 0;
/* See if the queue is the only one on the list. */
#def 從_tx_queue_created_ptr 鏈表中刪除
if (queue_ptr == queue_ptr -> tx_queue_created_next)
{
/* Only created queue, just set the created list to NULL. */
_tx_queue_created_ptr = TX_NULL;
}
else
{
/* Link-up the neighbors. */
(queue_ptr -> tx_queue_created_next) -> tx_queue_created_previous =
queue_ptr -> tx_queue_created_previous;
(queue_ptr -> tx_queue_created_previous) -> tx_queue_created_next =
queue_ptr -> tx_queue_created_next;
/* See if we have to update the created list head pointer. */
if (_tx_queue_created_ptr == queue_ptr)
/* Yes, move the head pointer to the next link. */
_tx_queue_created_ptr = queue_ptr -> tx_queue_created_next;
}
#def 先禁止線程搶佔,然後TX_RESTORE開中斷,之後處理可以被中斷打斷,減少中斷處理時延,但不能被高優先級搶佔,因爲高優先級可能使用這個隊列
/* Temporarily disable preemption. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Walk through the queue list to resume any and all threads suspended
on this queue. */
#def 清除掛起鏈表中線程,恢復線程
thread_ptr = queue_ptr -> tx_queue_suspension_list;
while (queue_ptr -> tx_queue_suspended_count)
{
/* Lockout interrupts. */
TX_DISABLE
/* Clear the cleanup pointer, this prevents the timeout from doing
anything. */
thread_ptr -> tx_suspend_cleanup = TX_NULL;
/* Temporarily disable preemption again. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Yes, deactivate the thread's timer just in case. */
_tx_timer_deactivate(&(thread_ptr -> tx_thread_timer));
/* Clear the remaining time to ensure timer doesn't get activated. */
thread_ptr -> tx_thread_timer.tx_remaining_ticks = 0;
/* Set the return status in the thread to TX_DELETED. */
thread_ptr -> tx_suspend_status = TX_DELETED;
/* Move the thread pointer ahead. */
thread_ptr = thread_ptr -> tx_suspended_next;
/* Resume the thread. */
_tx_thread_resume(thread_ptr -> tx_suspended_previous);
/* Decrease the suspended count. */
queue_ptr -> tx_queue_suspended_count--;
}
#def 禁止中斷,爲了操作_tx_thread_preempt_disable,開啓搶佔
/* Disable interrupts. */
TX_DISABLE
/* Release previous preempt disable. */
_tx_thread_preempt_disable--;
/* Restore interrupts. */
TX_RESTORE
/* Check for preemption. */
#def 前面已經恢復了線程,可能有高優先級線程需要調度
if (_tx_thread_current_ptr != _tx_thread_execute_ptr)
/* Transfer control to system. */
#def 線程切換
_tx_thread_system_return();
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}
清空消息隊列_tx_queue_flush
_tx_queue_flush函數清空消息隊列中消息,掛起線程,設置爲初始化狀態。
UINT _tx_queue_flush(TX_QUEUE *queue_ptr)
{
TX_INTERRUPT_SAVE_AREA
TX_THREAD *suspension_list; /* Pickup the suspension list head */
UINT suspended_count; /* Count of suspended threads */
TX_THREAD *thread_ptr; /* Working thread pointer */
/* Initialize the suspended count. */
suspended_count = 0;
/* Disable interrupts to reset various queue parameters. */
#def 禁止中斷,下面操作全局變量,防止被打斷,被搶佔
TX_DISABLE
/* Determine if there is something on the queue. */
#def 如果隊列不爲空,清除消息,恢復初始化值
if (queue_ptr -> tx_queue_enqueued)
{
/* Yes, there is something in the queue. */
/* Reset the queue parameters to erase all of the queued messages. */
#def 設置爲空,初始化
queue_ptr -> tx_queue_enqueued = 0;
queue_ptr -> tx_queue_available_storage = queue_ptr -> tx_queue_capacity;
queue_ptr -> tx_queue_read = queue_ptr -> tx_queue_start;
queue_ptr -> tx_queue_write = queue_ptr -> tx_queue_start;
/* Now determine if there are any threads suspended on a full queue. */
#def 如果有掛起的線程,刪除list中線程,並恢復線程狀態
if (queue_ptr -> tx_queue_suspended_count)
{
/* Yes, there are threads suspended on this queue, they must be
resumed! */
/* Copy the information into temporary variables. */
suspension_list = queue_ptr -> tx_queue_suspension_list;
suspended_count = queue_ptr -> tx_queue_suspended_count;
/* Clear the queue variables. */
queue_ptr -> tx_queue_suspension_list = TX_NULL;
queue_ptr -> tx_queue_suspended_count = 0;
/* Temporarily disable preemption. */
#def 禁止搶佔
_tx_thread_preempt_disable++;
}
}
/* Restore interrupts. */
TX_RESTORE
/* Walk through the queue list to resume any and all threads suspended
on this queue. */
#def 恢復線程
if (suspended_count)
{
/* Pickup the thread to resume. */
thread_ptr = suspension_list;
do
{
/* Resume the next suspended thread. */
/* Lockout interrupts. */
TX_DISABLE
/* Clear the cleanup pointer, this prevents the timeout from doing
anything. */
thread_ptr -> tx_suspend_cleanup = TX_NULL;
/* Temporarily disable preemption again. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Yes, deactivate the thread's timer just in case. */
_tx_timer_deactivate(&(thread_ptr -> tx_thread_timer));
/* Clear the remaining time to ensure timer doesn't get activated. */
thread_ptr -> tx_thread_timer.tx_remaining_ticks = 0;
/* Set the return status in the thread to TX_SUCCESS. */
thread_ptr -> tx_suspend_status = TX_SUCCESS;
/* Move the thread pointer ahead. */
thread_ptr = thread_ptr -> tx_suspended_next;
/* Resume the thread. */
_tx_thread_resume(thread_ptr -> tx_suspended_previous);
/* Continue while there are suspended threads. */
}
while (--suspended_count);
/* Disable interrupts. */
TX_DISABLE
/* Restore previous preempt posture. */
_tx_thread_preempt_disable--;
/* Restore interrupts. */
TX_RESTORE
/* Check for preemption. */
#def 恢復線程或中斷後,可能有高優先級線程,進行調度切換
if (_tx_thread_current_ptr != _tx_thread_execute_ptr)
/* Transfer control to system. */
_tx_thread_system_return();
}
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}
參考:嵌入式實時操作系統的多線程計算