Abstract: This article leads you to analyze the source code of the mutex module of the Hongmeng Light Kernel, including the structure of the mutex, the initialization of the mutex pool, the creation and deletion of the mutex, and the application for release.
This article is shared from the Huawei Cloud Community " Light Kernel M Core Source Code Analysis Series Ten Mutex ", the original author: zhushy.
In a multitasking environment, there will be scenarios where multiple tasks access the same common resource, and some common resources are non-shared critical resources and can only be used exclusively. Hongmeng Light Kernel uses mutex locks to avoid such conflicts. Mutex locks are a special binary semaphore used to implement exclusive processing of critical resources. In addition, mutual exclusion locks can solve the priority inversion problem of semaphores. When a mutex is used to process synchronous access to a critical resource, if a task accesses the resource, the mutex is locked. At this time, if other tasks want to access this critical resource, they will be blocked. After the mutex is released by the task that holds the lock, other tasks can re-access the public resource. At this time, the mutex is locked again to ensure the same Only one task is accessing this critical resource at any time, ensuring the integrity of critical resource operations.
In this article, let’s learn the source code of the mutex module of the Hongmeng light kernel. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be found on the open source site https://gitee.com/openharmony/kernel_liteos_m .
Next, let's look at the structure of the mutex, the initialization of the mutex, and the source code of the common operations of the mutex.
1. Mutex structure definition and common macro definition
1.1 Mutex structure definition
The mutex control block structure LosMuxCB defined in the file kernel\include\los_mux.h, the source code is as follows, and the explanation of the structure members is in the comment section.
typedef struct {
UINT8 muxStat; /**< 互斥锁状态:OS_MUX_UNUSED, OS_MUX_USED */
UINT16 muxCount; /**< 锁被持有的次数 */
UINT32 muxID; /**< 互斥锁Id */
LOS_DL_LIST muxList; /**< 互斥锁双向链表 */
LosTaskCB *owner; /**< 当前持有锁的任务 */
UINT16 priority; /**< 当前持有锁的任务的优先级,为避免优先级翻转,可能会更改任务的优先级,此时有备份的作用 */
} LosMuxCB;
1.2 Commonly used macro definitions of mutex locks
The number of mutexes supported by the system is defined by the macro LOSCFG_BASE_IPC_MUX_LIMIT according to the development board. The muxId is of type UINT32, and the value of muxId is [0,LOSCFG_BASE_IPC_MUX_LIMIT), which represents the number of each mutex in the mutex pool. .
The macros at ⑴ and ⑵ indicate the unused and used state values of the mutex lock. (3) Obtain the mutex control block corresponding to the specified mutex muxid from the mutex lock pool. (4) Obtain the mutex control block structure pointer according to the linked list node pointer ptr in the doubly linked list of the mutex.
⑴ #define OS_MUX_UNUSED 0
⑵ #define OS_MUX_USED 1
⑶ #define GET_MUX(muxid) (((LosMuxCB *)g_allMux) + (muxid))
⑷ #define GET_MUX_LIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosMuxCB, muxList)
2. Mutual exclusion lock initialization
The mutex is turned on by default in the kernel, and the user can turn it off through the macro LOSCFG_BASE_IPC_MUX. When the mutex is turned on, when the system starts, call OsMuxInit() in kernel\src\los_init.c to initialize the mutex module.
Next, we analyze the code for initializing the mutex lock.
(1) Initialize the two-way circular linked list g_unusedMuxList and maintain unused mutex locks. ⑵ If the macro LOSCFG_BASE_IPC_MUX is not set, an error code will be returned. (3) Apply for memory for the mutex lock, if the application fails, the error LOS_ERRNO_MUX_NO_MEMORY will be returned
⑷Initialize each mutex loop, specify the index muxID for each mutex node, muxStat is the unused OS_MUX_UNUSED, and insert the mutex node into the unused mutex doubly linked list g_unusedMuxList.
⑷If the mutex lock debugging switch is turned on, call the function UINT32 OsMuxDbgInit(VOID) to initialize.
LITE_OS_SEC_TEXT_INIT UINT32 OsMuxInit(VOID)
{
LosMuxCB *muxNode = NULL;
UINT32 index;
⑴ LOS_ListInit(&g_unusedMuxList);
⑵ if (LOSCFG_BASE_IPC_MUX_LIMIT == 0) {
return LOS_ERRNO_MUX_MAXNUM_ZERO;
}
⑶ g_allMux = (LosMuxCB *)LOS_MemAlloc(m_aucSysMem0, (LOSCFG_BASE_IPC_MUX_LIMIT * sizeof(LosMuxCB)));
if (g_allMux == NULL) {
return LOS_ERRNO_MUX_NO_MEMORY;
}
⑷ for (index = 0; index < LOSCFG_BASE_IPC_MUX_LIMIT; index++) {
muxNode = ((LosMuxCB *)g_allMux) + index;
muxNode->muxID = index;
muxNode->muxStat = OS_MUX_UNUSED;
LOS_ListTailInsert(&g_unusedMuxList, &muxNode->muxList);
}
return LOS_OK;
}
3. Common operations of mutex locks
3.1 Mutex creation
We can use the function UINT32 LOS_MuxCreate(UINT32 *muxHandle) to create a mutex. Let's analyze the source code to see how to create a mutex.
⑴ Determine whether the unused mutex list g_unusedMuxList is empty. If there is no mutex that can be used, jump to the error code. (2) If g_unusedMuxList is not empty, get the first available mutex node, then delete it from the doubly linked list g_unusedMuxList, then call the GET_MUX_LIST macro function to get LosMuxCB muxCreated, and then initialize the created mutex information, including holding Information such as the number of locks, status, and priority. ⑶Initialize doubly linked list & muxCreated->muxList, tasks blocked on this mutex will hang on this linked list. ⑷ Assign a value to the output parameter muxHandle, and the subsequent program uses this mutex Id to perform other operations on the mutex.
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MuxCreate(UINT32 *muxHandle)
{
UINT32 intSave;
LosMuxCB *muxCreated = NULL;
LOS_DL_LIST *unusedMux = NULL;
UINT32 errNo;
UINT32 errLine;
if (muxHandle == NULL) {
return LOS_ERRNO_MUX_PTR_NULL;
}
intSave = LOS_IntLock();
⑴ if (LOS_ListEmpty(&g_unusedMuxList)) {
LOS_IntRestore(intSave);
OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_ALL_BUSY);
}
⑵ unusedMux = LOS_DL_LIST_FIRST(&(g_unusedMuxList));
LOS_ListDelete(unusedMux);
muxCreated = (GET_MUX_LIST(unusedMux));
muxCreated->muxCount = 0;
muxCreated->muxStat = OS_MUX_USED;
muxCreated->priority = 0;
muxCreated->owner = (LosTaskCB *)NULL;
⑶ LOS_ListInit(&muxCreated->muxList);
⑷ *muxHandle = (UINT32)muxCreated->muxID;
LOS_IntRestore(intSave);
OsHookCall(LOS_HOOK_TYPE_MUX_CREATE, muxCreated);
return LOS_OK;
ERR_HANDLER:
OS_RETURN_ERROR_P2(errLine, errNo);
}
3.2 Mutex deletion
We can use the function LOS_MuxDelete(UINT32 muxHandle) to delete the mutex. Let's look at how to delete the mutex by analyzing the source code.
(1) It is judged whether the muxHandle exceeds LOSCFG_BASE_IPC_MUX_LIMIT, if it exceeds, an error code will be returned. (2) Get the mutex control block LosMuxCB *muxDeleted. (3) If the mutex to be deleted is in an unused state, jump to the error label for processing. ⑷If the number of mutex lock holders is not empty, deletion is not allowed, and jump to the error label for processing. ⑸Recycle the deleted mutex to the unused mutex doubly linked list g_unusedMuxList, and then update it to the unused state.
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MuxDelete(UINT32 muxHandle)
{
UINT32 intSave;
LosMuxCB *muxDeleted = NULL;
UINT32 errNo;
UINT32 errLine;
⑴ if (muxHandle >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {
OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
}
⑵ muxDeleted = GET_MUX(muxHandle);
intSave = LOS_IntLock();
⑶ if (muxDeleted->muxStat == OS_MUX_UNUSED) {
LOS_IntRestore(intSave);
OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
}
⑷ if ((!LOS_ListEmpty(&muxDeleted->muxList)) || muxDeleted->muxCount) {
LOS_IntRestore(intSave);
OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_PENDED);
}
⑸ LOS_ListAdd(&g_unusedMuxList, &muxDeleted->muxList);
muxDeleted->muxStat = OS_MUX_UNUSED;
LOS_IntRestore(intSave);
OsHookCall(LOS_HOOK_TYPE_MUX_DELETE, muxDeleted);
return LOS_OK;
ERR_HANDLER:
OS_RETURN_ERROR_P2(errLine, errNo);
}
3.3 Mutual exclusion lock application
We can use the function UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout) to request a mutex. The two required parameters are the mutex Id and the waiting time timeout, the unit is Tick, and the value range is [0, LOS_WAIT_FOREVER].
Let's look at how to request a mutex by analyzing the source code.
When applying for a mutual exclusion lock, the legality of the mutual exclusion lock Id and parameters will be checked first, which is relatively simple. (1) The code obtains the currently running task. (2) If the mutex is not held, update the number of times the mutex is held, the holder's information and priority, and complete the application for the mutex. (3) If the number of times the mutex is held is not 0 and is held by the current task, the number of times that can be held is increased by 1, and nested holding is performed again to complete the application for the mutex. If the code executes to ⑷, it means that the applied mutex is held by other tasks. At this time, if the waiting time is 0, the application fails and returns. ⑸ Update the current task is blocked on the applied mutex lock.
⑹The code indicates that when the priority of the task currently applying for the mutex is higher than the priority of the task holding the mutex, the priority of the mutex is modified to the priority of the current task. Through this modification, priority inversion can be avoided. ⑺ Call the function OsSchedTaskWait() to update the status of the current task, set the waiting time, and then call the function LOS_Schedule to trigger task scheduling. Subsequent programs will not be executed temporarily, and you need to wait until the mutex can be acquired or the time expires.
If the time expires or a mutex lock is applied for, the system reschedules to execute this task, and the program continues to execute from point ⑻. If the time expires, (9) the task status is updated and the return code is returned, and the application for the mutex lock fails. If the mutex is successfully applied for, execute ⑽ and return success.
LITE_OS_SEC_TEXT UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)
{
UINT32 intSave;
LosMuxCB *muxPended = NULL;
UINT32 retErr;
LosTaskCB *runningTask = NULL;
if (muxHandle >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {
OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
}
muxPended = GET_MUX(muxHandle);
intSave = LOS_IntLock();
retErr = OsMuxValidCheck(muxPended);
if (retErr) {
goto ERROR_MUX_PEND;
}
⑴ runningTask = (LosTaskCB *)g_losTask.runTask;
⑵ if (muxPended->muxCount == 0) {
muxPended->muxCount++;
muxPended->owner = runningTask;
muxPended->priority = runningTask->priority;
LOS_IntRestore(intSave);
goto HOOK;
}
⑶ if (muxPended->owner == runningTask) {
muxPended->muxCount++;
LOS_IntRestore(intSave);
goto HOOK;
}
⑷ if (!timeout) {
retErr = LOS_ERRNO_MUX_UNAVAILABLE;
goto ERROR_MUX_PEND;
}
⑸ runningTask->taskMux = (VOID *)muxPended;
⑹ if (muxPended->owner->priority > runningTask->priority) {
(VOID)OsSchedModifyTaskSchedParam(muxPended->owner, runningTask->priority);
}
⑺ OsSchedTaskWait(&muxPended->muxList, timeout);
LOS_IntRestore(intSave);
OsHookCall(LOS_HOOK_TYPE_MUX_PEND, muxPended);
LOS_Schedule();
⑻ intSave = LOS_IntLock();
if (runningTask->taskStatus & OS_TASK_STATUS_TIMEOUT) {
⑼ runningTask->taskStatus &= (~OS_TASK_STATUS_TIMEOUT);
retErr = LOS_ERRNO_MUX_TIMEOUT;
goto ERROR_MUX_PEND;
}
LOS_IntRestore(intSave);
⑽ return LOS_OK;
HOOK:
OsHookCall(LOS_HOOK_TYPE_MUX_PEND, muxPended);
return LOS_OK;
ERROR_MUX_PEND:
LOS_IntRestore(intSave);
OS_RETURN_ERROR(retErr);
}
3.4 Mutual exclusion lock release
We can use the function UINT32 LOS_MuxPost (UINT32 muxHandle) to release the mutex. Let's look at how to release the mutex by analyzing the source code.
When the mutex is released, the legality of the mutex Id and parameters will be checked first. These are relatively simple, and you can read them by yourself. (1) If the mutex lock to be released is not held, or is not held by the current task, an error code is returned. (2) The number of mutex locks held is reduced by 1. If it is not 0, the current task nest holds the mutex lock and does not need to be scheduled, and the mutex lock is released successfully. If after releasing once, the current task no longer holds the mutex, then execute (3). If the priority of the task holding the mutex is not equal to the low backup priority of the mutex, the priority of the current task needs to be restored.
⑷If there are other tasks blocked on the mutex, get the blocked task resumedTask, the task successfully obtains the mutex, and then execute⑸Update the holding information of the mutex. Execute (6) to update the status of the task resumedTask, and then call the function LOS_Schedule to trigger the scheduling.
LITE_OS_SEC_TEXT UINT32 LOS_MuxPost(UINT32 muxHandle)
{
UINT32 intSave;
LosMuxCB *muxPosted = GET_MUX(muxHandle);
LosTaskCB *resumedTask = NULL;
LosTaskCB *runningTask = NULL;
intSave = LOS_IntLock();
if ((muxHandle >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) ||
(muxPosted->muxStat == OS_MUX_UNUSED)) {
LOS_IntRestore(intSave);
OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
}
runningTask = (LosTaskCB *)g_losTask.runTask;
⑴ if ((muxPosted->muxCount == 0) || (muxPosted->owner != runningTask)) {
LOS_IntRestore(intSave);
OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
}
⑵ if (--(muxPosted->muxCount) != 0) {
LOS_IntRestore(intSave);
OsHookCall(LOS_HOOK_TYPE_MUX_POST, muxPosted);
return LOS_OK;
}
⑶ if ((muxPosted->owner->priority) != muxPosted->priority) {
(VOID)OsSchedModifyTaskSchedParam(muxPosted->owner, muxPosted->priority);
}
⑷ if (!LOS_ListEmpty(&muxPosted->muxList)) {
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(muxPosted->muxList)));
⑸ muxPosted->muxCount = 1;
muxPosted->owner = resumedTask;
muxPosted->priority = resumedTask->priority;
resumedTask->taskMux = NULL;
⑹ OsSchedTaskWake(resumedTask);
LOS_IntRestore(intSave);
OsHookCall(LOS_HOOK_TYPE_MUX_POST, muxPosted);
LOS_Schedule();
} else {
LOS_IntRestore(intSave);
}
return LOS_OK;
}
summary
This article led everyone to analyze the source code of the mutex module of the Hongmeng Light Kernel, including the structure of the mutex, the initialization of the mutex pool, the creation and deletion of the mutex, and the application for release. Thanks for reading. If you have any questions or suggestions, you can leave a message to us: https://gitee.com/openharmony/kernel_liteos_m/issues . light kernel code warehouse easier, it is recommended to visit 160f0ef2faee0c https://gitee.com/openharmony/kernel_liteos_m , follow Watch, like Star, and Fork to your account, thank you.
Click to follow, and get to know the fresh technology of Huawei Cloud for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。