The basic concept of the queue
A queue is a data structure used for communication between tasks, queues can 任务与任务间、中断和任务间
transfer messages to implement the task receives a message from another task is not a fixed length, or interrupted, the task which can be read from the queues, the queue when the message is when empty, the task reads the message is blocked, the user can also specify the time of the task waiting for a message timeout
, during this time, if the queue is empty, the task 保持阻塞
state waiting queue data is valid. When there are new messages in the queue, blocked task will wake up and deal with new messages; when the wait time exceeds the specified blocking time, even if there is no valid data in the queue, the task will automatically be converted to a ready state from blocking state , the message queue is a 异步
means of communication.
Queue services, task or interrupt service routine can be one or more messages in a queue. Likewise, one or more tasks may be obtained from the message queue. When sending multiple messages to the queue, the message is usually the first to enter the queue first be preached to the task, that is to say, the first task is to get the message the first to enter the queue, that is, the FIFO principle ( FIFO
), in fact, TencentOS tiny
temporarily support LIFO principle of LIFO
operation of the queue, but supports LIFO operation 消息队列
.
Tip:
TencentOS tiny
The queue is not equivalent to the message queue, although the队列
underlying implementation is dependent消息队列
, butTencentOS tiny
they will be separated, which is two concepts, after the operation is not the same.
Queue blocking mechanism
Here is a simple example to understand the blocking mechanism operating systems:
Suppose you go to a restaurant one day, but no food restaurant, then you may have three options, you walked away, since no food, and certainly change a restaurant ah is not it. Or you may choose to wait, maybe the boss to go shopping, and there will be a dish of it, you can eat. Or, do you think this restaurant is delicious, eat less food you will not go, and so on - in this dead
The same: Suppose there is a task to a queue A read operation time ( 出队
), and this was not found to the message, then the time Task A has three options: The first option, the task A and walked away, Since no message queue, then I do not wait, go do other things, like this task a 不会进入阻塞态
; second option, the task a is here and so it may have a message queue after a while, this time the task will be a blocking state, waiting for the arrival of the message, and waiting time on task a 由我们自己指定
, the period when the blocked task a until the message queue, the task becomes ready state a from a blocking state; if wait timeout the queue not news, that task a not wait, wake up from blocking states; third option, tasks, etc. a dead, do not wait until the news is not gone, so subtasks a will enter the blocking state until completion of reading the message queue.
Queue implementation data structure
Queue Control Block
TencentOS tiny
Operation queue by the queue control block of the data type k_queue_t
, the queue control block is composed of a plurality of elements, there are pend_obj_t
types pend_obj
, and k_msg_queue_t
the type of msg_queue
message listing . In fact, the entire queue is very simple, mainly by msg_queue
the queue_head
member variable (which is actually a list of messages (message list)), all messages will be recorded in the message list, when reading the message from the message list will read the message.
Data structures inherited from the kernel object in the \ kernel \ core \ include \ tos_pend.h of 35 lines
typedef struct pend_object_st {
pend_type_t type;
k_list_t list;
} pend_obj_t;
Data type (Message Queue Control Block) message list, in \ kernel \ core \ include \ file line 13 tos_msg.h
typedef struct k_msg_queue_st {
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
knl_obj_t knl_obj;
#endif
k_list_t queue_head;
} k_msg_queue_t;
Queue control block, in \ kernel \ core \ include \ file line 6 tos_queue.h
typedef struct k_queue_st {
pend_obj_t pend_obj;
k_msg_queue_t msg_queue;
} k_queue_t;
The queue control block diagram is as follows:
Message Control Block
In addition to the queue control block, and the message queue control block, because TencentOS tiny
the queue is dependent on the message queue, the queue can be transmitted since the data (message), then there must be a data structure can store the message, called I the control block for the message, the message stored in the recording control block address of the message msg_addr
, and the size of the message msg_size
, in addition to the presence of a list
member variable, may be mounted to a message queue in the message list.
Message control block data structure, the \ kernel \ core \ include \ file line 7 tos_msg.h
typedef struct k_message_st {
k_list_t list;
void *msg_addr;
size_t msg_size;
} k_msg_t;
In fact queue depends on the message queue, their relationship is as follows:
Member variable message task control block
A assume the task waiting in the queue messages, interrupted, or other tasks to task A waiting queue write (send) a message, then the message will not be mounted to the list of messages in the queue, but will be recorded directly in a task control block of the task, the task a represents waiting to this message from the queue, the task control block must be some member variables for recording information message (e.g., message address, message size, etc.):
Task control block data structure in \ Kernel \ line 90 core \ include \ tos_task.h file
typedef struct k_task_st {
···
#if TOS_CFG_MSG_EN > 0u
void *msg_addr; /**< 保存接收到的消息地址 */
size_t msg_size; /**< 保存接收到的消息大小 */
#endif
···
} k_task_t;
Macro definitions associated with the message
In the tos_config.h
document, enabling the queue component macro definition TOS_CFG_QUEUE_EN
, Message Queuing component enabling macro definition TOS_CFG_MSG_EN
, the number of messages the system supports macros message pool TOS_CFG_MSG_POOL_SIZE
.
#define TOS_CFG_QUEUE_EN 1u
#define TOS_CFG_MSG_EN 1u
#define TOS_CFG_MSG_POOL_SIZE 3u
News pond
In TencentOS tiny
the definition of an array k_msg_pool[TOS_CFG_MSG_POOL_SIZE]
as a message pool, its data type is a message control block type k_msg_t
, since access to the message when the message queue is used more frequently, at system initialization time and will initialize the various elements of a large array of strings, and mount to idle message list k_msg_freelist
, the message we are talking about the composition of the pool k_msg_pool
, the pool and the member variable is what we call news.
Why use a pooled approach to news, because the efficient reuse rate, as we went to a spoonful of water in the pond, and then return them after use in the pond, this operation is very efficient, and limited embedded resource duplication of resources can be effectively utilized.
Message is defined in the relevant pool \ kernel \ core \ tos_global.c file line 51
#if TOS_CFG_MSG_EN > 0u
TOS_LIST_DEFINE(k_msg_freelist);
k_msg_t k_msg_pool[TOS_CFG_MSG_POOL_SIZE];
#endif
Create a queue
tos_queue_create()
Function is used to create a queue, the queue is a data structure, pass data between tasks for. Each create a new queue need to assign RAM, at the time of creation we need to define a queue control block, its memory is assigned automatically by the compiler. In fact, the contents of the queue control block is initialized in the process created, the queue control block pend_obj
member variable type
attribute identifies PEND_TYPE_QUEUE
, it indicates that this is a queue, and then call the message queue API function tos_msg_queue_create()
messages will queue member variables msg_queue
initialization, in fact, is to initialize the message list.
Create a queue function, in \ kernel \ core \ tos_queue.c line 5
__API__ k_err_t tos_queue_create(k_queue_t *queue)
{
TOS_PTR_SANITY_CHECK(queue);
pend_object_init(&queue->pend_obj, PEND_TYPE_QUEUE);
tos_msg_queue_create(&queue->msg_queue);
return K_ERR_NONE;
}
Destruction queue
tos_queue_destroy()
Function for the destruction of a queue when the queue is not in use it can be destroyed, the destruction of the nature of the content is actually cleared the queue control blocks, first determine what type of queue control block is PEND_TYPE_QUEUE
, this function can only destroy queue type of control Piece. And then determine whether there are tasks waiting for messages in the queue, if you call the pend_wakeup_all()
function to wake up this task, and then call tos_msg_queue_flush()
the function of the message queue list of all " 清空
", "Empty" means to mount on the queue message back to the message for releasing the pool (if the message queue is a list of the presence message, using the msgpool_free()
function release message), knl_object_deinit()
the function is to ensure that queue has been destroyed, this time in the queue control block pend_obj
member variable type
attribute is identified KNL_OBJ_TYPE_NONE
. Finally, after the destruction of a task scheduling queue to switch tasks (after all, just probably awakened the task).
But one thing to note, because RAM queue control block is allocated statically by the compiler, so even if the queue is destroyed, this memory is no way the release of ~
Destruction queue function, in \ kernel \ core \ line 14 tos_queue.c
__API__ k_err_t tos_queue_destroy(k_queue_t *queue)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(queue);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (!pend_is_nopending(&queue->pend_obj)) {
pend_wakeup_all(&queue->pend_obj, PEND_STATE_DESTROY);
}
pend_object_deinit(&queue->pend_obj);
tos_msg_queue_flush(&queue->msg_queue);
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
Clear the queue
Clear the message queue is actually released back into the pool a message, or call essentially tos_msg_queue_flush()
function. It is dependent on the message queue implementation.
Function queue is empty, the \ kernel \ core \ tos_queue.c line 41
__API__ k_err_t tos_queue_flush(k_queue_t *queue)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(queue);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
tos_msg_queue_flush(&queue->msg_queue);
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
Waiting queue (message)
When the task queue when trying to get messages from, the user can specify a wait time, if and only when there is a message queue, the task can get the message. During this waiting time, if the queue is empty, the task remains blocked waiting queue message is valid. When the other task or interrupt service routine writes the data to its waiting queue, the task will be automatically blocked by the state into ready state. When the task is waiting for a timeout occurs, even if there is no valid message queue, the task will automatically be converted to a ready state from blocking state.
Process waiting queue is very simple, take a look at the parameters of it (in which msg_addr
the msg_size
parameter is used to save the contents returned by the function that outputs):
| Parameter | Description |
| - | - |
| Queue | Queue Control Block pointer |
| msg_addr | for storing the acquired message (which is the output) |
| msg_size | for storing the acquired message size (which is the output) |
| timeout | latency (in units of k_tick_t) |
Process queue message is as follows:
First detected incoming parameters are correct
Try to call the
tos_msg_queue_get()
function to get the message, if there is a queue message is successfully acquired (returnK_ERR_NONE
), or acquisition failure. (About the function explained in the next chapter)When you can get successful exit function, and when get the message fails, you can specify the waiting time
timeout
be blocked, if you do not wait (timeout =TOS_TIME_NOWAIT
), simply return an error codeK_ERR_PEND_NOWAIT
.If the scheduler is locked
knl_is_sched_locked()
, you can not wait for the operation and return an error codeK_ERR_PEND_SCHED_LOCKED
, after all, need to switch tasks, the scheduler is locked you can not switch tasks.Call the
pend_task_block()
function task blocked, this function is actually ready to be removed from the task listk_rdyq.task_list_head[task_prio]
, and inserted into the waiting listobject->list
, if the waiting time is not wait foreverTOS_TIME_FOREVER
, but also the task list insertion timek_tick_list
, blocking timetimeout
, then a task schedulingknl_sched()
.When the program is able to execute
pend_state2errno()
, it represents任务等待到消息
, or发生超时
, then call thepend_state2errno()
function to get about the task wait state, look at what kind of situation leads to the task resume operation.If it is normal (wait acquired message), the message from the task control block is
k_curr_task->msg_addr
read out, and writtenmsg_addr
for the return. The size of the same message also will be bymsg_size
return.
Get (wait) message queue function, the \ kernel \ core \ tos_queue.c line 60
__API__ k_err_t tos_queue_pend(k_queue_t *queue, void **msg_addr, size_t *msg_size, k_tick_t timeout)
{
TOS_CPU_CPSR_ALLOC();
k_err_t err;
TOS_PTR_SANITY_CHECK(queue);
TOS_PTR_SANITY_CHECK(msg_addr);
TOS_PTR_SANITY_CHECK(msg_size);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (tos_msg_queue_get(&queue->msg_queue, msg_addr, msg_size) == K_ERR_NONE) {
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
if (timeout == TOS_TIME_NOWAIT) {
*msg_addr = K_NULL;
*msg_size = 0;
TOS_CPU_INT_ENABLE();
return K_ERR_PEND_NOWAIT;
}
if (knl_is_sched_locked()) {
TOS_CPU_INT_ENABLE();
return K_ERR_PEND_SCHED_LOCKED;
}
pend_task_block(k_curr_task, &queue->pend_obj, timeout);
TOS_CPU_INT_ENABLE();
knl_sched();
err = pend_state2errno(k_curr_task->pend_state);
if (err == K_ERR_NONE) {
*msg_addr = k_curr_task->msg_addr;
*msg_size = k_curr_task->msg_size;
k_curr_task->msg_addr = K_NULL;
k_curr_task->msg_size = 0;
}
return err;
}
The task waiting for the message added to the corresponding waiting list function, in \ kernel \ core \ line 106 tos_pend.c file
__KERNEL__ void pend_task_block(k_task_t *task, pend_obj_t *object, k_tick_t timeout)
{
readyqueue_remove(task);
pend_list_add(task, object);
if (timeout != TOS_TIME_FOREVER) {
tick_list_add(task, timeout);
}
}
Get task wait function of the state, in \ the first 72 rows kernel \ core \ tos_pend.c file
__KERNEL__ k_err_t pend_state2errno(pend_state_t state)
{
if (state == PEND_STATE_POST) {
return K_ERR_NONE;
} else if (state == PEND_STATE_TIMEOUT) {
return K_ERR_PEND_TIMEOUT;
} else if (state == PEND_STATE_DESTROY) {
return K_ERR_PEND_DESTROY;
} else if (state == PEND_STATE_OWNER_DIE) {
return K_ERR_PEND_OWNER_DIE;
} else {
return K_ERR_PEND_ABNORMAL;
}
}
(Message) written to the queue
Task or interrupt service routine can send a message to a message queue, when sending a message, TencentOS tiny
will remove a message from the message pool, to mount the end of the list the message queue (FIFO transmit mode). tos_queue_post()
Is a wake-up message task queue, tos_queue_post_all()
it will wake up all the tasks waiting queue messages, regardless of the circumstances, all calls queue_do_post
to write messages to the queue.
Writing the message queue process:
First detected incoming parameters are correct
- Determine whether there are tasks waiting for a message, if there is based on
opt
wakeup waiting for a task or all tasks parameter decides otherwise directly write messages to the queue. When there is no task while waiting for a message, call the
tos_msg_queue_put()
function to write messages to the queue, the queue is written the way to followFIFO
the principle of (TOS_OPT_MSG_PUT_FIFO
), write successful returnK_ERR_NONE
. If the message has no message pool (the maximum number of messagesTOS_CFG_MSG_POOL_SIZE
defined macro determined), the write fails, returnK_ERR_QUEUE_FULL
error code. (About the function will explain in the next chapter)If there are tasks waiting for a message, then call the
queue_task_msg_recv()
function to write the task control block of the message content and the size ofmsg_addr
themsg_size
member variable, also need to wake up the task, it is by callingpend_task_wakeup()
the wake function corresponding waiting task, the core idea is that by processingTOS_LIST_FIRST_ENTRY
the acquired wait for the task in the queue, then wake it up.For all wake up processing of waiting tasks is actually the same, just more cycles of treatment, the waiting list of all the tasks sequentially wake up, that's it -
Write queue message function, the \ kernel \ core \ tos_queue.c 159, line 164
__API__ k_err_t tos_queue_post(k_queue_t *queue, void *msg_addr, size_t msg_size)
{
TOS_PTR_SANITY_CHECK(queue);
TOS_PTR_SANITY_CHECK(msg_addr);
return queue_do_post(queue, msg_addr, msg_size, OPT_POST_ONE);
}
__API__ k_err_t tos_queue_post_all(k_queue_t *queue, void *msg_addr, size_t msg_size)
{
TOS_PTR_SANITY_CHECK(queue);
TOS_PTR_SANITY_CHECK(msg_addr);
return queue_do_post(queue, msg_addr, msg_size, OPT_POST_ALL);
}
Write queue message function actually invoked by
opt
for different process parameters, the \ kernel \ core \ tos_queue.c Line 118
__STATIC__ k_err_t queue_do_post(k_queue_t *queue, void *msg_addr, size_t msg_size, opt_post_t opt)
{
TOS_CPU_CPSR_ALLOC();
k_list_t *curr, *next;
TOS_PTR_SANITY_CHECK(queue);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (pend_is_nopending(&queue->pend_obj)) {
if (tos_msg_queue_put(&queue->msg_queue, msg_addr, msg_size, TOS_OPT_MSG_PUT_FIFO) != K_ERR_NONE) {
TOS_CPU_INT_ENABLE();
return K_ERR_QUEUE_FULL;
}
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
if (opt == OPT_POST_ONE) {
queue_task_msg_recv(TOS_LIST_FIRST_ENTRY(&queue->pend_obj.list, k_task_t, pend_list),
msg_addr, msg_size);
} else { // OPT_QUEUE_POST_ALL
TOS_LIST_FOR_EACH_SAFE(curr, next, &queue->pend_obj.list) {
queue_task_msg_recv(TOS_LIST_ENTRY(curr, k_task_t, pend_list),
msg_addr, msg_size);
}
}
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
Wake-up function tasks waiting in the \ kernel \ core \ line 87 tos_pend.c file
Wake up thinking of waiting tasks is to be removed from the corresponding task waiting list, and then added to the ready list.
__KERNEL__ void pend_task_wakeup(k_task_t *task, pend_state_t state)
{
if (task_state_is_pending(task)) {
// mark why we wakeup
task->pend_state = state;
pend_list_remove(task);
}
if (task_state_is_sleeping(task)) {
tick_list_remove(task);
}
if (task_state_is_suspended(task)) {
return;
}
readyqueue_add(task);
}
to sum up
Code lean and short, clear thinking, highly recommended depth study ~
I like to focus on it!
For more information please concern "Things IoT development," the public number!