1. 程式人生 > 實用技巧 >佇列:實用程式服務和資料結構

佇列:實用程式服務和資料結構

佇列:實用程式服務和資料結構

Queues: utility services and data structures

佇列實用程式服務

Nucleus RTOS有四個API呼叫,它們提供與佇列相關的實用函式:重置佇列、返回有關佇列的資訊、返回應用程式中的佇列數量以及返回指向應用程式中所有佇列的指標。前三個在Nucleus SE中實現。

重置佇列

此API呼叫將佇列恢復到其未使用的初始狀態。佇列中儲存的所有訊息都將丟失。佇列上掛起的所有任務都將恢復,並收到返回程式碼NUSE_queue_WAS_RESET。

Nucleus RTOS API Call for Resetting a Queue

Service call prototype:

STATUS NU_Reset_Queue(NU_QUEUE *queue;

Parameters:

queue– pointer to user-define queue control block

Returns:

NU_SUCCESS– the call was completed successfully
NU_INVALID_QUEUE– the queue pointer is not valid

Nucleus SE API Call for Resetting a Queue

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Queue_Reset(NUSE_QUEUE queue);

Parameters:

queue– the index (ID) of the queue to be reset

Returns:

NUSE_SUCCESS– the call was completed successfully
NUSE_INVALID_QUEUE– the queue index is not valid

Nucleus SE Implementation of Queue Reset

在引數檢查之後,NUSE_Queue_Reset()API函式的最初部分程式碼非常簡單。頭和尾索引以及佇列的訊息計數都設定為零。

啟用阻塞時,其他程式碼負責喚醒任何掛起的任務,因此:

while(NUSE_Queue_Blocking_Count[queue]!=0){U8index;/*checkwhetheranytasksareblocked*//*onthisqueue*/for(index=0;index<NUSE_TASK_NUMBER;index++){if((LONIB(NUSE_Task_Status[index])==NUSE_QUEUE_SUSPEND)&&(HINIB(NUSE_Task_Status[index])==queue)){NUSE_Task_Blocking_Return[index]=NUSE_QUEUE_WAS_RESET;NUSE_Task_Status[index]=NUSE_READY;break;}}NUSE_Queue_Blocking_Count[queue]--;}#ifNUSE_SCHEDULER_TYPE==NUSE_PRIORITY_SCHEDULERNUSE_Reschedule(NUSE_NO_TASK);#endif

佇列上掛起的每個任務都標記為“就緒”,掛起返回程式碼NUSE_queue_WAS_RESET。此過程完成後,如果正在使用優先順序排程程式,則會呼叫NUSE_Reschedule(),因為一個或多個優先順序較高的任務可能已準備就緒,需要允許其執行。

佇列資訊

此服務呼叫獲取有關佇列的資訊選擇。Nucleus SE實現與Nucleus RTOS的不同之處在於,它返回的資訊較少,因為不支援物件命名、可變訊息大小和掛起順序,並且可能無法啟用任務掛起。

Nucleus RTOS API Call for Queue Information

Service call prototype:

STATUS NU_Queue_Information(NU_QUEUE *queue, CHAR *name,
VOID **start_address,UNSIGNED *queue_size, UNSIGNED *available,
UNSIGNED *messages, OPTION *message_type,
UNSIGNED *message_size, OPTION *suspend_type,
UNSIGNED *tasks_waiting,
NU_TASK **first_task);

Parameters:

queue– pointer to the user-supplied queue control block
name– pointer to an 8-character destination area for the message-queue’s name
start_address– a pointer to a pointer, which will receive the address of the start of the queue’s data area
queue_size– a pointer to a variable for holding the total number ofUNSIGNEDdata elements in the queue
available– a pointer to a variable for holding the number of availableUNSIGNEDdata elements in the queue
messages– a pointer to a variable for holding the number of messages currently in the queue
message_type– pointer to a variable for holding the type of messages supported by the queue; valid message types areNU_FIXED_SIZEandNU_ VARIABLE_SIZE
message_size– pointer to a variable for holding the number ofUNSIGNEDdata elements in each queue message; if the queue supports variable-length messages, this number is the maximum message size
suspend_type– pointer to a variable for holding the task suspend type. Valid task suspend types areNU_FIFOandNU_PRIORITY
tasks_waiting– a pointer to a variable which will receive the number of tasks suspended on this queue
first_task– a pointer to a task pointer; the pointer of the first suspended task is placed in this task pointer

Returns:

NU_SUCCESS– the call was completed successfully
NU_INVALID_QUEUE– the queue pointer is not valid

Nucleus SE API Call for Queue Information

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Queue_Information(NUSE_QUEUE queue,
ADDR *start_address, U8 *queue_size, U8 *available, U8 *messages, U8 *tasks_waiting, NUSE_TASK *first_task);

Parameters:

queue– the index of the queue about which information is being requested
start_address– a pointer to a variable of typeADDR, which will receive the address of the start of the queue’s data area
queue_size– a pointer to a variable of typeU8, which will receive the total number of messages for which the queue has capacity
available– a pointer to a variable of typeU8, which will receive the number of messages for which the queue has currently remaining capacity
messages– a pointer to a variable of typeU8, which will receive the number of messages currently in the queue
tasks_waiting– a pointer to a variable which will receive the number of tasks suspended on this queue (nothing returned if task suspend is disabled)
first_task– a pointer to a variable of typeNUSE_TASKwhich will receive the index of the first suspended task (nothing returned if task suspend is disabled)

Returns:

NUSE_SUCCESS– the call was completed successfully
NUSE_INVALID_QUEUE– the queue index is not valid
NUSE_INVALID_POINTER– one or more of the pointer parameters is invalid

Nucleus SE Implementation of Queue Information

The implementation of this API call is quite straightforward:

*start_address=NUSE_Queue_Data[queue];*queue_size=NUSE_Queue_Size[queue];*available=NUSE_Queue_Size[queue]-NUSE_Queue_Items[queue];*messages=NUSE_Queue_Items[queue];#ifNUSE_BLOCKING_ENABLE*tasks_waiting=NUSE_Queue_Blocking_Count[queue];if(NUSE_Queue_Blocking_Count[queue]!=0){U8index;for(index=0;index<NUSE_TASK_NUMBER;index++){if((LONIB(NUSE_Task_Status[index])==NUSE_QUEUE_SUSPEND)&&(HINIB(NUSE_Task_Status[index])==queue)){*first_task=index;break;}}}else{*first_task=0;}#else*tasks_waiting=0;*first_task=0;#endif

函式返回佇列狀態。然後,如果啟用了阻塞API呼叫,則返回等待的任務數和第一個任務的索引(否則這兩個引數設定為0)。

獲取佇列數

此服務呼叫返回應用程式中配置的佇列數。在Nucleus RTOS中,這將隨時間而變化,返回的值將表示當前的佇列數,而在Nucleus SE中,返回的值在構建時設定,不能更改。

Nucleus RTOS API Call for Queue Count

Service call prototype:

UNSIGNED NU_Established_Queues(VOID);

Parameters:

None

Returns:

The number of created queues in the system.

Nucleus SE API Call for Queue Count

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

U8 NUSE_Queue_Count(void);

Parameters:

None

Returns:

The number of configured queues in the application

佇列計數的Nucleus SE實現

這個API呼叫的實現非常簡單:返回#define symbol NUSE_QUEUE_NUMBER的值。

資料結構

佇列使用五到六個資料結構—全部在RAM和ROM中—與其他Nucleus SE物件一樣,這些資料結構是一系列表,根據配置的佇列數量和選擇的選項進行包含和標註。

我強烈建議應用程式程式碼不要直接訪問這些資料結構,而是使用提供的API函式。這避免了與Nucleus SE未來版本的不相容和不必要的副作用,並簡化了將應用程式移植到Nucleus RTOS的過程。這裡包含資料結構的詳細資訊,以便更容易地理解服務呼叫程式碼的工作方式和除錯。

核心RAM資料

這些資料結構包括:

NUSE_Queue_Head[]–這是一個U8型別的陣列,每個配置的佇列有一個條目,它表示指向訊息佇列前面的指標。它用作NUSE_Queue_Data[]中地址的索引(見下文)。

NUSE_Queue_Tail[]–這是一個U8型別的陣列,每個配置的佇列有一個條目,它表示指向訊息佇列末尾的指標。它用作NUSE_Queue_Data[]中地址的索引(見下文)。

NUSE_Queue_Items[]–這是一個U8型別的陣列,每個配置的佇列有一個條目,表示佇列中當前訊息數的計數。這個資料可以說是多餘的,因為它的值可以從head和tail索引中派生出來,但是儲存計數可以簡化程式碼。

NUSE_Queue_Blocking_Count[]–此型別的U8陣列包含每個佇列上阻塞的任務數。此陣列僅在啟用阻止API呼叫支援時存在。

當Nucleus SE啟動時,這些資料結構都由NUSE_Init_Queue()初始化為零。這是合乎邏輯的,因為它將每個佇列呈現為空(未使用)。未來的文章將全面描述Nucleus SE的啟動過程。 以下是nuse_init.c檔案中這些資料結構的定義:

RAMU8NUSE_Queue_Head[NUSE_QUEUE_NUMBER];RAMU8NUSE_Queue_Tail[NUSE_QUEUE_NUMBER];RAMU8NUSE_Queue_Items[NUSE_QUEUE_NUMBER];#ifNUSE_BLOCKING_ENABLERAMU8NUSE_Queue_Blocking_Count[NUSE_QUEUE_NUMBER];#endif

使用者RAM

使用者負責為每個配置的佇列提供一個RAM區域用於資料儲存。這個RAM區域的大小必須容納ADDR型別的陣列,佇列中的每條訊息都有一個條目。

ROM資料

這些資料結構包括:

NUSE_Queue_Data[]–這是一個ADDR型別的陣列,每個配置的佇列有一個條目,它表示每個佇列的資料區指標(在上面的使用者RAM中討論過)。

NUSE_Queue_Size[]–這是一個U8型別的陣列,每個配置的佇列有一個條目,表示每個佇列可以容納的訊息數。

這些資料結構都在nuse_config.c中宣告和初始化(當然是靜態的),因此:

ROMADDR*NUSE_Queue_Data[NUSE_QUEUE_NUMBER]={/*addressesofqueuedataareas------*/};ROMU8NUSE_Queue_Size[NUSE_QUEUE_NUMBER]={/*queuesizes------*/};

Queue Data Footprint

Like all kernel objects in Nucleus SE, the amount of data memory required for queues is readily predictable.

The ROM data footprint (in bytes) for all the queues in an application may be computed thus:

NUSE_QUEUE_NUMBER * (sizeof(ADDR) + 1)

The kernel RAM data footprint (in bytes) for all the queues in an application, when blocking API calls is enabled, may be computed thus:

NUSE_QUEUE_NUMBER * 3

Otherwise it is:

NUSE_QUEUE_NUMBER * 4

The amount of user RAM (in bytes) required for the queue with indexqueueis:

NUSE_Queue_Size[queue] * sizeof(ADDR)

Unimplemented API Calls

Four queue API calls found in Nucleus RTOS are not implemented in Nucleus SE:

Create Queue

This API call creates a queue. It is not needed with Nucleus SE, as queues are created statically.

Service call prototype:

STATUS NU_Create_Queue(NU_QUEUE *queue, char *name,
VOID *start_address, UNSIGNED queue_size, OPTION message_type,
UNSIGNED message_size, OPTION suspend_type);

Parameters:

queue– pointer to a user-supplied queue control block; this will be used as a “handle” for the queue in other API calls
name– pointers to a 7-character, null-terminated name for the queue
start_address– starting address for the queue
message_type– type of message supported by the queue; may beNU_FIXED_SIZEorNU_VARIABLE_SIZE
message_size –if the queue supports fixed size messages, this parameter specifies the exact size of each message; otherwise, if the queue supports variable sized messages, this is the maximum message size
suspend_type– specifies how tasks suspend on the queue. Valid options for this parameter areNU_FIFOandNU_PRIORITY, which represent First-In-First-Out (FIFO) and priority-order task suspension, respectively

Returns:

NU_SUCCESS– indicates successful completion of the service
NU_INVALID_QUEUE– indicates the queue control block pointer isNULLor already in use
NU_INVALID_MEMORY– indicates the memory area specified by thestart_addressis invalid
NU_INVALID_MESSAGE– indicates that themessage_typeparameter is invalid
NU_INVALID_SIZE– indicates that either the message size is greater than the queue size, or that the queue size or message size is zero
NU_INVALID_SUSPEND– indicates that thesuspend_typeparameter is invalid

Delete Queue

This API call deletes a previously created queue. It is not needed with Nucleus SE, as queues are created statically and cannot be deleted.

Service call prototype:

STATUS NU_Delete_Queue(NU_QUEUE *queue);

Parameters:

queue– pointer to queue control block

Returns:

NU_SUCCESS– indicates successful completion of the service
NU_INVALID_QUEUE– indicates the queue pointer is invalid

Queue Pointers

This API call builds a sequential list of pointers to all queues in the system. It is not needed with Nucleus SE, as queues are identified by a simple index, not a pointer, and it would be redundant.

Service call prototype:

UNSIGNED NU_Queue_Pointers(NU_QUEUE **pointer_list,
UNSIGNED maximum_pointers);

Parameters:

pointer_list– pointer to an array ofNU_QUEUEpointers; this array will be filled with pointers to established queues in the system
maximum_pointers– the maximum number of pointers to place in the array

Returns:

The number ofNU_QUEUEpointers placed into the array

Broadcast to Queue

This API call broadcasts a message to all tasks waiting for a message from the specified queue. It is not implemented with Nucleus SE, as it would have added excessive complexity.

Service call prototype:

STATUS NU_Broadcast_To_Queue(NU_QUEUE *queue, VOID *message,
UNSIGNED size, UNSIGNED suspend);

Parameters:

queue– pointer to queue control block
message– pointer to the broadcast message
size– the number ofUNSIGNEDdata elements in the message. If the queue supports variable-length messages, this parameter must be equal to or less than the message size supported by the queue. If the queue supports fixed-size messages, this parameter must be exactly the same as the message size supported by the queue
suspend– specifies whether or not to suspend the calling task if the queue is already full; valid options for this parameter areNU_NO_SUSPEND,NU_SUSPENDor a timeout value.

Returns:

NU_SUCCESS– indicates successful completion of the service
NU_INVALID_QUEUE– indicates the queue pointer is invalid
NU_INVALID_POINTER– indicates that the message pointer isNULL
NU_INVALID_SIZE– Indicates that the message size specified is not compatible with the size specified when the queue was created
NU_INVALID_SUSPEND– indicates that suspend attempted from a non-task thread
NU_QUEUE_FULL– indicates that there is insufficient space in the queue for the message
NU_TIMEOUT– indicates the queue is still full after the timeout has expired
NU_QUEUE_DELETED– queue was deleted while task was suspended
NU_QUEUE_RESET– queue was reset while the task was suspended

與Nucleus RTOS的相容性

對於Nucleus SE的各個方面,我的目標是儘可能保持與Nucleus RTOS的高級別應用程式程式碼相容性。佇列也不例外,從使用者的角度來看,它們的實現方式與Nucleus RTOS中的方式大致相同。有一些不相容的地方,我認為這樣的不相容是可以接受的,因為生成的程式碼更容易理解,或者更有可能提高記憶體效率。否則,Nucleus RTOS API呼叫幾乎可以直接對映到Nucleus SE呼叫上。未來的一篇文章將包括關於為Nucleus RTOS使用者使用Nucleus SE的更多資訊。

物件識別符號

在Nucleus RTOS中,所有物件都由具有特定資料型別的資料結構(控制塊)來描述。指向此控制塊的指標用作佇列的識別符號。在Nucleus SE中,我決定需要一種不同的方法來提高記憶體效率,所有的核心物件都由RAM和/或ROM中的許多表來描述,這些表的大小由配置的每個物件型別的數量決定。特定物件的識別符號只是這些表的索引。因此,我將NUSE_QUEUE定義為等同於U8;此型別的變數(而不是指標)將用作佇列識別符號。這是一個小的不相容性,如果程式碼被移植到Nucleus RTOS或從Nucleus RTOS移植過來,就很容易處理這個問題。物件識別符號通常只是儲存和傳遞,而不是以任何方式操作。

Nucleus RTOS還支援佇列命名。這些名稱僅用於基於目標的除錯工具。為了節省記憶體,我把它們從Nucleus SE中省略了。

訊息大小和可變性

在Nucleus RTOS中,佇列可以配置為處理由任意數量的無符號資料元素組成的訊息。在Nucleus SE中,佇列被簡化,只支援單個ADDR訊息。管道在Nucleus SE中更為靈活,可能為某些應用程式提供了替代佇列的有用方法;本系列的下兩篇文章將介紹管道。

Nucleus RTOS還支援具有可變大小訊息的佇列,其中僅在建立時指定最大大小。Nucleus SE不支援可變大小的訊息。

佇列大小

Nucleus SE中佇列中的訊息數被限制為256條,因為所有索引變數和常量都是U8型別。Nucleus RTOS不受此限制。

未實現的API呼叫

Nucleus RTOS支援10個服務呼叫來處理佇列。其中4個在Nucleus SE中沒有實現。