关于物联网:一文带你剖析LiteOS互斥锁Mutex源代码

8次阅读

共计 13172 个字符,预计需要花费 33 分钟才能阅读完成。

摘要:多任务环境下会存在多个工作拜访同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占应用。LiteOS 应用互斥锁来防止这种抵触,互斥锁是一种非凡的二值性信号量,用于实现对临界资源的独占式解决。

本文分享自华为云社区《LiteOS 内核源码剖析系列七 互斥锁 Mutex》,原文作者:zhushy。

多任务环境下会存在多个工作拜访同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占应用。LiteOS 应用互斥锁来防止这种抵触,互斥锁是一种非凡的二值性信号量,用于实现对临界资源的独占式解决。另外,互斥锁能够解决信号量存在的优先级翻转问题。用互斥锁解决临界资源的同步拜访时,如果有工作拜访该资源,则互斥锁为加锁状态。此时其余工作如果想拜访这个临界资源则会被阻塞,直到互斥锁被持有该锁的工作开释后,其余工作能力从新拜访该公共资源,此时互斥锁再次上锁,如此确保同一时刻只有一个工作正在拜访这个临界资源,保障了临界资源操作的完整性。

本文咱们来一起学习下 LiteOS 互斥锁模块的源代码,文中所波及的源代码,均能够在 LiteOS 开源站点 https://gitee.com/LiteOS/LiteOS 获取。互斥锁源代码、开发文档,示例程序代码如下:

  • LiteOS 内核互斥锁源代码

包含互斥锁的公有头文件 kernelbaseincludelos_mux_pri.h、头文件 kernelincludelos_mux.h、C 源代码文件 kernelbaselos_mux.c。

  • 开发指南文档–互斥锁

在线文档 https://gitee.com/LiteOS/Lite…

接下来,咱们看下互斥锁的构造体,互斥锁初始化,互斥锁罕用操作的源代码。

1、互斥锁构造体定义和罕用宏定义

1.1 互斥锁构造体定义

在文件 kernelbaseincludelos_mux_pri.h 定义的互斥锁管制块构造体有 2 个,MuxBaseCB 和 LosMuxCB,前者和后者的前三个成员一样,能够和 pthread_mutex_t 共享内核互斥锁机制。构造体源代码如下,构造体成员的解释见正文局部。

typedef struct {
    LOS_DL_LIST muxList; /**< 互斥锁双向链表 */
    LosTaskCB *owner; /**< 以后持有锁的工作 */
    UINT16 muxCount; /**< 锁被持有的次数 */
} MuxBaseCB;

typedef struct {
    LOS_DL_LIST muxList; /**< 互斥锁双向链表 */
    LosTaskCB *owner; /**< 以后持有锁的工作 */
    UINT16 muxCount; /**< 锁被持有的次数 */
    UINT8 muxStat; /**< 互斥锁状态:OS_MUX_UNUSED, OS_MUX_USED */
    UINT32 muxId; /**< 互斥锁 Id */
} LosMuxCB;

1.2 互斥锁罕用宏定义

零碎反对创立多少互斥锁是依据开发板状况应用宏 LOSCFG_BASE_IPC_MUX_LIMIT 定义的,互斥锁 Id 是 UINT32 类型的,由 2 局部组成:count 和 muxId,别离处于高 16 位和低 16 位。创立互斥锁,应用后删除时,互斥锁回收到互斥锁池时,互斥锁 Id 的高 16 位即 count 值会加 1,这样能够用来示意该互斥锁被创立删除的次数。muxId 取值为[0,LOSCFG_BASE_IPC_MUX_LIMIT),示意互斥锁池中各个的互斥锁的编号。

⑴处的宏用来宰割 count 和 muxId 的位数,⑵处互斥锁被删除时更新互斥锁 Id,能够看出高 16 位为 count 和低 16 位为 muxId。⑶处获取互斥锁 Id 的低 16 位。⑷依据互斥锁 Id 获取对应的互斥锁被创立删除的次数 count。⑸处从互斥锁池中获取指定互斥锁 Id 对应的互斥锁管制块。

⑴    #define MUX_SPLIT_BIT 16

⑵    #define SET_MUX_ID(count, muxId)    (((count) << MUX_SPLIT_BIT) | (muxId))

⑶    #define GET_MUX_INDEX(muxId)        ((muxId) & ((1U << MUX_SPLIT_BIT) - 1))

⑷    #define GET_MUX_COUNT(muxId)        ((muxId) >> MUX_SPLIT_BIT)

⑸    #define GET_MUX(muxId)              (((LosMuxCB *)g_allMux) + GET_MUX_INDEX(muxId))

2、互斥锁初始化

互斥锁在内核中默认开启,用户能够通过宏 LOSCFG_BASE_IPC_MUX 进行敞开。开启互斥锁的状况下,在系统启动时,在 kernelinitlos_init.c 中调用 OsMuxInit()进行互斥锁模块初始化。

上面,咱们剖析下互斥锁初始化的代码。

⑴初始化双向循环链表 g_unusedMuxList,保护未应用的互斥锁。⑵为互斥锁申请内存,如果申请失败,则返回谬误 LOS_ERRNO_MUX_NO_MEMORY
⑶循环每一个互斥锁进行初始化,为每一个互斥锁节点指定索引 muxId,owner 为空,muxStat 为未应用 OS_MUX_UNUSED,并把互斥锁节点插入未应用互斥锁双向链表 g_unusedMuxList。
⑷如果开启了互斥锁调测开关,则调用函数 UINT32 OsMuxDbgInit(VOID)进行初始化。

LITE_OS_SEC_TEXT UINT32 OsMuxInit(VOID)
{
    LosMuxCB *muxNode = NULL;
    UINT32 index;

⑴  LOS_ListInit(&g_unusedMuxList);
⑵  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 = g_allMux + index;
        muxNode->muxId = index;
        muxNode->owner = NULL;
        muxNode->muxStat = OS_MUX_UNUSED;
        LOS_ListTailInsert(&g_unusedMuxList, &muxNode->muxList);
    }

⑷  if (OsMuxDbgInitHook() != LOS_OK) {return LOS_ERRNO_MUX_NO_MEMORY;}
    return LOS_OK;
}

3、互斥锁罕用操作

3.1 互斥锁创立

咱们能够应用函数 UINT32 LOS_MuxCreate(UINT32 *muxHandle)来创立互斥锁,上面通过剖析源码看看如何创立互斥锁的。

⑴判断 g_unusedMuxList 是否为空,还有能够应用的互斥锁资源?如果没有能够应用的互斥锁,调用函数 OsMutexCheckHook()判断是否有互斥锁溢出等谬误,这个函数须要开启调测开关。⑵处如果 g_unusedMuxList 不为空,则获取第一个可用的互斥锁节点,接着从双向链表 g_unusedMuxList 中删除,而后调用 LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList)获取 LosMuxCB muxCreated,初始化创立的互斥锁信息,蕴含持有锁的次数、状态、持有者等信息。⑶初始化双向链表 &muxCreated->muxList,阻塞在这个互斥上的工作会挂在这个链表上。⑷赋值给输入参数 muxHandle,后续程序应用这个互斥锁 Id 对互斥锁进行其余操作。⑸开启调测时,会调用函数 OsMuxDbgUpdateHook() 更新互斥锁的应用状况。

LITE_OS_SEC_TEXT 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;}

    SCHEDULER_LOCK(intSave);
⑴  if (LOS_ListEmpty(&g_unusedMuxList)) {SCHEDULER_UNLOCK(intSave);
        OsMutexCheckHook();
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_ALL_BUSY);
    }

⑵  unusedMux = LOS_DL_LIST_FIRST(&g_unusedMuxList);
    LOS_ListDelete(unusedMux);
    muxCreated = LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList);
    muxCreated->muxCount = 0;
    muxCreated->muxStat = OS_MUX_USED;
    muxCreated->owner = NULL;
⑶  LOS_ListInit(&muxCreated->muxList);
    *muxHandle = muxCreated->muxId;

⑸  OsMuxDbgUpdateHook(muxCreated->muxId, OsCurrTaskGet()->taskEntry);

    SCHEDULER_UNLOCK(intSave);

    LOS_TRACE(MUX_CREATE, muxCreated->muxId);
    return LOS_OK;

ERR_HANDLER:
    OS_RETURN_ERROR_P2(errLine, errNo);
}

3.2 互斥锁删除

咱们能够应用函数 LOS_MuxDelete(UINT32 muxHandle)来删除互斥锁,上面通过剖析源码看看如何删除互斥锁的。

⑴处判断互斥锁 handleId 是否超过 LOSCFG_BASE_IPC_MUX_LIMIT,如果超过则返回错误码。⑵获取互斥锁管制块 LosMuxCB *muxDeleted。⑶如果要删除的互斥锁 Id 有问题,或者要删除的互斥锁处于未应用状态,跳转到谬误标签进行解决。⑷如果互斥锁的持有者数量不为空,不容许删除,跳转到谬误标签进行解决。⑸把删除的互斥锁回收到未应用互斥锁双向链表 g_unusedMuxList,而后更新为未应用状态,更新互斥锁 Id。⑹开启调测时,会调用函数 OsMuxDbgUpdateHook()更新互斥锁的应用状况。

LITE_OS_SEC_TEXT UINT32 LOS_MuxDelete(UINT32 muxHandle)
{
    UINT32 intSave;
    LosMuxCB *muxDeleted = NULL;
    UINT32 errNo;
    UINT32 errLine;

⑴  if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
    }

⑵  muxDeleted = GET_MUX(muxHandle);

    LOS_TRACE(MUX_DELETE, muxHandle, muxDeleted->muxStat, muxDeleted->muxCount,
        ((muxDeleted->owner == NULL) ? 0xFFFFFFFF : muxDeleted->owner->taskId));

    SCHEDULER_LOCK(intSave);
⑶  if ((muxDeleted->muxId != muxHandle) || (muxDeleted->muxStat == OS_MUX_UNUSED)) {SCHEDULER_UNLOCK(intSave);
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
    }

⑷  if (!LOS_ListEmpty(&muxDeleted->muxList) || muxDeleted->muxCount) {SCHEDULER_UNLOCK(intSave);
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_PENDED);
    }

⑸  LOS_ListTailInsert(&g_unusedMuxList, &muxDeleted->muxList);
    muxDeleted->muxStat = OS_MUX_UNUSED;
    muxDeleted->muxId = SET_MUX_ID(GET_MUX_COUNT(muxDeleted->muxId) + 1, GET_MUX_INDEX(muxDeleted->muxId));

⑹  OsMuxDbgUpdateHook(muxDeleted->muxId, NULL);

    SCHEDULER_UNLOCK(intSave);

    return LOS_OK;

ERR_HANDLER:
    OS_RETURN_ERROR_P2(errLine, errNo);
}

3.3 互斥锁申请

咱们能够应用函数 UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)来申请互斥锁,须要的 2 个参数别离是互斥锁 Id 和等待时间 timeout,单位 Tick,取值范畴为[0, LOS_WAIT_FOREVER]。

上面通过剖析源码看看如何申请互斥锁的。

申请互斥锁时首先会进行互斥锁 Id、参数的合法性校验,这些比较简单。⑴处代码判断申请互斥锁的是否零碎工作,如果是零碎工作输入正告信息。⑵如果互斥锁没有被持有,更新互斥锁的持有次数和持有者信息,实现互斥锁的申请。⑶处如果互斥锁的持有次数不为 0,并且被当前任务持有,能够持有次数加 1,再次嵌套持有,实现互斥锁的申请。⑷如果等待时间为 0,申请失败返回。⑸如果以后锁任务调度,不容许申请互斥锁,打印回溯栈并返回错误码。

能运行到⑹处,示意互斥锁已被其余工作持有。在以后申请互斥锁的工作优先级高于持有互斥锁的工作优先级时,批改持有互斥锁的优先级为当前任务的优先级,持有锁的工作优先级备份到成员变量 muxPended->owner->priBitMap。通过这样的批改,能够防止优先级翻转。⑺处的函数 OsMuxPendOp()下文持续剖析。

LITE_OS_SEC_TEXT UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)
{
    UINT32 ret;
    UINT32 intSave;
    LosMuxCB *muxPended = NULL;
    LosTaskCB *runTask = NULL;

    if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    muxPended = GET_MUX(muxHandle);

    LOS_TRACE(MUX_PEND, muxHandle, muxPended->muxCount,
        ((muxPended->owner == NULL) ? 0xFFFFFFFF : muxPended->owner->taskId), timeout);

    SCHEDULER_LOCK(intSave);

    ret = OsMuxParaCheck(muxPended, muxHandle);
    if (ret != LOS_OK) {goto OUT_UNLOCK;}

    runTask = OsCurrTaskGet();
⑴  if (runTask->taskFlags & OS_TASK_FLAG_SYSTEM) {PRINT_DEBUG("Warning: DO NOT recommend to use %s in system tasks.n", __FUNCTION__);
    }

⑵  if (muxPended->muxCount == 0) {OsMuxDlockNodeInsertHook(runTask->taskId, muxPended);
        muxPended->muxCount++;
        muxPended->owner = runTask;
        goto OUT_UNLOCK;
    }

⑶  if (muxPended->owner == runTask) {
        muxPended->muxCount++;
        goto OUT_UNLOCK;
    }

⑷  if (!timeout) {
        ret = LOS_ERRNO_MUX_UNAVAILABLE;
        goto OUT_UNLOCK;
    }

⑸  if (!OsPreemptableInSched()) {
        ret = LOS_ERRNO_MUX_PEND_IN_LOCK;
        PRINT_ERR("!!!LOS_ERRNO_MUX_PEND_IN_LOCK!!!n");
        OsBackTrace();
        goto OUT_UNLOCK;
    }

⑹  OsMuxBitmapSet(runTask, (MuxBaseCB *)muxPended);
⑺  ret = OsMuxPendOp(runTask, (MuxBaseCB *)muxPended, timeout, &intSave);

OUT_UNLOCK:
    SCHEDULER_UNLOCK(intSave);
    return ret;
}

接下来持续剖析函数 OsMuxPendOp(),⑴处设置申请互斥锁的工作的构造体成员变量 runTask->taskMux 为申请的互斥锁。⑵处获取互斥锁的双向链表,阻塞在申请这个互斥锁的工作都挂在这个链表上,后文详细分析这个函数。⑶处把申请互斥锁的工作改为非就绪状态、阻塞状态,插入到互斥锁的阻塞工作列表里。如果是非永恒期待互斥锁,还须要把工作退出超时排序链表里。⑷触发任务调度,后续程序临时不再执行,须要等到能够获取互斥锁或者工夫超时。

如果工夫超时或者申请到互斥锁,零碎从新调度到执行此工作,程序从⑸处继续执行。如果是工夫超时,⑹处更新工作状态并返回码,申请互斥锁失败。⑺如果胜利申请到互斥锁,并且超时工夫不等于 LOS_WAIT_FOREVER,须要判断是否复原工作优先级。

LITE_OS_SEC_TEXT UINT32 OsMuxPendOp(LosTaskCB *runTask, MuxBaseCB *muxPended, UINT32 timeout,
                                    UINT32 *intSave)
{
    LOS_DL_LIST *node = NULL;
    UINT32 ret = LOS_OK;
    LosTaskCB *owner = muxPended->owner;

⑴  runTask->taskMux = (VOID *)muxPended;
⑵  node = OsMuxPendFindPos(runTask, muxPended);
⑶  OsTaskWait(node, OS_TASK_STATUS_PEND, timeout);
⑷  OsSchedResched();
    SCHEDULER_UNLOCK(*intSave);
⑸  SCHEDULER_LOCK(*intSave);

⑹  if (runTask->taskStatus & OS_TASK_STATUS_TIMEOUT) {
        runTask->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;
        ret = LOS_ERRNO_MUX_TIMEOUT;
    }

⑺  if (timeout != LOS_WAIT_FOREVER) {OsMuxBitmapRestore(runTask, owner);
    }

    return ret;
}

接下来,剖析下外部函数 OsMuxPendFindPos()。LiteOS 互斥锁反对 2 种期待模式,能够通过宏来配置:

  • LOSCFG_MUTEX_WAITMODE_PRIO

互斥锁基于工作优先级的期待模式,阻塞在互斥锁的工作里,谁的优先级高,在互斥锁开释时,谁先获取到互斥锁。

  • LOSCFG_MUTEX_WAITMODE_FIFO

互斥锁基于 FIFO 的期待模式,阻塞在互斥锁的工作里,谁先进入阻塞队列,在互斥锁开释时,谁先获取到互斥锁。

在开启宏 LOSCFG_MUTEX_WAITMODE_FIFO,互斥锁基于 FIFO 的期待模式时,函数 OsMuxPendFindPos()的源码比较简单,间接获取互斥锁的阻塞链表,在后续的 OsTaskWait()函数里,会把工作挂在在获取的阻塞链表的尾部。代码如下:

LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPos(const LosTaskCB *runTask, MuxBaseCB *muxPended)
{
    LOS_DL_LIST *node = NULL;
    node = &muxPended->muxList;
    return node;
}

咱们再来看看开启宏 LOSCFG_MUTEX_WAITMODE_PRIO,互斥锁基于工作优先级的期待模式时的函数的代码。⑴如果互斥锁的阻塞链表为空,间接返回链表即可。⑵阻塞链表不为空时,从链表中获取第一个和最初一个链表节点,别离为 pendedTask1 和 pendedTask2。⑶如果阻塞链表第一个工作的优先级低于当前任务的优先级,链表中所有的工作的优先级都会低,返回互斥锁的阻塞链表的第一个节点接口。⑷如果阻塞链表的最初一个工作的优先级大于当前任务的优先级,返回互斥锁阻塞链表的头结点即可。⑸对于其余状况,须要调用函数 OsMuxPendFindPosSub()进行解决。

LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPos(const LosTaskCB *runTask, MuxBaseCB *muxPended)
{
    LOS_DL_LIST *node = NULL;
    LosTaskCB *pendedTask1 = NULL;
    LosTaskCB *pendedTask2 = NULL;

⑴  if (LOS_ListEmpty(&muxPended->muxList)) {node = &muxPended->muxList;} else {⑵      pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&muxPended->muxList));
        pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(&muxPended->muxList));
⑶      if ((pendedTask1 != NULL) && (pendedTask1->priority > runTask->priority)) {
            node = muxPended->muxList.pstNext;
⑷      } else if ((pendedTask2 != NULL) && (pendedTask2->priority <= runTask->priority)) {node = &muxPended->muxList;} else {⑸          node = OsMuxPendFindPosSub(runTask, muxPended);
        }
    }
    return node;
}

持续剖析下函数 OsMuxPendFindPosSub()。⑴循环遍历互斥锁阻塞链表,⑵如果链表上工作优先级大于当前任务的优先级,则持续遍历。⑶如果链表上工作优先级小于当前任务的优先级,不须要持续遍历了,返回链表的以后节点。⑷如果优先级相等,返回链表的下一个节点。

LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPosSub(const LosTaskCB *runTask, const MuxBaseCB *muxPended)
{
    LosTaskCB *pendedTask = NULL;
    LOS_DL_LIST *node = NULL;

⑴  LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, &(muxPended->muxList), LosTaskCB, pendList) {⑵      if (pendedTask->priority < runTask->priority) {
            continue;
⑶      } else if (pendedTask->priority > runTask->priority) {
            node = &pendedTask->pendList;
            break;
        } else {
⑷          node = pendedTask->pendList.pstNext;
            break;
        }
    }

    return node;
}

3.4 互斥锁开释

咱们能够应用函数 UINT32 LOS_MuxPost(UINT32 muxHandle)来开释互斥锁,上面通过剖析源码看看如何开释互斥锁的。

开释互斥锁时首先会进行互斥锁 Id、参数的合法性校验,这些比较简单,自行浏览即可。⑴处如果要开释的互斥锁没有被持有、或者不是被当前任务持有,返回错误码。⑵互斥锁的持有数量减 1,如果不为 0,当前任务嵌套持有该互斥锁,不须要调度,返回开释互斥锁胜利。如果开释一次后,当前任务不再持有互斥锁,则调用⑶处函数 OsMuxPostOp(),判断是否有工作阻塞在该互斥锁,是否须要触发任务调度等,下文剖析该函数。执行结束⑶后,执行⑷,如果须要调度则触发任务调度。

LITE_OS_SEC_TEXT UINT32 LOS_MuxPost(UINT32 muxHandle)
{
    UINT32 ret;
    LosTaskCB *runTask = NULL;
    LosMuxCB *muxPosted = GET_MUX(muxHandle);
    UINT32 intSave;

    if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    LOS_TRACE(MUX_POST, muxHandle, muxPosted->muxCount,
        ((muxPosted->owner == NULL) ? 0xFFFFFFFF : muxPosted->owner->taskId));

    SCHEDULER_LOCK(intSave);

    ret = OsMuxParaCheck(muxPosted, muxHandle);
    if (ret != LOS_OK) {SCHEDULER_UNLOCK(intSave);
        return ret;
    }

    runTask = OsCurrTaskGet();
⑴  if ((muxPosted->muxCount == 0) || (muxPosted->owner != runTask)) {SCHEDULER_UNLOCK(intSave);
        OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

⑵  if (--muxPosted->muxCount != 0) {SCHEDULER_UNLOCK(intSave);
        return LOS_OK;
    }

⑶  ret = OsMuxPostOp(runTask, (MuxBaseCB *)muxPosted);
    SCHEDULER_UNLOCK(intSave);
⑷  if (ret == MUX_SCHEDULE) {LOS_MpSchedule(OS_MP_CPU_ALL);
        LOS_Schedule();}

    return LOS_OK;
}

咱们持续剖析函数 OsMuxPostOp()。⑴处如果等到该互斥锁的工作列表为空,则标记没有工作持有该互斥锁,并返回不须要调度。⑵获取期待互斥锁的第一个工作 resumedTask。如果开启宏 LOSCFG_MUTEX_WAITMODE_PRIO,如果期待互斥锁的工作 resumedTask 的优先级比以后优先级低,须要复原当前任务的优先级。如果当前任务优先级 runTask->priBitMap 不为 0,会调用⑷处的 OsMuxPostOpSub 函数,稍后剖析该函数。

⑸处把该互斥锁的持有数量设置为 1,持有人设置为期待互斥锁的第一个工作 resumedTask,resumedTask 持有了互斥锁不再阻塞在该互斥锁 resumedTask->taskMux = NULL。而后 2 个语句,属于调测个性的。⑹处调用 OsTaskWake()函数,把 resumedTask 从互斥锁的阻塞链表中删除,从定时器排序链表中删除。更新工作状态,退出就绪队列,返回工作须要调度。

LITE_OS_SEC_TEXT UINT32 OsMuxPostOp(LosTaskCB *runTask, MuxBaseCB *muxPosted)
{
    LosTaskCB *resumedTask = NULL;

⑴  if (LOS_ListEmpty(&muxPosted->muxList)) {
        muxPosted->owner = NULL;
        OsMuxDlockNodeDeleteHook(runTask->taskId, muxPosted);
        return MUX_NO_SCHEDULE;
    }

⑵  resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(muxPosted->muxList)));
#ifdef LOSCFG_MUTEX_WAITMODE_PRIO
⑶  if (resumedTask->priority > runTask->priority) {if (LOS_HighBitGet(runTask->priBitMap) != resumedTask->priority) {LOS_BitmapClr(&runTask->priBitMap, resumedTask->priority);
        }
    } else if (runTask->priBitMap != 0) {⑷      OsMuxPostOpSub(runTask, muxPosted);
    }
#else
    if (runTask->priBitMap != 0) {⑷      OsMuxPostOpSub(runTask, muxPosted);
    }
#endif

⑸  muxPosted->muxCount = 1;
    muxPosted->owner = resumedTask;
    resumedTask->taskMux = NULL;
    OsMuxDlockNodeDeleteHook(runTask->taskId, muxPosted);
    OsMuxDlockNodeInsertHook(resumedTask->taskId, muxPosted);

⑹  OsTaskWake(resumedTask, OS_TASK_STATUS_PEND);

    return MUX_SCHEDULE;
}

最初,咱们剖析函数 OsMuxPostOpSub()。

⑴如果互斥锁上还有其余工作阻塞着,获取以后运行工作记录的优先级.priBitMap。⑵处循环遍历挂在互斥锁阻塞链表上的每一个工作,如果阻塞工作的优先级不等于 bitMapPri,则执行⑶清理优先级位。⑷处复原以后持有互斥锁的工作的优先级。

LITE_OS_SEC_TEXT STATIC VOID OsMuxPostOpSub(LosTaskCB *runTask, MuxBaseCB *muxPosted)
{
    LosTaskCB *pendedTask = NULL;
    UINT16 bitMapPri;

⑴  if (!LOS_ListEmpty(&muxPosted->muxList)) {bitMapPri = LOS_HighBitGet(runTask->priBitMap);
⑵      LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, (&muxPosted->muxList), LosTaskCB, pendList) {if (bitMapPri != pendedTask->priority) {⑶              LOS_BitmapClr(&runTask->priBitMap, pendedTask->priority);
            }
        }
    }
⑷  bitMapPri = LOS_LowBitGet(runTask->priBitMap);
    LOS_BitmapClr(&runTask->priBitMap, bitMapPri);
    OsTaskPriModify(muxPosted->owner, bitMapPri);
}

小结

本文率领大家一起分析了 LiteOS 互斥锁模块的源代码,蕴含互斥锁的构造体、互斥锁池初始化、互斥锁创立删除、申请开释等。

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0