In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "the principle and example usage of TencentOS tiny mutex lock". In daily operation, I believe that many people have doubts about the principle and example usage of TencentOS tiny mutex lock. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "the principle and example usage of TencentOS tiny mutex lock". Next, please follow the editor to study!
Mutex lock
Mutex, also known as mutex, is a special semaphore, which is different from semaphore in that it has the characteristics of mutex ownership, recursive access and priority inheritance, and is often used to deal with critical resources exclusively in operating systems. There are only two states of mutex at any time, unlocking or locking. When the mutex is held by the task, the mutex is locked, and when the task releases the mutex, the mutex is unlocked.
A task holding a mutex means that it has ownership of the mutex, only the task can release the mutex, and other tasks will not be able to hold the mutex, which is the ownership characteristic of the mutex.
When the task that holds the mutex acquires the mutex again, it will not be suspended, but can be acquired recursively, which is the recursive access characteristic of the mutex. This characteristic is very different from the general semaphore, in the semaphore, because there is no available semaphore, when the task recursively obtains the semaphore, it will suspend the task and eventually form a deadlock.
Mutex also has a priority inheritance mechanism, which can temporarily raise the priority of low-priority tasks to the same priority as the high-priority tasks that acquire mutexes, and reduce the harm of priority inversion as much as possible.
In practical applications, semaphores can be used if you want to achieve synchronization. Although mutexes can also be used for task-to-task synchronization, mutexes are more often used for mutually exclusive access to critical resources.
When using a mutex to protect a critical resource, the task must first acquire the mutex to gain ownership of the resource, and when the task uses the resource, the mutex must be released so that other tasks can access the resource (priority reversal may occur when semaphores are used to protect critical resources, and the harm is uncontrollable).
Priority flipping
To put it simply, high-priority tasks are waiting for low-priority tasks to finish, which has gone against the original intention of the design of the operating system (preemptive scheduling).
Why does the priority reversal occur?
When a critical resource in the system is protected by a mutex, the task needs to obtain the mutex when accessing the resource. If the resource is being used by a low priority task, the mutex is locked. If a high-priority task wants to access the resource at this time, the high-priority task will enter the blocking state because it cannot acquire the mutex, and the high-priority task is waiting for the low-priority task to run (waiting for it to release the mutex).
Priority flipping is harmful. The priority of the task is assigned at the beginning of the design of the system, and the more important the task is, the higher the priority is, but after the priority flipping occurs, the high-priority task is waiting for the low-priority task. This may prevent the high-priority tasks from being handled effectively, and may cause the system to crash in serious cases.
The harm of priority flipping is uncontrollable, because low-priority tasks are likely to be preempted by other intermediate priority tasks in the system (priority tasks between low-priority and high-priority tasks). This may lead to a situation in which high-priority tasks will wait for all intermediate-priority tasks to finish running, which is unacceptable for high-priority tasks and violates the principles of operating system design.
Priority inheritance
The priority inheritance algorithm is used in TencentOS tiny to reduce the harm caused by priority flipping: temporarily increase the priority of the low-priority task that occupies a critical resource so that it is equal to the highest priority task of all the tasks waiting for the resource, and when the low-priority task finishes executing and releases the resource Priority restores the initial setting (here it can be seen that a low-priority task temporarily inherits the priority of a high-priority task). Therefore, tasks that inherit priorities prevent system resources from being preempted by any intermediate priority tasks. The priority inheritance mechanism of mutex, which ensures that high-priority tasks enter the blocking state for as short as possible, and minimizes the harm caused by priority flipping, but does not eliminate the harm caused by priority flipping.
It is worth mentioning that TencentOS tiny also allows you to call the API interface that modifies the priority of a task when holding a mutex.
Data structure of mutex lock mutex control block
TencentOS tiny operates the mutex through the mutex control block, whose data type is k_mutex_t, and the mutex control block is composed of multiple elements.
Pend_obj is somewhat similar to object-oriented inheritance, inheriting properties that describe the types of kernel resources (such as mutexes, queues, mutexes, etc., as well as a waiting list list).
Pend_nesting is actually a variable of type uint8_t, which records the number of times the mutex is acquired. If pend_nesting is 0, it indicates the unlocked state, and if it is not 0, it indicates the locked state, and its value records the number of times the mutex is acquired (with recursive access).
* owner is a task control block pointer that records which task holds the current mutex.
The owner_orig_prio variable records the priority of the task holding the mutex and temporarily changes the priority of the task because of the possibility of a priority inheritance mechanism. (has priority inheritance mechanism).
Owner_list is a list node, which is added to the task- > mutex_own_list list of the task control block when the mutex is acquired by the task, indicating which mutexes are acquired by the task itself. When the mutex is fully released (pend_nesting equals 0), the node is removed from the task- > mutex_own_list list of the task control block.
The prio_pending variable is interesting: in a general operating system, a task is not allowed to change its priority when it holds a mutex, but it is allowed in TencentOS tiny. Because of this variable, when a task is in a mutex priority inversion, I assume that it has been raised because of priority inversion, and a user is trying to change the priority of the task. However, this changed priority will make the task violate the principle that its priority must be higher than all tasks waiting for the mutex held by him. At this time, it is necessary to record the user's priority change request in prio_pending and change it after the mutex is released, which is tantamount to delaying the change of task priority.
For example: for example, if a task has a priority of 10 and holds a lock, and a task with priority 6 tries to acquire the lock, the task priority will be raised to 6. If the user tries to change his priority to 7, then the user cannot respond to the request immediately, and the real priority modification cannot be made until the lock is released.
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;} karma mutexistenttypedef 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;与互斥锁相关的宏定义 在tos_config.h中,使能互斥锁的宏定义是TOS_CFG_MUTEX_EN #define TOS_CFG_MUTEX_EN 1u创建互斥锁 系统中每个互斥锁都有对应的互斥锁控制块,互斥锁控制块中包含了互斥锁的所有信息,比如它的等待列表、它的资源类型,以及它的互斥锁值,那么可以想象一下,创建互斥锁的本质是不是就是对互斥锁控制块进行初始化呢?很显然就是这样子的。因为在后续对互斥锁的操作都是通过互斥锁控制块来操作的,如果控制块没有信息,那怎么能操作嘛~ 创建互斥锁函数是tos_mutex_create(),传入一个互斥锁控制块的指针*mutex即可。 互斥锁的创建实际上就是调用pend_object_init()函数将互斥锁控制块中的mutex->The pend_obj member variable is initialized and its resource type is identified as PEND_TYPE_MUTEX. Then set the mutex- > pend_nesting member variable to 0, indicating that the mutex is unlocked; set the mutex- > owner member variable to K_NULL, which means that no task holds the mutex; set the mutex- > owner_orig_prio member variable to the default value K_TASK_PRIO_INVALID, after all, no task holds the mutex, and there is no need to record the priority of the task holding the mutex. Finally, the tos_list_init () function is called to owner_list the list node of the mutex holding the mutex task.
_ _ 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 = Kendall null; mutex- > owner_orig_prio = Kathaskian PRIONVALID; tos_list_init (& mutex- > owner_list) Return mutex;} destroy mutex
The mutex termination function is directly terminated according to the mutex control block. After the mutex is terminated, all the information of the mutex will be cleared, and the mutex cannot be used again. When the mutex is destroyed, there are tasks in the waiting list. It is necessary for the system to wake up these waiting tasks and inform the task that the mutex has been destroyed PEND_STATE_DESTROY. A task schedule is then generated to switch to the highest priority task execution.
The processing flow for mutex termination by TencentOS tiny is as follows:
Call the pend_is_nopending () function to determine whether a task is waiting for a mutex
If so, the pend_wakeup_all () function is called to wake up these tasks and tell them that the waiting task mutex has been destroyed (that is, set the wait state member variable pend_state in the task control block to PEND_STATE_DESTROY).
Call the pend_object_deinit () function to clear the contents of the mutex control block, most importantly by setting the resource type in the control block to PEND_TYPE_NONE, so that the mutex cannot be used.
Restore the mutex- > pend_nesting member variable to the default value of 0.
If a task holds a mutex when it is deleted, the mutex_old_owner_release () function is called to release the mutex.
Perform task scheduling knl_sched ()
Note: if the RAM of the mutex control block is statically allocated by the compiler, this memory cannot be freed even if the mutex is destroyed. Of course, you can also use dynamic memory to allocate memory for mutex control blocks, but release the memory after destruction 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 Knowled OBJINVALID;} # 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 mutex;} acquire mutex
The tos_mutex_pend_timed () function is used to acquire mutexes, which are like tokens of critical resources, and tasks can access critical resources only when they acquire mutexes. When and only when the mutex is unlocked, the task can obtain the mutex successfully. when the task holds a mutex, other tasks cannot acquire the mutex and need to wait until the task holding the mutex is released. other tasks can be successful. The task acquires the ownership of the mutex through the mutex acquisition function, and the task's ownership of the mutex is exclusive. A mutex can only be held by one task at any time. If the mutex is unlocked, the task that acquires the mutex will successfully acquire the mutex and have the right to use the mutex If the mutex is locked, the task that acquires the mutex will not be able to obtain the mutex, and the task may be blocked or returned immediately. The blocking time timeout is specified by the user. When the mutex cannot be acquired at the specified time, the timeout will be sent, and the waiting task will automatically return to ready state. Priority inheritance occurs before the task is blocked, and if the current task has a higher priority than the task that holds the mutex, the priority of the task holding the mutex will be temporarily raised.
TencentOS tiny provides two sets of API interfaces for acquiring mutexes, namely tos_mutex_pend_timed () and tos_mutex_pend (). The main difference is that the parameters are different: optional blocking and permanent blocking acquire mutexes, and the actual acquisition process is the same. The procedure for obtaining a mutex is as follows:
First check whether the passed parameters are correct. Here, not only the information of the mutex control block is checked, but also TOS_IN_IRQ_CHECK () is called to check whether the context is in an interrupt, because mutex operations are not allowed in the interrupt.
Determine whether the mutex- > pend_nesting member variable in the mutex control block is 0. A value of 0 means that the mutex is in the unlocked state. Call the mutex_fresh_owner_mark () function to save the relevant information about obtaining the mutex task to the mutex control block. For example, if the value of the mutex- > pend_nesting member variable changes to 1, the mutex is locked and cannot be obtained by other tasks. The mutex- > owner member variable points to the task control block that currently acquires the mutex, while the mutex- > owner_orig_prio member variable records the priority of the current task. Finally, the mutex- > owner_list node of the mutex control block is mounted to the task- > mutex_own_list list of the task control block using the tos_list_add () function. When the task is successfully acquired, the K_ERR_NONE is returned.
If the mutex- > pend_nesting member variable in the mutex control block is not 0, it means that the mutex is locked. Since the mutex has recursive access, it will determine whether the task that already holds the mutex acquires the mutex (knl_is_self ()) again, because this is also allowed. Determine whether the value of the mutex- > pend_nesting member variable is (k_nesting_t)-1. If so, it indicates that the maximum number of recursive visits has been reached, the mutex has been overflowed, and the error code K_ERR_MUTEX_NESTING_OVERFLOW is returned. Otherwise, add 1 to the value of the mutex- > pend_nesting member variable, and return K_ERR_MUTEX_NESTING to indicate that the recursive acquisition was successful.
If the mutex is locked and the current task does not hold the mutex, check whether the blocking time timeout specified by the user is not blocking TOS_TIME_NOWAIT, and return the K_ERR_PEND_NOWAIT error code directly if it is not blocked.
If the scheduler is locked knl_is_sched_locked (), the wait operation cannot be performed and the error code K_ERR_PEND_SCHED_LOCKED is returned. After all, the task needs to be switched, and the scheduler cannot switch tasks if the scheduler is locked.
The most important feature is that before blocking the current task, you need to determine the priority of the current task and the task holding the mutex. If the priority of the current task is higher than that of the task holding the mutex, you need to inherit the priority, temporarily raise the priority of the task holding the mutex to the current priority, and change the priority through the tos_task_prio_change () function.
Call the pend_task_block () function to block the task, which actually removes the task from the ready list and inserts it into the waiting list object- > list. If the waiting time is not permanent waiting for TOS_TIME_FOREVER, it inserts the task into the time list k_tick_list with a blocking time of timeout, and then makes a task scheduling knl_sched ().
When the program can reach pend_state2errno (), it means that the task has acquired the mutex, or the wait timeout has occurred. Then call the pend_state2errno () function to get the wait status of the task to see what caused the task to resume running. If the task has acquired the mutex, you need to call the mutex_new_owner_mark () function to mark the task information. The information about the task of acquiring the mutex is saved to the mutex control block.
Note: when the task of acquiring a mutex can resume running from blocking, it may not necessarily acquire the mutex, or it may have timed out, so it is necessary to judge the status of the acquired mutex when writing the program. If the returned values are K_ERR_NONE and K_ERR_MUTEX_NESTING, the acquisition is successful!
_ _ 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 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 if (knl_is_sched_locked ()) {TOS_CPU_INT_ENABLE (); return K_ERR_PEND_SCHED_LOCKED } if (mutex- > owner- > prio > kicking currents 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, kicking currents task-> prio);} pend_task_block (k_curr_task, & mutex- > pend_obj, timeout); TOS_CPU_INT_ENABLE (); knl_sched (); err = pend_state2errno (kicking currents 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);}
The implementation of mutex_fresh_owner_mark and mutex_new_owner_mark () 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
The release of mutex is not allowed to be released in an interrupt, the main reason is that the interrupt context cannot be held or released because there is no concept of context in the interrupt. Only the task that holds the mutex can release the mutex, and the holder is the task.
When a task wants to access a resource, it needs to acquire the mutex first, and then access the resource. when the task finishes using the resource, it must release the mutex in time so that other tasks can access the critical resource. The task can call the tos_mutex_post () function to release the mutex. When the mutex is unlocked, it means that I have run out of it, and others can acquire the mutex to access the critical resources.
When using the tos_mutex_post () function interface, only the task that already owns the mutex can release it. When the task calls the tos_mutex_post () function, the mutex will be released once, and when the mutex is fully released (the value of the mutex- > pend_nesting member variable is 0), it will become unlocked, and the task waiting to acquire the mutex will be awakened. If the priority of the task is temporarily raised by the priority flipping mechanism of the mutex, then when the mutex is released, the priority of the task will be restored to the priority originally set.
In TencentOS tiny, only one task while waiting can get the mutex (with the highest priority among the waiting tasks).
The processing in the tos_mutex_post () function is also very simple and straightforward, and its execution ideas are as follows:
First, check the parameters related to the incoming mutex control block, and then determine whether the task holding the mutex releases the mutex, if so, release the mutex, and if not, return the error code K_ERR_MUTEX_NOT_OWNER.
Subtract the value of the mutex- > pend_nesting member variable by 1, and then determine whether its value is 0. If it is not 0, it means that the current task still holds a mutex, and returns K_ERR_MUTEX_NESTING directly without any subsequent operation.
If the value of the mutex- > pend_nesting member variable is 0, it means that the mutex is unlocked. You need to call the mutex_old_owner_release () function to release the mutex completely, in which the member variables of the mutex control block (such as owner_list, owner, owner_orig_prio, etc.) are set to the initial values, and the most important thing is to determine whether priority inheritance has occurred in the task. If so, you need to restore the task to its original priority, otherwise it will be invalid.
Call the pend_is_nopending () function to determine whether a task is waiting for the mutex. If not, returning K_ERR_NONE indicates that the mutex has been released successfully, because if there is no wake-up task, there is no need for task scheduling, and you can simply return.
If a task is waiting for a mutex, the pend_wakeup_one () function is called directly to wake up a waiting task, which is the highest priority among waiting tasks, because TencentOS tiny's waiting tasks are sorted by priority.
Perform 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 Knowled OBJINVALID;} # endif TOS_CPU_INT_DISABLE () If (! knl_is_self (mutex- > owner)) {TOS_CPU_INT_ENABLE (); return KnowledgeMUTEXNOTOWNERER;} if (mutex- > pend_nesting = = (k_nesting_t) 0u) {TOS_CPU_INT_ENABLE (); return Kinkerred MUTEXNESTINGOVERLOW;}-- mutex- > pend_nesting If (mutex- > pend_nesting > (k_nesting_t) 0u) {TOS_CPU_INT_ENABLE (); return Knowlewer MUTEXNESTING;} 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 Knowerr é None;}
The task that holds the mutex releases 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 = Knavull; / / 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 = Knights TASKPRIOPRION owner_orig_prio;} 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 }} at this point, the study of "the principle and example usage of TencentOS tiny mutex" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.