[] TencentOS tiny depth source code analysis (7) - Event

introduction

We are likely to bare metal frequently used in programming flagthis variable, used to mark what an event to occur, and then determine whether these markers in a loop occurs, if it is to wait for multiple events, you may also if((xxx_flag)&&(xxx_flag))like this to make a judgment. Of course, if a little clever students will take flaga 某些位make signs, such as the first digit indicates the variable Aevent, the second represents the Bincident, when two events are happening, it is determined that flag&0x03the value is the number, in order to determine the which event has occurred.

But how will we achieve in the operating system?

event

In the operating system, 事件a kernel resources, mainly for 任务与任务间、中断与任务间the 同步, 不提供data transmission function!

And the use of 信号量synchronization is slightly different: it can achieve event-many, many-sync. That is a task can wait for multiple events occur: either a wake-up event processing tasks during any one event; can also be several events have occurred after the wake-up event processing tasks. Similarly, there can be multiple tasks to synchronize multiple events.

Each event group requires minimal RAMspace to hold the event flag, a 事件(控制块)contains a 旗标, that 旗标each bit represents a " 事件," flags are stored in a k_event_flag_ttype of variable (name flag, flag simple to understand 事件标记变量), the variable is defined in the event control blocks, each represents an event, tasks “逻辑与”or “逻辑或”with one or more associated events, the task will be woken up when an event occurs.

  • Event "OR" is 独立型同步, referring to any one of several events in the event task waits to be awakened;

  • Event "logic" is 关联型同步, referring to the number of events in all the tasks waiting to be awakened only occur.

Event task communication is a mechanism implemented, may be used to achieve synchronization between tasks, but no data transmission event. Multitasking environment, tasks, often need to synchronize operations between interrupts, tells a task waiting in the event that the formation of a task with the task, interrupt and synchronization between tasks.

An event 无排队性that set more than once to the same event task (if the task has not yet had time to read go), equivalent to the set only once.

Further events can provide many, many to many synchronous operation.

  • 一对多Synchronization model: a task waiting for a trigger multiple events, this situation is more common;

  • 多对多Synchronization model: multiple tasks waiting for a trigger multiple events, the task can be accomplished event triggering event and wait for the operation by setting the bit.

The event data structure

Event Control Block

TencentOS tinyOperation event by the event control block, which is a data type k_event_t, the event control block is composed of a plurality of elements.

  • pend_objSomewhat similar to the object-oriented inheritance, some properties, there are described the type of kernel resources (e.g. mutex queue mutex, etc, and a waiting list list).
  • flagA flag, a 32-bit variable, so that each event control block 32 can only identify events!
typedef struct k_event_st {
    pend_obj_t      pend_obj;
    k_event_flag_t  flag;
} k_event_t;

Task control block data structure associated with the event

typedef struct k_task_st {
    ···
    k_opt_t             opt_event_pend;     /**< 等待事件的的操作类型:TOS_OPT_EVENT_PEND_ANY 、 TOS_OPT_EVENT_PEND_ALL */
    k_event_flag_t      flag_expect;        /**< 期待发生的事件 */
    k_event_flag_t     *flag_match;         /**< 等待到的事件(匹配的事件) */
    ···
} k_task_t;

Macro definitions related to the event

In tos_config.hthe macro definition event switches are arrangedTOS_CFG_EVENT_EN

#define TOS_CFG_EVENT_EN            1u

In the tos_event.hmiddle, there are some macro definitions are used to operate the event ( opt选项):

// if we are pending an event, for any flag we expect is set is ok, this flag should be passed to tos_event_pend 
#define TOS_OPT_EVENT_PEND_ANY          (k_opt_t)0x0001

// if we are pending an event, must all the flag we expect is set is ok, this flag should be passed to tos_event_pend 
#define TOS_OPT_EVENT_PEND_ALL          (k_opt_t)0x0002

#define TOS_OPT_EVENT_PEND_CLR          (k_opt_t)0x0004
  • TOS_OPT_EVENT_PEND_ANY: A task is waiting for any event, the "OR"!
  • TOS_OPT_EVENT_PEND_ALL: Task waiting for all events, namely the "AND"!
  • TOS_OPT_EVENT_PEND_CLR: To clear pending event flag, may be TOS_OPT_EVENT_PEND_ANY, TOS_OPT_EVENT_PEND_ALLa mixture (by “|”operator).

In addition there is a type of data structure enumeration, operation options for sending an event, you can clear the event flag other bits (that is, covering the impact of other events) when sending events, can be kept in the original flag the other bits (no cover, does not affect other events).

typedef enum opt_event_post_en {
    OPT_EVENT_POST_KEP,
    OPT_EVENT_POST_CLR,
} opt_event_post_t;

Create an event

System, each event has a corresponding event control block, Event Control Block contains all the information about the event, such as its waiting list, its resource type, and its event flag value, then imagine, create an event It is not that the essence of the event control block to initialize it? Obviously it is like that. Since the subsequent operation of the event by the event control block is operated, the control block if there is no information, how it can operate Well ~

Create function is the event tos_event_create(), passing a pointer to the event control block *event, in addition to the event you can specify the initial value init_flag.

Create an event is actually calling pend_object_init()function to the event control block event->pend_objmember variable is initialized, it is identified as the resource type PEND_TYPE_EVENT. Then the event->flagmember variable is set to mark the initial value of the event flag init_flag.

__API__ k_err_t tos_event_create(k_event_t *event, k_event_flag_t init_flag)
{
    TOS_PTR_SANITY_CHECK(event);

    pend_object_init(&event->pend_obj, PEND_TYPE_EVENT);
    event->flag = init_flag;
    return K_ERR_NONE;
}

Destruction event

Event destructor is cleared based on all the information will be an event control block direct destruction, after the destruction of the event, but can not use this event again, when the event is destroyed, its waiting list exists task, the system is necessary to those waiting for these tasks wake up and inform the task event has been destroyed PEND_STATE_DESTROY. And then generates a task scheduler to switch to the highest priority task execution.

TencentOS tiny Event destruction process is as follows:

  1. Call the pend_is_nopending()function to determine whether there are tasks waiting for an event
  2. If there are tasks waiting for an event it is called in pend_wakeup_all()the function of these tasks will wake up, and told to wait for the task event has been destroyed (ie set the wait state task control block member variable pend_stateis PEND_STATE_DESTROY).
  3. Call the pend_object_deinit()function to control events in the removal of content block, the most important is the control block resource type is set PEND_TYPE_NONE, so the child will not be able to use this incident.
  4. The event->flagrecovery member variables to their default values 0.
  5. Task schedulingknl_sched()

Note: If the event is determined by the control block RAM 编译器静态分配, so even if the event is destroyed, this is no way to release the memory. Of course, you can also use dynamic memory control block to allocate memory for the event, but after the destruction freed want this memory to avoid memory leaks.

__API__ k_err_t tos_event_destroy(k_event_t *event)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_PTR_SANITY_CHECK(event);

#if TOS_CFG_OBJECT_VERIFY_EN > 0u
    if (!pend_object_verify(&event->pend_obj, PEND_TYPE_EVENT)) {
        return K_ERR_OBJ_INVALID;
    }
#endif

    TOS_CPU_INT_DISABLE();

    if (!pend_is_nopending(&event->pend_obj)) {
        pend_wakeup_all(&event->pend_obj, PEND_STATE_DESTROY);
    }

    pend_object_deinit(&event->pend_obj);
    event->flag = (k_event_flag_t)0u;

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

Waiting for the event

tos_event_pend()Function is used to get events through this function, you can know 事件旗标the what is 置1that event which has occurred, then the task can be specified to wait for an event “逻辑与”、“逻辑或”waiting operation ( opt_pend选项).

And this function implements the 等待超时mechanism, and only when a task waiting for an event occurs, the task can wait for the event. When the event has not occurred, the task waits for an event enters the blocking state, blocking time timeoutspecified by the user, at this time, if the event has not occurred, the task will remain blocked waiting for an event to occur. When the standard set corresponding event flag flag other tasks or interrupt service routine to their waiting, the task will automatically by blocking state into ready state. When the task wait time exceeds the specified blocking time, even if the event has not occurred, the task will be automatically transferred from the blocking state to ready state. Like this very effectively reflects the real-time operating system.

When the task acquired an event, you can choose to clear the event action.

Wait event interrupt operation is not allowed in the context of running!

While waiting for events is as follows:

  1. First detected incoming parameters are correct. , Note opt_pendOptions must exist TOS_OPT_EVENT_PEND_ALLor TOS_OPT_EVENT_PEND_ANYone, and both allowed to exist ( 互斥).
  2. Call event_is_match()whether to wait for the function to determine an event has occurred (ie, the task waiting for events and event control block whether the flag 匹配).
  3. In event_is_match()function based on waiting option opt_pendis to wait for any one event ( TOS_OPT_EVENT_PEND_ANY) or wait for all events ( TOS_OPT_EVENT_PEND_ANY) to judge whether the match if the match is returned K_TRUE, otherwise returns K_FALSE, while waiting for the event by flag_matchvariable returns (match has occurred) . Waiting time for all of the options, if and only if all events occur is considered to match: (event & flag_expect) == flag_expect)For any option to wait for an event, an event which has occurred is considered a match: (event & flag_expect).
  4. If an event occurs, it can not block the acquisition of the current task, look at the user-specified blocking time timeoutwhether or not to block TOS_TIME_NOWAIT, if not blocked directly returns K_ERR_PEND_NOWAITan error code.
  5. 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.
  6. The task control block variables about the event set about that task to set an event expected to wait k_curr_task->flag_expect, the task of matching the event k_curr_task->flag_match, and the event task waits options k_curr_task->opt_event_pend.
  7. 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().
  8. When the program can continue down, it means 任务等待到事件, or if 等待发生了超时the task does not need to wait for the event, and this time the contents of the task control block cleared, clear the waiting task desired events k_curr_task->flag_expect, events that match the task k_curr_task->flag_match, and the task wait options events k_curr_task->opt_event_pend, while also calling pend_state2errno()function to get about the task wait state, look at what kind of circumstances leading to the recovery task to run, and the result is returned to the calling function waits for the event task.

Note: When a task waiting for an event from the obstruction to resume operation, not necessarily wait until an event occurs, there may be a timeout has occurred, so when writing the program must wait for the state to determine what events, if it is K_ERR_NONEthen retrieves success!

code show as below:

__STATIC__ int event_is_match(k_event_flag_t event, k_event_flag_t flag_expect, k_event_flag_t *flag_match, k_opt_t opt_pend)
{
    if (opt_pend & TOS_OPT_EVENT_PEND_ALL) {
        if ((event & flag_expect) == flag_expect) {
            *flag_match = flag_expect;
            return K_TRUE;
        }
    } else if (opt_pend & TOS_OPT_EVENT_PEND_ANY) {
        if (event & flag_expect) {
            *flag_match = event & flag_expect;
            return K_TRUE;
        }
    }
    return K_FALSE;
}

__API__ k_err_t tos_event_pend(k_event_t *event, k_event_flag_t flag_expect, k_event_flag_t *flag_match, k_tick_t timeout, k_opt_t opt_pend)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_PTR_SANITY_CHECK(event);
    TOS_PTR_SANITY_CHECK(flag_match);
    TOS_IN_IRQ_CHECK();

#if TOS_CFG_OBJECT_VERIFY_EN > 0u
    if (!pend_object_verify(&event->pend_obj, PEND_TYPE_EVENT)) {
        return K_ERR_OBJ_INVALID;
    }
#endif

    if (!(opt_pend & TOS_OPT_EVENT_PEND_ALL) && !(opt_pend & TOS_OPT_EVENT_PEND_ANY)) {
        return K_ERR_EVENT_PEND_OPT_INVALID;
    }

    if ((opt_pend & TOS_OPT_EVENT_PEND_ALL) && (opt_pend & TOS_OPT_EVENT_PEND_ANY)) {
        return K_ERR_EVENT_PEND_OPT_INVALID;
    }

    TOS_CPU_INT_DISABLE();

    if (event_is_match(event->flag, flag_expect, flag_match, opt_pend)) {
        if (opt_pend & TOS_OPT_EVENT_PEND_CLR) { // destroy the bridge after get across the river
            event->flag = (k_event_flag_t)0u;
        }
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (timeout == TOS_TIME_NOWAIT) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_PEND_NOWAIT;
    }

    if (knl_is_sched_locked()) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_PEND_SCHED_LOCKED;
    }

    k_curr_task->flag_expect      = flag_expect;
    k_curr_task->flag_match       = flag_match;
    k_curr_task->opt_event_pend   = opt_pend;

    pend_task_block(k_curr_task, &event->pend_obj, timeout);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    k_curr_task->flag_expect      = (k_event_flag_t)0u;
    k_curr_task->flag_match       = (k_event_flag_t *)K_NULL;
    k_curr_task->opt_event_pend   = (k_opt_t)0u;

    return pend_state2errno(k_curr_task->pend_state);
}

Send event

TencentOS tinySend event provides two functions, namely: tos_event_post()and tos_event_post_keep(), the two functions are essentially the same function is called event_do_post()to realize the operation that sent the event, only option is different, use the tos_event_post()function will overwrite the specified event, may affect other events that have occurred, and tos_event_post_keep()the function can be maintained simultaneous events other events position does not change, in reality the latter is more commonly used.

This function is used to write event has occurred event flag specified position, when the corresponding bit is set, the task waits for an event likely to be restored, this time need to wait on the event object to traverse the event waiting list to determine whether there are tasks 期望的事件with the current 事件旗标values match, if there is, then wake up the task.

In simple terms, is to set their own definition of the event flag to 1 and see if there are tasks waiting for this event, yes, it will wake up.

TencentOS tinyGood place in the design is simple and low coupling, are calling on both the nature of the interface api event_do_post()functions to the occurrence of an event, but by opt_postdifferent parameters selected a different approach.

In the event_do_post()handler is very simple, the execution of the idea is as follows:

  1. First, determine what manner of events opt_post, if it OPT_EVENT_POST_KEPis used or operation “|”written to the event flag, or direct assignment.
  2. Use TOS_LIST_FOR_EACH_SAFEthe event to traverse the event object to wait on the waiting list, by event_is_match()function to determine whether there are tasks 期望的事件with the current 事件旗标values match, if you call pend_task_wakeup()the function wake corresponding task.
  3. If the wake of waiting tasks assigned to clear the corresponding event, then clears the flag value of the event.
  4. Finally, a task scheduling knl_sched().
__STATIC__ k_err_t event_do_post(k_event_t *event, k_event_flag_t flag, opt_event_post_t opt_post)
{
    TOS_CPU_CPSR_ALLOC();
    k_task_t *task;
    k_list_t *curr, *next;

#if TOS_CFG_OBJECT_VERIFY_EN > 0u
    if (!pend_object_verify(&event->pend_obj, PEND_TYPE_EVENT)) {
        return K_ERR_OBJ_INVALID;
    }
#endif

    if (opt_post == OPT_EVENT_POST_KEP) {
        event->flag |= flag;
    } else {
        event->flag = flag;
    }

    TOS_CPU_INT_DISABLE();

    TOS_LIST_FOR_EACH_SAFE(curr, next, &event->pend_obj.list) {
        task = TOS_LIST_ENTRY(curr, k_task_t, pend_list);

        if (event_is_match(event->flag, task->flag_expect, task->flag_match, task->opt_event_pend)) {
            pend_task_wakeup(TOS_LIST_ENTRY(curr, k_task_t, pend_list), PEND_STATE_POST);

            // if anyone pending the event has set the TOS_OPT_EVENT_PEND_CLR, then no wakeup for the others pendig for the event.
            if (task->opt_event_pend & TOS_OPT_EVENT_PEND_CLR) {
                event->flag = (k_event_flag_t)0u;
                break;
            }
        }
    }

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

__API__ k_err_t tos_event_post(k_event_t *event, k_event_flag_t flag)
{
    TOS_PTR_SANITY_CHECK(event);

    return event_do_post(event, flag, OPT_EVENT_POST_CLR);
}

__API__ k_err_t tos_event_post_keep(k_event_t *event, k_event_flag_t flag)
{
    TOS_PTR_SANITY_CHECK(event);

    return event_do_post(event, flag, OPT_EVENT_POST_KEP);
}

I like to focus on it!

I welcome public attention No.

Related code can reply "19" Get back in the public number.
For more information please concern "Things IoT development," the public number!

Guess you like

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