Bear Pie Huawei IoT Operating System LiteOS Kernel Tutorial 05- Mutex Lock
[Copy link]
1. LiteOS Mutexes1.1
. MutexesIn
a multitasking environment, there are often scenarios where multiple tasks compete for the same shared resource. Mutexes can be used to protect shared resources and achieve exclusive access. Mutexes, also known as mutual exclusion semaphores, are a special binary semaphore used to achieve exclusive access to shared resources. In addition, the mutexes provided by Huawei LiteOS solve the priority flipping problem through the priority inheritance algorithm.
1.2. How to use mutexesIn
a multitasking environment, there are scenarios where multiple tasks access the same public resource, and some public resources are non-shared and require tasks to handle them exclusively.
How do mutexes avoid such conflicts?
At any time, there are only two states of a mutex: unlocked and locked.
When a task holds the mutex, it is in the locked state, and the task obtains ownership of the mutex. When the task releases it, the mutex is unlocked, and the task loses ownership of the mutex. When a task holds a mutex, other tasks can no longer unlock or hold the mutex.
Then, when a mutex is locked, other tasks will be blocked if they want to access the common resource. They can only access the common resource again after the mutex is released by the task holding the mutex. At this time, the mutex is locked again, ensuring that only one task is accessing the common resource at the same time, thus ensuring the integrity of the common resource operation.
1.3. Usage scenarios of mutexes Mutexes
can provide a mutual exclusion mechanism between tasks to prevent two tasks from accessing the same shared resource at the same time.
In addition, mutexes can also be used to prevent priority flipping problems caused by multi-task synchronization.
2. Mutex API
The mutex module in the Huawei LiteOS system provides users with the functions of creating/deleting mutexes and acquiring/releasing mutexes.
The mutex APIs provided in the Huawei LiteOS system all start with LOS, but these APIs are more complicated to use, so in this article we use the unified API interface provided by the Huawei IoT Link SDK for experiments. The underlying APIs of these interfaces have been implemented using the API provided by LiteOS, which is more concise for users. The API list is as follows: The
osal API interface is declared in <osal.h>. To use the relevant interface, you need to include this header file. For detailed function parameters, please refer to the declaration of this header file.
The relevant interface definitions are in osal.c, and the LiteOS-based interface implementation is in the liteos_imp.c file:
Interface Name
|
Functional Description
|
osal_mutex_create
|
Creating a Mutex
|
osal_mutex_del
|
Deleting a mutex
|
osal_mutex_lock
|
Get the mutex lock (lock)
|
osal_mutex_unlock
|
Release the mutex (unlock)
|
2.1. osal_mutex_create
The osal_mutex_create interface is used to create a mutex lock. Its interface prototype is as follows:
bool_t osal_mutex_create(osal_mutex_t *mutex)
{
bool_t ret = false;
if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->mutex_create))
{
ret = s_os_cb- >ops->mutex_create(mutex);
}
return ret;
}
The parameters of this interface are described in the following table:
parameter
|
describe
|
mutex
|
The address of the mutex index ID
|
Return Value
|
false - creation failed
|
Return Value
|
true - creation successful
|
2.2. osal_mutex_del
The osal_mutex_del interface is used to delete a mutex lock. Its interface prototype is as follows:
bool_t osal_mutex_del(osal_mutex_t mutex)
{
bool_t ret = false;
if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->mutex_del))
{
ret = s_os_cb-> ops->mutex_del(mutex);
}
return ret;
}
The parameters of this interface are described in the following table:
parameter
|
describe
|
mutex
|
Mutex index ID
|
Return Value
|
false - deletion failed
|
Return Value
|
true - deletion was successful
|
2.3. osal_mutex_lock
The osal_mutex_lock interface is used to obtain a mutex lock. Its interface prototype is as follows:
bool_t osal_mutex_lock(osal_mutex_t mutex)
{
bool_t ret = false;
if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->mutex_lock))
{
ret = s_os_cb-> ops->mutex_lock(mutex);
}
return ret;
}
parameter
|
describe
|
mutex
|
Mutex index ID
|
Return Value
|
false - application failed
|
Return Value
|
true - application successful
|
2.4. osal_mutex_unlock
The osal_mutex_unlock interface is used to release a mutex. If there is a task blocked waiting for the mutex, the earliest blocked task is awakened, the task enters the ready state, and is scheduled.
The interface prototype is as follows:
bool_t osal_mutex_unlock(osal_mutex_t mutex)
{
bool_t ret = false;
if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->mutex_unlock))
{
ret = s_os_cb-> ops->mutex_unlock(mutex);
}
return ret;
}
The parameters of this interface are described in the following table:
parameter
|
describe
|
mutex
|
Mutex index ID
|
Return Value
|
false - release failed
|
Return Value
|
true - release successful
|
3. Hands-on experiment - Using mutex locks for resource protection
Experiment content
In this experiment, two tasks will be created, a low-priority task task1 and a high-priority task task2. The two tasks will lock, operate, and unlock the shared resources in turn, and observe the operation of the two tasks in the serial terminal.
Experiment code
First open the HelloWorld project used in the previous article and conduct experiments based on this project.
Right-click on the Demo folder and create a new folder osal_kernel_demo to store the kernel's experimental files (if you already have one, please ignore this step).
Next, create a new experimental file osal_mutex_demo.c in this folder and start writing code:
/* Use the osal interface and include this header file*/
#include <osal.h>
/* Task priority macro definition (shell task priority is 10) */
#define USER_TASK1_PRI 12 //Low priority
#define USER_TASK2_PRI 11 //High priority
/* Shared resources*/
uint32_t public_value = 0;
/* Mutex index ID */
osal_mutex_t public_value_mutex;
/* Task task1 entry function*/
static int user_task1_entry()
{
while(1)
{
/* Try to acquire the mutex*/
if(true == osal_mutex_lock(public_value_mutex))
{
/* Get the mutex and operate on shared resources*/
printf("\r\ntask1: lock a mutex.\r\n");
public_value = 10;
printf("task1: public_value = %ld.\r\n", public_value);
/* Complete the operation on shared resources and release the mutex*/
printf("task1: unlock a mutex.\r\n\r\n");
osal_mutex_unlock(public_value_mutex);
/* End the task if the condition is met*/
if(public_value > 100)
break;
}
}
/* while(1) will end, so the return value is required*/
return 0;
}
/* Task task2 entry function*/
static int user_task2_entry()
{
while (1)
{
/* Try to obtain the mutex*/
if(true == osal_mutex_lock(public_value_mutex))
{
/* Get the mutex and operate on shared resources*/
printf("\r\ntask2: lock a mutex.\r\n");
public_value = 5;
printf("task2: public_value = %ld.\r\n", public_value);
/* Complete the operation on shared resources and release the mutex*/
printf("task2: unlock a mutex.\r\n\r\n");
osal_mutex_unlock(public_value_mutex);
/* End the task if the condition is met*/
if(public_value > 90)
break;
/* The priority is high, so it needs to be suspended to allow task1 to obtain the mutex, otherwise task2 will be locked again and form a deadlock*/
osal_task_sleep(10);
}
}
/* while(1) will end, so the return value is required*/
return 0;
}
/* Standard demo startup function, do not modify the function name, otherwise it will affect the next experiment*/
int standard_app_demo_main()
{
/* Create a mutex public_value_mutex */
osal_mutex_create(&public_value_mutex);
/* Create task task1 */
osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
/* Create task task2 */
osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
}
After writing, add the osal_mutex_demo.c file we wrote to the makefile and add it to the compilation of the entire project:
Here is a relatively simple method, directly modify the user_demo.mk configuration file in the Demo folder and add the following code:
#example for osal_mutex_demo
ifeq ($(CONFIG_USER_DEMO), "osal_mutex_demo")
user_demo_src = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_mutex_demo.c}
endif
The adding position is shown in the figure:
This code means:
If the value of the CONFIG_USER_DEMO macro definition is osal_mutex_demo , then add the osal_mutex_demo.c file to the makefile for compilation.
So, how to configure the CONFIG_USER_DEMO macro definition? You can configure it at the end of the .sdkconfig file in the root directory of the project: Because we have modified the mk configuration file, click the Recompile button to compile, and after the compilation is complete, click the Download button to burn the program. Experimental phenomenon After the program is burned, you can see that the program has started running, and the output content of the experiment can be seen in the serial terminal:
linkmain:V1.2.1 AT 11:30:59 ON Nov 28 2019
WELCOME TO IOT_LINK SHELL
LiteOS:/>
task2: lock a mutex.
task2: public_value = 5.
task2: unlock a mutex.
task1: lock a mutex.
task1: public_value = 15.
task1: unlock a mutex.
task1: lock a mutex.
task1: public_value = 25.
task1: unlock a mutex.
task2: lock a mutex.
task2: public_value = 30.
task2: unlock a mutex.
task1: lock a mutex task1
: public_value = 40.
task1: unlock a mutex.
task1: lock a mutex.
task1: public_value = 50.
task1: unlock a mutex.
task2: lock a mutex.
task2: public_value = 55.
task2: unlock a mutex.
task1 : lock a mutex.
task1: public_value = 65.
task1: unlock a mutex.
task1: lock a mutex.
task1: public_value = 75.
task1: unlock a mutex.
task2: lock a mutex.
task2: public_value = 80.
task2: unlock a mutex.
task1: lock a mutex.
task1: public_value = 90.
task1: unlock a mutex.
task1: lock a mutex.
task1: public_value = 100.
task1: unlock a mutex.
task2: lock a mutex.
task2: public_value = 105.
It can be seen that after the system starts, the version number is printed first. The priority of the serial shell is 10, and the shell information is printed first. Next, task1 is created first, but its priority is lower, so the later created task2 preempts the execution. Task2 obtains the mutex lock, operates on the shared resources, unlocks after the operation is completed, and then actively suspends. Task1 obtains the mutex lock, performs another operation on the shared resources, unlocks after the operation is completed. When task1 is operating, task2 has already been suspended, but cannot obtain the mutex lock, so it suspends and waits. After task1 is unlocked, the blocked task2 is awakened and starts to execute. The
Bear Pie Open Source Community focuses on sharing cutting-edge technologies such as IoT, AI, and 5G. Follow the "Bear Pie Open Source Community" WeChat official account to get more information and tutorials.
--------------------------------------------------END--------------------------------------------
|