[TencentOS tiny source depth analysis] (3) - the queue

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 tinytemporarily support LIFO principle of LIFOoperation of the queue, but supports LIFO operation 消息队列.

Tip: TencentOS tinyThe queue is not equivalent to the message queue, although the 队列underlying implementation is dependent消息队列 , but TencentOS tinythey 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 tinyOperation 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_ttypes pend_obj, and k_msg_queue_tthe type of msg_queuemessage listing . In fact, the entire queue is very simple, mainly by msg_queuethe queue_headmember 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 tinythe 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 listmember 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.hdocument, 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 tinythe 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_objmember variable typeattribute 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_queueinitialization, 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_objmember variable typeattribute 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_addrthe msg_sizeparameter 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:

  1. First detected incoming parameters are correct

  2. Try to call the tos_msg_queue_get()function to get the message, if there is a queue message is successfully acquired (return K_ERR_NONE), or acquisition failure. (About the function explained in the next chapter)

  3. When you can get successful exit function, and when get the message fails, you can specify the waiting time timeoutbe blocked, if you do not wait ( timeout =TOS_TIME_NOWAIT), simply return an error code K_ERR_PEND_NOWAIT.

  4. If the scheduler is locked knl_is_sched_locked(), you can not wait for the operation and return an error code K_ERR_PEND_SCHED_LOCKED, after all, need to switch tasks, the scheduler is locked you can not switch tasks.

  5. Call the pend_task_block()function task blocked, this function is actually ready to be removed from the task list k_rdyq.task_list_head[task_prio], and inserted into the waiting list object->list, if the waiting time is not wait forever TOS_TIME_FOREVER, but also the task list insertion time k_tick_list, blocking time timeout, then a task scheduling knl_sched().

  6. When the program is able to execute pend_state2errno(), it represents 任务等待到消息, or 发生超时, then call the pend_state2errno()function to get about the task wait state, look at what kind of situation leads to the task resume operation.

  7. If it is normal (wait acquired message), the message from the task control block is k_curr_task->msg_addrread out, and written msg_addrfor the return. The size of the same message also will be by msg_sizereturn.

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 tinywill 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_postto write messages to the queue.
Writing the message queue process:

  1. First detected incoming parameters are correct

  2. Determine whether there are tasks waiting for a message, if there is based on optwakeup waiting for a task or all tasks parameter decides otherwise directly write messages to the queue.
  3. 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 follow FIFOthe principle of ( TOS_OPT_MSG_PUT_FIFO), write successful return K_ERR_NONE. If the message has no message pool (the maximum number of messages TOS_CFG_MSG_POOL_SIZEdefined macro determined), the write fails, return K_ERR_QUEUE_FULLerror code. (About the function will explain in the next chapter)

  4. 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 of msg_addrthe msg_sizemember variable, also need to wake up the task, it is by calling pend_task_wakeup()the wake function corresponding waiting task, the core idea is that by processing TOS_LIST_FIRST_ENTRYthe acquired wait for the task in the queue, then wake it up.

  5. 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 optfor 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!

I welcome public attention No.

For more information please concern "Things IoT development," the public number!

Guess you like

Origin www.cnblogs.com/iot-dev/p/11688920.html