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 ( pend
time), 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
post
amount signal operation, and may have the task of blocking on this semaphore. - Value: indicates that one or more of the
post
semaphore 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 tiny
Block 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_t
types pend_obj
and k_sem_cnt_t
types count
. While pend_obj
somewhat 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 count
it 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 tiny
Implemented semaphores is very simple, with only the core code 125
line 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_obj
member variables are initialized, it is identified as resource types PEND_TYPE_SEM
. Then sem->count
setting 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:
- Call the
pend_is_nopending()
function to determine whether there are tasks waiting for the semaphore - 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 variablepend_state
isPEND_STATE_DESTROY
). - Call
pend_object_deinit()
function semaphore control content block to clear the most important is the control block resource type is setPEND_TYPE_NONE
, so the child will not be able to use a semaphore. - Task scheduling
knl_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 timeout
specified 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:
- First detected incoming parameters are correct.
- Analyzing the signal to control the amount of block
count
member variable is greater than0
, greater than 0 indicates the amount of available signal is present, thecount
member variable value减1
acquisition task after a successful returnK_ERR_NONE
. - If there is no semaphore may block the acquisition of the current task, look at the user-specified blocking time
timeout
whether or not to blockTOS_TIME_NOWAIT
, if not blocked directly returnsK_ERR_PEND_NOWAIT
an error code. - 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 to do it
pend_state2errno()
, it represents任务等获取到信号量
, or等待发生了超时
, then call thepend_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_NONE
then 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 count
value 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 count
value 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 count
value of a member variable to 减1
this to a gyrus, block amount control signal count
value of a member variable is not changed.
TencentOS tiny
You 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 tiny
Where good design is simple and low coupling, these two calls are api interfaces essentially sem_do_post()
function to release the semaphore, but by opt
different parameters selecting different processing methods.
In the sem_do_post()
handler is very simple, the execution of the idea is as follows:
- First, determine what semaphore has overflowed because of an integer overflow always will, can not always release the semaphore so that
count
the member variable value加1
it, it is necessary to determine what the overflow, ifsem->count
the value(k_sem_cnt_t)-1
, then has overflowed, unable to continue release the semaphore, the error codes returned K_ERR_SEM_OVERFLOW. - Call the
pend_is_nopending()
function to determine whether there are tasks waiting for the semaphore, if not then thecount
value of the member variables加1
, returnK_ERR_NONE
shows the release semaphore success, because at this time there is no need to not wake task scheduling, can be returned directly. - If there are tasks waiting for the semaphore, the
count
value of a member variable无需加1
, direct calls topend_wakeup
the corresponding task to wake up, wake task is based onopt
wakeup parameters, you can wake up a task or is waiting for all tasks. - 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->count
is (k_sem_cnt_t)-1
on 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!
Related code can reply "19" Get back in the public number.
For more information please concern "Things IoT development," the public number!