[TencentOS tiny source depth analysis] (5) - semaphore

signal

Semaphore ( sem) in the operating system is an implementation of the system tasks and tasks, between tasks and interrupt synchronization or mutual exclusion mechanism for the protection of critical resources. In a multitasking system, often require synchronization or mutual exclusion between tasks, semaphores can provide support in this respect that user.

Abstract, a semaphore is a non-negative integer, every time the semaphore is acquired ( pendtime), the integer is decremented by one, and when the integer value 0, it indicates that the semaphore is in the inactive state, it can not be acquired again, all its task will be trying to get into the blocking state. Semaphore is usually a count value, the count value may be used which system resource count (count).

Generally, there are two semaphore value:

  • 0: no accumulated postamount signal operation, and may have the task of blocking on this semaphore.
  • Value: indicates that one or more of the postsemaphore operation.

Generally semaphore used for synchronization and not mutually exclusive, as the operating system will provide another mechanism for mutual exclusion (mutex), the role of mutual exclusion mutex better: mutex have priority inheritance mechanism, the semaphores do not have this mechanism, in addition to the owner of the property also has a mutex, we will explain the follow-up.

The queue as semaphores, have 阻塞机制. Tasks need to wait for an interrupt occurs, the corresponding processing is executed again, the task can be in a blocking state semaphore wait until after the interruption occurred after the release of the semaphore, the task is woken up to execute the corresponding processing. Release ( post) semaphore when the task immediately will wait for changes to the ready state, if the highest priority task in the ready task, the task can be executed immediately, this is the operating system " 实时响应,实时处理." Semaphore used in the operating system can improve the efficiency of the process.

Semaphore data structure

Semaphore control block

TencentOS tinyBlock operation controlled by a semaphore semaphore, which is a data type k_sem_t, a plurality of semaphore control block elements, there are pend_obj_ttypes pend_objand k_sem_cnt_ttypes count. While pend_objsomewhat similar to the object-oriented inheritance, some properties, there are described the type of kernel resources (such as semaphores, queues, mutexes, etc, and a waiting list list). And countit is a simple variable (which is a 16-bit unsigned integer), represents the value of the semaphore.

typedef struct k_sem_st {
    pend_obj_t      pend_obj;
    k_sem_cnt_t     count;
} k_sem_t;

Macro definitions associated with the semaphore

In tos_config.h, the amount of the macros which signal isTOS_CFG_SEM_EN

#define TOS_CFG_SEM_EN              1u

Semaphore achieve

TencentOS tinyImplemented semaphores is very simple, with only the core code 125line can be said to be very less.

Create Semaphore

The amount of signals in the system has a corresponding semaphore each control block, the amount of signal in the control block contains all the information amount of the signal, such as its waiting list that resource type, as well as its signal magnitude, then imagine , nature is not to create a semaphore semaphore control block is to initialize it? Obviously it is like that. Since the subsequent operation of the block control signal by an amount of the semaphore is operated, the control block if there is no information, how it can operate Well ~

Create a semaphore function tos_sem_create(), with two arguments, a semaphore is a pointer to the control block *sem, the other is the initial value of the semaphore init_count, the non-negative integer value can, but mainly not exceed 65535.

Actually calling pend_object_init()function to control the amount of signal block sem->pend_objmember variables are initialized, it is identified as resource types PEND_TYPE_SEM. Then sem->countsetting a member variable to the initial value of the amount of the signal passed in init_count.

__API__ k_err_t tos_sem_create(k_sem_t *sem, k_sem_cnt_t init_count)
{
    TOS_PTR_SANITY_CHECK(sem);

    pend_object_init(&sem->pend_obj, PEND_TYPE_SEM);
    sem->count = init_count;
    
    return K_ERR_NONE;
}

Destroy Semaphore

Semaphore destructor is a block according to a signal amount control directly destroyed, all semaphores after destruction will be cleared, and can not use the semaphore again, when the signal amount is destroyed, it waits there is a task list, the system is necessary these tasks will wait wake up and inform the task semaphore has been destroyed PEND_STATE_DESTROY. And then generates a task scheduler to switch to the highest priority task execution.

TencentOS tiny Semaphore destruction process is as follows:

  1. Call the pend_is_nopending()function to determine whether there are tasks waiting for the semaphore
  2. If so, call pend_wakeup_all()the function will wake up these tasks, and wait for the task to inform the semaphore has been destroyed (ie set the wait state task control block member variable pend_stateis PEND_STATE_DESTROY).
  3. Call pend_object_deinit()function semaphore control content block to clear the most important is the control block resource type is set PEND_TYPE_NONE, so the child will not be able to use a semaphore.
  4. Task schedulingknl_sched()

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

__API__ k_err_t tos_sem_destroy(k_sem_t *sem)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_PTR_SANITY_CHECK(sem);

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

    TOS_CPU_INT_DISABLE();

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

    pend_object_deinit(&sem->pend_obj);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

Acquire Semaphore

tos_sem_pend()Function is used to obtain the semaphore when the semaphore effective when the task in order to obtain the semaphore. When a task acquires the semaphore, the semaphore number minus one is available, when it is 0, the task of acquiring the semaphore will enter the blocking state, blocking time timeoutspecified by the user, can not obtain the signal at the specified time the amount will be sent timeout wait task will automatically revert to the ready state.

Get semaphore procedure is as follows:

  1. First detected incoming parameters are correct.
  2. Analyzing the signal to control the amount of block countmember variable is greater than 0, greater than 0 indicates the amount of available signal is present, the countmember variable value 减1acquisition task after a successful return K_ERR_NONE.
  3. If there is no semaphore may 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.
  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 to do it pend_state2errno(), it represents 任务等获取到信号量, or 等待发生了超时, then call the pend_state2errno()function to get about the wait state task is to look at what has led to the recovery task to run, and the result is returned to the calling obtain a semaphore task.

Note: When obtaining the semaphore from the task of blocking the recovery operation, not necessarily to get the semaphore, it could be a timeout has occurred, so when writing the program must determine what the state of the semaphore acquisition, if it is K_ERR_NONEthen It represents succeed!

__API__ k_err_t tos_sem_pend(k_sem_t *sem, k_tick_t timeout)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_PTR_SANITY_CHECK(sem);
    TOS_IN_IRQ_CHECK();

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

    TOS_CPU_INT_DISABLE();

    if (sem->count > (k_sem_cnt_t)0u) {
        --sem->count;
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (timeout == TOS_TIME_NOWAIT) { // no wait, return immediately
        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, &sem->pend_obj, timeout);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return pend_state2errno(k_curr_task->pend_state);
}

Release Semaphore

Task or interrupt service routine can release the semaphore (post), release the nature of the semaphore is a semaphore control block of the countvalue of a member variable 加1, a signal representing the amount effective, but if there are tasks while waiting for the semaphore, the semaphore control block countvalue of the member variables will not change, because the task waiting to wake up, wake up and wait for the nature of the task is to wait for the task to acquire the semaphore, the semaphore control block of the countvalue of a member variable to 减1this to a gyrus, block amount control signal countvalue of a member variable is not changed.

TencentOS tinyYou can only make a task waiting to acquire a semaphore, but also allows all tasks are waiting to obtain the semaphore. Corresponding to each API is tos_sem_post()with tos_sem_post_all(). Incidentally point, tos_sem_post_all()the design pattern is actually observer mode, when an observation object changes, then all observers will know it has changed, and specifically look at the "Westward Design Patterns" book.

TencentOS tinyWhere good design is simple and low coupling, these two calls are api interfaces essentially sem_do_post()function to release the semaphore, but by optdifferent parameters selecting different processing methods.

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

  1. First, determine what semaphore has overflowed because of an integer overflow always will, can not always release the semaphore so that countthe member variable value 加1it, it is necessary to determine what the overflow, if sem->countthe value (k_sem_cnt_t)-1, then has overflowed, unable to continue release the semaphore, the error codes returned K_ERR_SEM_OVERFLOW.
  2. Call the pend_is_nopending()function to determine whether there are tasks waiting for the semaphore, if not then the countvalue of the member variables 加1, return K_ERR_NONEshows the release semaphore success, because at this time there is no need to not wake task scheduling, can be returned directly.
  3. If there are tasks waiting for the semaphore, the countvalue of a member variable 无需加1, direct calls to pend_wakeupthe corresponding task to wake up, wake task is based on optwakeup parameters, you can wake up a task or is waiting for all tasks.
  4. To conduct a task scheduling knl_sched().
__API__ k_err_t tos_sem_post(k_sem_t *sem)
{
    TOS_PTR_SANITY_CHECK(sem);

    return sem_do_post(sem, OPT_POST_ONE);
}

__API__ k_err_t tos_sem_post_all(k_sem_t *sem)
{
    TOS_PTR_SANITY_CHECK(sem);

    return sem_do_post(sem, OPT_POST_ALL);
}

__STATIC__ k_err_t sem_do_post(k_sem_t *sem, opt_post_t opt)
{
    TOS_CPU_CPSR_ALLOC();

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

    TOS_CPU_INT_DISABLE();

    if (sem->count == (k_sem_cnt_t)-1) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_SEM_OVERFLOW;
    }

    if (pend_is_nopending(&sem->pend_obj)) {
        ++sem->count;
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    pend_wakeup(&sem->pend_obj, PEND_STATE_POST, opt);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

About why the judge sem->countis (k_sem_cnt_t)-1on behalf of the overflow of it? Let me give a simple example in C:

#include <stdio.h>

int main()
{
    unsigned int a = ~0;
    if(a == (unsigned int)0XFFFFFFFF)
    {
        printf("OK\n");
    }
    if(a == (unsigned int)-1)
    {
        printf("OK\n");
    }
    
   printf("unsigned int a = %d \n",a);
   
   return 0;
}

输出:
OK
OK
unsigned int a = -1 

to sum up

Code lean and short, clear thinking, highly recommended depth study ~

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/11688950.html