多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用。鸿蒙轻内核使用互斥锁来避免这种冲突,互斥锁是一种特殊的二值性信号量,用于实现对临界资源的独占式处理。另外,互斥锁可以解决信号量存在的优先级翻转问题。用互斥锁处理临界资源的同步访问时,如果有任务访问该资源,则互斥锁为加锁状态。此时其他任务如果想访问这个临界资源则会被阻塞,直到互斥锁被持有该锁的任务释放后,其他任务才能重新访问该公共资源,此时互斥锁再次上锁,如此确保同一时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的完整性。
本文我们来一起学习下鸿蒙轻内核互斥锁模块的源代码,本文中所涉及的源码,以OpenHarmony LiteOS-M
内核为例,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_m 获取。
接下来,我们看下互斥锁的结构体,互斥锁初始化,互斥锁常用操作的源代码。
1、互斥锁结构体定义和常用宏定义
1.1 互斥锁结构体定义
在文件kernel\include\los_mux.h
定义的互斥锁控制块结构体LosMuxCB
,源代码如下,结构体成员的解释见注释部分。
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 互斥锁常用宏定义
系统支持创建多少互斥锁是根据开发板情况使用宏LOSCFG_BASE_IPC_MUX_LIMIT
定义的,互斥锁muxId
是UINT32
类型的,muxId
取值为[0,LOSCFG_BASE_IPC_MUX_LIMIT)
,表示互斥锁池中各个的互斥锁的编号。
⑴处、⑵处的宏表示互斥锁的未使用、使用状态值。⑶处从互斥锁池中获取指定互斥锁muxid
对应的互斥锁控制块。⑷处根据互斥锁双向链表中的链表节点指针ptr
获取互斥锁控制块结构体指针。
⑴ #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、互斥锁初始化
互斥锁在内核中默认开启,用户可以通过宏LOSCFG_BASE_IPC_MUX
进行关闭。开启互斥锁的情况下,在系统启动时,在kernel\src\los_init.c
中调用OsMuxInit()
进行互斥锁模块初始化。
下面,我们分析下互斥锁初始化的代码。
⑴初始化双向循环链表g_unusedMuxList,维护未使用的互斥锁。⑵处如果没有设置宏LOSCFG_BASE_IPC_MUX
,则返回错误码。⑶为互斥锁申请内存,如果申请失败,则返回错误LOS_ERRNO_MUX_NO_MEMORY
⑷循环每一个互斥锁进行初始化,为每一个互斥锁节点指定索引muxID
,muxStat
为未使用OS_MUX_UNUSED
,并把互斥锁节点插入未使用互斥锁双向链表g_unusedMuxList
。
⑷如果开启了互斥锁调测开关,则调用函数UINT32 OsMuxDbgInit(VOID)
进行初始化。
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 (