更多内容请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com多任务环境将存在多个任务访问同一个公共的场景资源,有些公共资源是非共享的关键资源,只能独占使用。鸿蒙轻内核使用互斥量来避免此类冲突。互斥量是一种特殊的二进制信号量,用于实现对关键资源的独占处理。另外,互斥量可以解决信号量的优先级倒置问题。当互斥量用于处理对关键资源的同步访问时,如果任务访问该资源,则互斥量被锁定。这时候,如果其他任务想要访问这个关键资源,就会被阻塞。在持有锁的任务释放互斥锁之前,其他任务可以重新访问公共资源。这时候互斥锁又被加锁了,这样就保证了同一个任务在任何时候都在访问这个关键资源,保证了对关键资源操作的完整性。接下来我们看一下互斥锁的结构,互斥锁的初始化,以及互斥锁常用操作的源码。1.互斥结构体定义及常用宏定义1.1互斥体结构体定义在文件kernel\include\los_mux.h中定义的互斥量控制块结构体LosMuxCB,源码如下,结构体成员在注释部分解释.typedefstruct{UINT8muxStat;/**muxID=index;muxInNode->muxStat=OS_MUXOSert_List_UNTUSED;L(&g_unusedMuxList,&muxNode->muxList);}returnLOS_OK;}3.互斥锁常用操作3.1互斥锁创建我们可以使用函数UINT32LOS_MuxCreate(UINT32*muxHandle)创建一个互斥锁。让我们分析一下源代码,看看如何创建它的互斥量。(1)判断未使用的互斥量列表g_unusedMuxList是否为空,如果没有可用的互斥量,则跳转到错误码处。(2)如果g_unusedMuxList不为空,获取第一个可用的互斥锁节点,然后从双向链表g_unusedMuxList中删除,然后调用GET_MUX_LIST宏函数获取LosMuxCB*muxCreated,然后初始化创建的互斥锁信息,包括holdingLock时间、状态、优先级和其他信息。⑶初始化双向链表&muxCreated->muxList,阻塞在这个mutex上的任务会挂在这个链表上。(4)为输出参数*muxHandle赋值,后续程序使用这个互斥量Id对互斥量进行其他操作。LITE_OS_SEC_TEXT_INITUINT32LOS_MuxCreate(UINT32*muxHandle){UINT32intSave;LosMuxCB*muxCreated=NULL;LOS_DL_LIST*unusedMux=NULL;UINT32errNo;UINT32errLine;if(muxHandle==NULL){returnLOS_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->muxmUXCount=0;muxCreatedat=0;muxCreatedat->priority=0;muxCreated->owner=(LosTaskCB*)NULL;⑶LOS_ListInit(&muxCreated->muxList);⑷*muxHandle=(UINT32)muxCreated->muxID;LOS_IntRestore(intSave);OsHookCall(LOS_HOOK_TYPE_MUX_CREATE,muxCreated);returnLOS_OK;ERR_HANDLER:OS_RETURN_ERROR_P2(errLine,errNo);}3.2删除互斥量我们可以使用函数LOS_MuxDelete(UINT32muxHandle)来删除互斥量,下面我们分析源码看看如何删除互斥量。(1)判断互斥量muxHandle是否超过LOSCFG_BASE_IPC_MUX_LIMIT,超过则返回错误码。(2)获取互斥量控制块LosMuxCB*muxDeleted。⑶如果要删除的互斥量没有被使用,则跳转到错误标签处进行处理。⑷如果mutexholder个数不为空,则不允许删除,跳转到错误标签处处理。(5)将删除的mutex回收到unusedmutex双向链表g_unusedMuxList,然后更新为unused状态。LITE_OS_SEC_TEXT_INITUINT32LOS_MuxDelete(UINT32muxHandle){UINT32intSave;LosMuxCB*muxDeleted=NULL;UINT32errNo;UINT32errLine;⑴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_IntERRestore(intSave);OS_GOTO_ERR_NDR_H);OS_GOTO_ERR_NDR_H)&muxDeleted->muxList);muxdeleted->muxStat=os_mux_unused;los_intrestore(intsave);oshookcall(los_hook_type_mux_delete,muxdelet,muxdeleted)muxHandle,UINT32timeout)请求互斥量,需要两个参数,互斥量Id和等待时间timeout,单位为Tick,取值范围为[0,LOS_WAIT_FOREVER]。下面我们分析一下源码,看看如何请求互斥量。申请互斥量时,会先验证互斥量Id和参数的合法性,比较简单。⑴该处代码获取当前正在运行的任务,⑵如果互斥锁没有被持有,则更新互斥锁的持有次数、持有者信息和优先级,完成对互斥锁的申请。(3)如果mutex的持有数不为0,被当前task持有,可以将持有数加1,再次嵌套,完成对mutex的申请。如果代码被执行,(4)意味着应用的互斥量被其他任务持有。此时如果等待时间为0,则申请失败,返回。(5)更新当前任务在应用的互斥锁上被阻塞。⑴处的代码表示当当前申请互斥量的任务优先级高于持有互斥量的任务优先级时,将持有互斥量的任务优先级修改为当前任务的优先级。通过这样的修改,可以避免优先级倒置。(7)调用函数OsSchedTaskWait()更新当前任务的状态,设置等待时间,然后调用函数LOS_Schedule触发任务调度。后面的程序暂时不会执行,需要等到可以拿到互斥量或者时间到了。如果时间到了或者申请了mutex,系统会重新调度执行这个任务,程序会从第(8)点继续执行。如果超时,则在(9)处更新任务状态并返回code,申请mutex失败。如果互斥锁申请成功,则执行⑽,返回成功。LITE_OS_SEC_TEXTUINT32LOS_MuxPend(UINT32muxHandle,UINT32timeout){UINT32intSave;LosMuxCB*muxPended=NULL;UINT32retErr;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){gotoERROR_MUX_PEND;}⑴runningTask=(LosTaskCB*)g_losTask.runTask;⑵if(muxPended->muxCount==0){muxPended->muxCount++;muxPended->owner=runningTask;muxPended->priority=runningTask->priority;LOS_IntRestore(intSave);gotoHOOK;}⑶if(muxPended->owner==runningTask){muxPended->muxCount++;LOS_IntRestore(intSave);gotoHOOK;}⑷if(!timeout){retErr=LOS_ERRNO_MUX_UNAVAILABLE;gotoERROR_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;gotoERROR_MUX_PEND;}los_intrestore(intsave);⑽returnlos_ok;hook:oshookcall(los_hook_type_mux_mux_pend,muxpend);returnlos_ok;error_mux_pend:los_intrestore(los_intrestore(intsave)排斥锁,我们分析一下源码,看看如何解除互斥锁。释放互斥锁时,会先检查互斥锁Id和参数的合法性。这些都比较简单,自己看就好了。(1)如果要释放的互斥锁没有被持有,或者没有被当前任务持有,则返回错误码。(2)持有的互斥量减1,如果不为0,则当前任务嵌套持有互斥量,不需要调度,成功释放互斥量。如果当前任务在释放一次后不再持有互斥锁,则执行。(3)如果持有互斥量的任务优先级不等于备份互斥量的低优先级,则需要恢复当前任务的优先级。⑷如果有其他任务阻塞在mutex上,获取阻塞任务resumedTask,任务成功获取到mutex,然后执行。(5)更新互斥量的持有信息。执行(6)更新任务resumedTask的状态,然后调用函数LOS_Schedule触发调度。lite_os_sec_textuint32los_muxpost(uint32muxhandle){uint32Intsave;losmuxcb*muxposted=get_mux(muxhandle);lossaskCb*losseScccb*resumedtask=null;>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);returnLOS_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_trestore(OK);OS;intSave}总结本文带领大家分析鸿蒙LightKernel的互斥量模块源码,包括互斥量的结构、互斥量池的初始化、互斥量的创建和删除、申请释放等。查看更多,请访问:https://harmonyos.51cto.com,与华为共同打造的鸿蒙技术社区