Mutex
Also called a mutex mutex mutex is a special kind of signals, semaphores, and it is different, it has 互斥锁所有权、递归访问以及优先级继承
other characteristics, the critical resources for the operating system often 独占式
treated. Mutex at any time of only two states, 开锁或闭锁
when the mutex is held by the task, the mutex in the locked state, when the task releases the mutex, the mutex is unlocked.
- A task holds a mutex says it owns the mutex, the task can only release the mutex, while other tasks will not hold the mutex, this is the mutex
所有权
characteristics. - When the task of holding the mutex acquire the mutex again will not be suspended, but can be acquired recursively, this is the mutex
递归访问
characteristics. The general characteristics of a signal amount are very different, in the signal amount, since the amount of available signal does not exist, recursion occurs when the task acquires the semaphore eventually pending tasks死锁
. - Mutex also has a
优先级继承
mechanism that can be低
priority of the task临时提升
to get the mutex高
priority of the task相同
, as far as possible降低
the harm of priority inversion.
In practical applications, if you want to achieve synchronization, you can use semaphores, synchronization between tasks and tasks Although a mutex can also be used, but more mutex is exclusive access to critical resources.
When using mutex protection of critical resources, tasks must obtain to obtain ownership of the mutex to access the resource, when the task using the resource, you must release the mutex so other tasks can access the resource (using a semaphore to protect critical the priority inversion may occur when resources, and 危害
is not controllable).
Priority inversion
Is simply a high-priority task is waiting for a low-priority task is finished, it has violated the operating system is designed (preemptive scheduling).
Why it happened priority inversion?
When the system is in a critical resource protection by a mutex, mutex when tasks need to access the resource, if the resource is being a 低优先级
use task, this time mutex in the locked state; if this time a 高优先级
task want to access the resource, then the high-priority task because not obtain exclusive lock into the blocking state, this time to form a high-priority task is waiting for low-priority task to run (wait for it to release the mutex).
Priority inversion is generated 危害
, the design of the system at the beginning of time, it has been designated a priority task, the higher the more important task priority, but the priority inversion occurs, the high-priority task is waiting for low-priority task , which is likely to make a high-priority task of the lack of effective treatment, severe cases may cause the system to crash.
Hazard priority inversion is not controllable, because of the low priority task is likely to be other system 中间优先级
tasks (low priority tasks between the priority and high-priority task) to seize, which may lead to high priority tasks We will wait for all medium priority task has finished running situation, a situation of high-priority task is unacceptable, and it is contrary to the principles of operating system design.
Priority Inheritance
In TencentOS tiny
in order to 降低
harm priority inversion resulting from the use of 优先级继承算法
: temporarily raise the priority of the low priority task possession of some critical resources to make it with all the resources to wait for the task, the highest priority task priority equal, when this low-priority task is finished release the resource priority to restore the initial set value (here can be seen as a low priority task temporarily inherited the high-priority task's priority). Therefore, inheritance priority task to avoid the system resources are preempted any intermediate priority task. Priority inheritance mutex lock mechanism, which ensures high-priority tasks into the blocked state of time as short as possible, and to have emerged "priority inversion" harm 降低到最小
, 但不能消除
priority inversion harm.
It is worth mentioning that TencentOS tiny
allows the caller to modify the priority of the task in holding mutex API interface Shihai.
Mutex data structure
Mutex control block
TencentOS tiny
Mutex operations is controlled by block mutex, which is a data type k_mutex_t
, a plurality of control blocks mutex elements.
pend_obj
Somewhat 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 listlist
).pend_nesting
Is actually auint8_t
type of variable, the number of recording mutex is acquired, and ifpend_nesting
0 indicates an unlocked state, not zero indicates a locked state, and it records the number of times the value of the mutex was obtained (have descend characteristic)*owner
A task control block pointer, the current record is held mutex which task.owner_orig_prio
Mutex variable recording the holding priority of the task, because there may be a priority inheritance mechanism of temporarily change the priority of the task. (Has priority inheritance mechanism).owner_list
Is a list of nodes, when a mutex is acquired task, the node is added to the task control block oftask->mutex_own_list
the list indicates that the task themselves to get what mutex. When the mutex完全
release (pend_nesting等于0
), the node of the task control block from thetask->mutex_own_list
removal list.prio_pending
More interesting variables: operating systems in general, are not allowed to modify the priority tasks while holding mutex lock, butTencentOS tiny
is permissible, because this variable, when a task is in a mutex priority inversion I suppose because he priority inversion priority has improved, there is at this time the user attempts to change the priority of this task, but will make this task a priority after this change contrary to its priority must wait than all of his holdings when the mutex principle task is higher, in which case you need to change the priority of the requesting user to recordprio_pending
where they hold until their release mutex after the change, which is equivalent to the task priority change postponed.For example: like a task priority is 10 and holds a lock, this time a priority task 6 attempts to acquire the lock, then the task priority will be promoted to 6, if the user tries at this time Changed his priority 7, you can not respond to this request immediately, we must wait for the lock to let go when they could make a real priority modification
typedef struct k_mutex_st {
pend_obj_t pend_obj;
k_nesting_t pend_nesting;
k_task_t *owner;
k_prio_t owner_orig_prio;
k_list_t owner_list;
} k_mutex_t;
typedef struct k_task_st {
#if TOS_CFG_MUTEX_EN > 0u
k_list_t mutex_own_list; /**< 任务拥有的互斥量 */
k_prio_t prio_pending; /*< 在持有互斥锁时修改任务优先级将被记录到这个变量中,在释放持有的互斥锁时再更改 */
#endif
} k_task_t;
Macro definitions associated with the mutex
In tos_config.h
the so macros can mutex isTOS_CFG_MUTEX_EN
#define TOS_CFG_MUTEX_EN 1u
Create a mutex
Each system has a corresponding mutex mutex control block mutex control block contains all the information mutex, such as its waiting list that resource type, and its value mutex then imagine, create nature mutex mutex is not that of the control block to initialize it? Obviously it is like that. Because of the mutex in a subsequent operation are controlled by the block mutex operation, if the control information block is not, then how can operate Well ~
Create function is a mutex tos_mutex_create()
, passing a pointer to the control block mutex *mutex
can.
Mutex created actually call the pend_object_init()
function mutex control block mutex->pend_obj
member variable is initialized, it is identified as the resource type PEND_TYPE_MUTEX
. Then the mutex->pend_nesting
member variable is set to 0
indicate the mutex is unlocked; the mutex->owner
member variable to K_NULL
indicate no matter the task mutex is held; the mutex->owner_orig_prio
member variable set to default values K_TASK_PRIO_INVALID
, after all, no matter the task holding mutex lock, and without mutex record holders priority tasks. The final call tos_list_init()
function will hold a list of nodes task mutex mutex owner_list
.
__API__ k_err_t tos_mutex_create(k_mutex_t *mutex)
{
TOS_PTR_SANITY_CHECK(mutex);
pend_object_init(&mutex->pend_obj, PEND_TYPE_MUTEX);
mutex->pend_nesting = (k_nesting_t)0u;
mutex->owner = K_NULL;
mutex->owner_orig_prio = K_TASK_PRIO_INVALID;
tos_list_init(&mutex->owner_list);
return K_ERR_NONE;
}
Destroying a Mutex
Destructor mutex is destroyed under the direct control block mutex, after the destruction of all of the information will be cleared mutex, and can not use the mutex again, when the mutex is destroyed, which is present on a waiting list task, the system is necessary to wake them wait for these tasks, and inform the task mutex has been destroyed PEND_STATE_DESTROY
. And then generates a task scheduler to switch to the highest priority task execution.
TencentOS tiny
Mutex destruction process is as follows:
- Call the
pend_is_nopending()
function to determine whether there are tasks waiting mutex - If so, call
pend_wakeup_all()
the function will wake up these tasks, and wait for the task to inform the mutex lock has been destroyed (ie set the wait state task control block member variablepend_state
isPEND_STATE_DESTROY
). - Call the
pend_object_deinit()
function block content mutex control clearance, the most important is the control block resource type is setPEND_TYPE_NONE
, so that the child can not use the mutex lock. - The
mutex->pend_nesting
recovery member variables to their default values0
. - If you have time to delete the task holds the mutex, then call the
mutex_old_owner_release()
function to release the mutex. - Task scheduling
knl_sched()
Note: If the mutex control RAM block is made 编译器静态分配
of, so even destroyed mutex, this is no way to release the memory. Of course, you can also use dynamic memory control block for the mutex allocate memory, but after the destruction freed want this memory to avoid memory leaks.
__API__ k_err_t tos_mutex_destroy(k_mutex_t *mutex)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(mutex);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (!pend_is_nopending(&mutex->pend_obj)) {
pend_wakeup_all(&mutex->pend_obj, PEND_STATE_DESTROY);
}
pend_object_deinit(&mutex->pend_obj);
mutex->pend_nesting = (k_nesting_t)0u;
if (mutex->owner) {
mutex_old_owner_release(mutex);
}
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
Acquire the mutex
tos_mutex_pend_timed()
Function is used to acquire the mutex mutex is like a token of critical resources, the task can only get access to critical resources when the mutex. If and only if the mutex lock in an unlocked state, in order to acquire the mutex task successfully, when a task holds an exclusive lock when other tasks you can not get the mutex, need to wait until the mutex is held after the release of the task, other tasks in order to succeed, a task acquisition function to obtain ownership of the mutex by mutex, task ownership of the mutex is exclusive, any time a mutex can only be held by a task If the mutex is unlocked, it acquires the mutex task will successfully obtained the mutex, and has the right to use a mutex; task if the mutex in the locked state, will acquire the mutex can not get mutex, the task may be blocked, or it may return immediately, blocking time timeout
is specified by the user, can not get to the mutex at a specified time, it will send a timeout, wait for the task will automatically revert to the ready state. Before the task is blocked, priority inheritance will be high if the current task priority than holding mutex priority task, it will temporarily elevate the priority task of holding a mutex.
TencentOS tiny
API provides two sets of interfaces used to obtain a mutex, respectively, tos_mutex_pend_timed()
and tos_mutex_pend()
the main difference is the different parameters: Optional blocking and permanent blocking mutex lock is acquired, the process of actually acquired are the same. Acquire the mutex is as follows:
- First detected incoming parameters are correct, will not only check information mutex control block here, but also call
TOS_IN_IRQ_CHECK()
to check if the context is in outage because the mutex operations are not allowed in the interrupt. - Analyzing mutex control block
mutex->pend_nesting
member variable is0
, 0 indicates that the mutex is unlocked, the callmutex_fresh_owner_mark()
to save the information acquired mutex function to the task control block mutex, asmutex->pend_nesting
the value of a member variable becomes1
represents the mutex in the locked state, other tasks can not be obtained,mutex->owner
the member variable points to the current acquire the mutex task control block,mutex->owner_orig_prio
member variables is to record the current priority of the task, the end-usetos_list_add()
function blocks mutex controlmutex->owner_list
node mount the task control block oftask->mutex_own_list
the list, and return the task to succeedK_ERR_NONE
. - If the mutex control block
mutex->pend_nesting
member variable is not0
, then the mutex in the locked state, due to the recursive mutex has access characteristics, it will determine what is not already own the mutex task to acquire the mutex again lock (knl_is_self()
), because this is allowed to determine what themutex->pend_nesting
value of a member variable of whether(k_nesting_t)-1
, and if it means that recursive reached the maximum number of visits, mutex has overflowed, and returns an error codeK_ERR_MUTEX_NESTING_OVERFLOW
. Otherwise it will bemutex->pend_nesting
incremented by one member variables, returnK_ERR_MUTEX_NESTING
represents a recursive for success. - If the mutex in the locked state, and the current task does not own the mutex, blocking a look at a user-specified 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. - Most most important feature here, before blocking the current task, you need to determine what the current task and holds task priority size mutex case, if the current task priority than holding mutex lock on task priority, the need for priority inheritance, temporarily holding the mutex task priority boost to the current priority, by
tos_task_prio_change()
change priority function. - 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 task of wait states, is to look at what has led to resume running task, if the task has been to acquire a mutex lock, then you need to callmutex_new_owner_mark()
a function of your experiences saving acquired task information acquired mutex-related information to a task control block mutex.
Note: When acquiring mutex task from the obstruction to resume operation, not necessarily to acquire a mutex, it could be a timeout has occurred, it is necessary to determine what the state of mutex acquisition in written procedures, If the value is K_ERR_NONE
and K_ERR_MUTEX_NESTING
it means to succeed!
__API__ k_err_t tos_mutex_pend_timed(k_mutex_t *mutex, k_tick_t timeout)
{
TOS_CPU_CPSR_ALLOC();
k_err_t err;
TOS_PTR_SANITY_CHECK(mutex);
TOS_IN_IRQ_CHECK();
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (mutex->pend_nesting == (k_nesting_t)0u) { // first come
mutex_fresh_owner_mark(mutex, k_curr_task);
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
if (knl_is_self(mutex->owner)) { // come again
if (mutex->pend_nesting == (k_nesting_t)-1) {
TOS_CPU_INT_ENABLE();
return K_ERR_MUTEX_NESTING_OVERFLOW;
}
++mutex->pend_nesting;
TOS_CPU_INT_ENABLE();
return K_ERR_MUTEX_NESTING;
}
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;
}
if (mutex->owner->prio > k_curr_task->prio) {
// PRIORITY INVERSION:
// we are declaring a mutex, which's owner has a lower(numerically bigger) priority.
// make owner the same priority with us.
tos_task_prio_change(mutex->owner, k_curr_task->prio);
}
pend_task_block(k_curr_task, &mutex->pend_obj, timeout);
TOS_CPU_INT_ENABLE();
knl_sched();
err = pend_state2errno(k_curr_task->pend_state);
if (err == K_ERR_NONE) {
// good, we are the owner now.
TOS_CPU_INT_DISABLE();
mutex_new_owner_mark(mutex, k_curr_task);
TOS_CPU_INT_ENABLE();
}
return err;
}
__API__ k_err_t tos_mutex_pend(k_mutex_t *mutex)
{
TOS_PTR_SANITY_CHECK(mutex);
return tos_mutex_pend_timed(mutex, TOS_TIME_FOREVER);
}
mutex_fresh_owner_mark
And mutex_new_owner_mark()
implementation of the function:
__STATIC_INLINE__ void mutex_fresh_owner_mark(k_mutex_t *mutex, k_task_t *task)
{
mutex->pend_nesting = (k_nesting_t)1u;
mutex->owner = task;
mutex->owner_orig_prio = task->prio;
tos_list_add(&mutex->owner_list, &task->mutex_own_list);
}
__STATIC_INLINE__ void mutex_new_owner_mark(k_mutex_t *mutex, k_task_t *task)
{
k_prio_t highest_pending_prio;
mutex_fresh_owner_mark(mutex, task);
// we own the mutex now, make sure our priority is higher than any one in the pend list.
highest_pending_prio = pend_highest_prio_get(&mutex->pend_obj);
if (task->prio > highest_pending_prio) {
tos_task_prio_change(task, highest_pending_prio);
}
}
Release mutex
Release the mutex is allowed to be released in the interrupt, the main reason is because there is no interruption in the context of the concept, so the interrupt context can not hold, it can not release the mutex; mutex have 所属
relationships only hold each other denounced the task to lock the mutex release, and is the holder of the task.
When the task you want to access a resource, you need to acquire the mutex and resource access, when the task is finished using the resource, it is necessary to 及时
release the mutex so other tasks can access critical resources. Tasks can call the tos_mutex_post()
function to release the mutex when the mutex is unlocked it means I've run out of other people can acquire the mutex to access critical resources.
Use tos_mutex_post()
the time function interface, only the tasks already holds ownership of mutex to release it when the task calls the tos_mutex_post()
function when will release the mutex once, when a mutex is fully released is completed ( mutex->pend_nesting
the value of member variables 0
) becomes unlocked state, waiting to acquire the mutex task will be awakened. If the priority inversion mechanism priority task is temporarily promoted mutex, then when the mutex lock is released, the priority tasks will revert to the original setting of priorities.
TencentOS tiny
You can only make a task waiting to acquire the mutex (with the highest priority task of waiting).
In the tos_mutex_post()
handler is very simple, the execution of the idea is as follows:
- Mutex is first incoming control block parameters related to detection, and then determine what the task is to hold a mutex to release the mutex, if a release operation is performed. If not, return an error code
K_ERR_MUTEX_NOT_OWNER
. - The
mutex->pend_nesting
value of a member variable minus 1, then it is determined whether the value 0, if not zero then the current task holding the mutex is, there is no need for subsequent operation directly returnsK_ERR_MUTEX_NESTING
. - If the
mutex->pend_nesting
member variable is 0, it indicates that the mutex is unlocked, it is necessary to callmutex_old_owner_release()
the function fully relieved mutex, member variables in this function will mutex control block (e.g.,owner_list、owner、owner_orig_prio
are all set to an initial value whether), in addition to the most important task is to determine what had happened priority inheritance, and if you need to restore the original task priority, otherwise invalid ignored. - Call the
pend_is_nopending()
function to determine whether there are tasks waiting mutex, if there is no returnK_ERR_NONE
shows the release mutex 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 mutex, direct call
pend_wakeup_one()
function wakes up a waiting task, the task is waiting for the task at the highest priority, becauseTencentOS tiny
the waiting task is sorted by priority. - To conduct a task scheduling
knl_sched()
.
__API__ k_err_t tos_mutex_post(k_mutex_t *mutex)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(mutex);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) {
return K_ERR_OBJ_INVALID;
}
#endif
TOS_CPU_INT_DISABLE();
if (!knl_is_self(mutex->owner)) {
TOS_CPU_INT_ENABLE();
return K_ERR_MUTEX_NOT_OWNER;
}
if (mutex->pend_nesting == (k_nesting_t)0u) {
TOS_CPU_INT_ENABLE();
return K_ERR_MUTEX_NESTING_OVERFLOW;
}
--mutex->pend_nesting;
if (mutex->pend_nesting > (k_nesting_t)0u) {
TOS_CPU_INT_ENABLE();
return K_ERR_MUTEX_NESTING;
}
mutex_old_owner_release(mutex);
if (pend_is_nopending(&mutex->pend_obj)) {
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
pend_wakeup_one(&mutex->pend_obj, PEND_STATE_POST);
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
Task holds a mutex release the mutex mutex_old_owner_release()
.
__STATIC_INLINE__ void mutex_old_owner_release(k_mutex_t *mutex)
{
k_task_t *owner;
owner = mutex->owner;
tos_list_del(&mutex->owner_list);
mutex->owner = K_NULL;
// the right time comes! let's do it!
if (owner->prio_pending != K_TASK_PRIO_INVALID) {
tos_task_prio_change(owner, owner->prio_pending);
owner->prio_pending = K_TASK_PRIO_INVALID;
} else if (owner->prio != mutex->owner_orig_prio) {
tos_task_prio_change(owner, mutex->owner_orig_prio);
mutex->owner_orig_prio = K_TASK_PRIO_INVALID;
}
}
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!