
# 互斥锁调测方法
#### 使用场景
多任务系统使用互斥锁达到资源互斥的目的，其他任务不能强行抢占任务已经占有的资源。使用互斥锁时，可能存在任务间相互等对方释放资源的情况，从而造成死锁。死锁会使任务陷入无限循环等待，导致业务功能障碍。
#### 功能说明
开启[dlock](https://support.huaweicloud.com/maintenance-LiteOS/zh-cn_topic_0312409053.html#ZH-CN_TOPIC_0312409053)互斥锁死锁检测功能后，每个任务在成功获取互斥锁时，会记录该互斥锁为本任务持有，因此通过任务ID可以得知持有的互斥锁。此外，互斥锁控制块本身会记录那些因申请不到该锁而被阻塞的任务。dlock命令会输出系统中所有任务持有互斥锁的信息及任务调用栈信息，再结合.asm反汇编文件和代码就可以确定哪些任务发生了死锁。
#### 互斥锁死锁检测机制
任务发生死锁后，无法得到调度，通过记录任务上次调度的时间，设置一个超时时间阈值，如果任务在这段时间内都没有得到调度，则怀疑该任务发生了死锁。
#### 使用方法
配置宏LOSCFG_DEBUG_DEADLOCK，该宏开关可以通过make menuconfig在菜单项中开启"Enable Mutex Deadlock Debugging"使能，若关闭该菜单项，则关闭死锁检测功能。
```
Debug ---> Enable a Debug Version ---> Enable Debug LiteOS Kernel Resource ---> Enable Mutex Deadlock Debugging
```
#### 注意事项
死锁检测输出的是超过时间阈值（默认为10min）的任务信息，但不代表这些任务都发生了死锁，需要通过互斥锁信息及任务调用栈信息进一步确认。
#### 死锁定位实例
构造ABBA互斥锁死锁场景，具体如下：
有两个任务，分别为app_Task和mutexDlock_Task，同时系统中还存在其他系统默认初始任务。在任务app_Task中执行MutexDlockDebug函数，并在该函数中创建任务mutexDlock_Task。函数MutexDlockDebug（即任务app_Task）创建了个互斥锁mutex0，并持有mutex0，接着创建更高优先级的任务mutexDlock_Task，休眠一段时间后去申请mutex1被阻塞（任务mutexDlock_Task已经率先持有mutex1）。任务mutexDlock_Task创建并持有mutex1，然后申请mutex0被阻塞（任务app_Task已经率先持有mutex0）。代码如下：
```
#include "unistd.h"
#include "los_mux.h"
#include "los_task.h"
static UINT32 mutexTest[2];
extern UINT32 OsShellCmdMuxDeadlockCheck(UINT32 argc, const CHAR **argv);
VOID DlockDebugTask(VOID)
{
    UINT32 ret;
    ret = LOS_MuxPend(mutexTest[1], LOS_WAIT_FOREVER);
    if (ret != LOS_OK) {
        PRINT_ERR("pend mutex1 error %u\n", ret);
    }
    ret = LOS_MuxPend(mutexTest[0], LOS_WAIT_FOREVER);
    if (ret != LOS_OK) {
        PRINT_ERR("pend mutex0 error %u\n", ret);
    }
    ret = LOS_MuxPost(mutexTest[1]);
    if (ret != LOS_OK) {
        PRINT_ERR("post mutex1 error %u\n", ret);
    }
    ret = LOS_MuxPost(mutexTest[0]);
    if (ret != LOS_OK) {
        PRINT_ERR("post mutex0 error %u\n", ret);
    }
}
// MutexDlockDebug函数在用户任务app_Task中被调度
STATIC UINT32 MutexDlockDebug(VOID)
{
    UINT32 ret;
    UINT32 taskId;
    TSK_INIT_PARAM_S debugTask;
    ret = LOS_MuxCreate(&mutexTest[0]);
    if (ret != LOS_OK) {
        PRINT_ERR("create mutex0 error %u\n", ret);
    }
    ret = LOS_MuxCreate(&mutexTest[1]);
    if (ret != LOS_OK) {
        PRINT_ERR("create mutex1 error %u\n", ret);
    }
    ret = LOS_MuxPend(mutexTest[0], LOS_WAIT_FOREVER);
    if (ret != LOS_OK) {
        PRINT_ERR("pend mutex0 error %u\n", ret);
    }
    (VOID)memset_s(&debugTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
    debugTask.pfnTaskEntry = (TSK_ENTRY_FUNC)DlockDebugTask;
    debugTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    debugTask.pcName = "mutexDlock_Task";
    debugTask.usTaskPrio = 9;
    debugTask.uwResved = LOS_TASK_STATUS_DETACHED;
    ret = LOS_TaskCreate(&taskId, &debugTask);   // 创建mutexDlock_Task任务，任务入口函数DlockDebugTask，优先级9高于app_Task任务
    if (ret != LOS_OK) {
        PRINT_ERR("create debugTask error %u\n", ret);
    }
    sleep(2);
    ret = LOS_MuxPend(mutexTest[1], LOS_WAIT_FOREVER);
    if (ret != LOS_OK) {
        PRINT_ERR("pend mutex1 error %u\n", ret);
    }
    ret = LOS_MuxPost(mutexTest[0]);
    if (ret != LOS_OK) {
        PRINT_ERR("post mutex0 error %u\n", ret);
    }
    ret = LOS_MuxPost(mutexTest[1]);
    if (ret != LOS_OK) {
        PRINT_ERR("post mutex1 error %u\n", ret);
    }
    return ret;
}
```
针对以上场景出现的问题，在怀疑发生死锁后，可以参考一下步骤定位问题：
1. 在Shell中运行dlock命令检测死锁。 
   输出结果如下图：
   ![](https://support.huaweicloud.com/maintenance-LiteOS/zh-cn_image_0304770797.png)
   - "Task_name:app_Task, ID:0x5, holds the Mutexs below："和"Task_name:mutexDlock_Task, ID:0xc, holds the Mutexs below:"这两行后面有mutex信息，表示可能是任务app_Task（任务ID为5）和mutexDlock_Task （任务ID为c）发生了死锁。
   
   - \<Mutex0 info\>：其后几行是该互斥锁的详细信息，包括"Ptr handle"为互斥锁句柄、"Owner"为锁的持有者、"Count"为该锁的引用计数、"Pended task"为阻塞在这把锁上的任务。如果该任务持有多把锁，会逐个打印这些锁的信息（Mutex0\~MutexN）。当前app_Task和mutexDlock_Task俩任务分别只持有一把锁。
   
   
   
   
2. 在Shell中运行task命令显示当前所有正在运行的任务状态和信息。 
   输出结果如下图：
   ![](https://support.huaweicloud.com/maintenance-LiteOS/zh-cn_image_0304776186.png)
   根据步骤1，任务app_Task和mutexDlock_Task是找到的疑似发生死锁的任务。上图中的TaskEntryAddr列为发生死锁时互斥锁pend的任务入口函数地址，如本例中为任务app_Task（0x8026f28c）和mutexDlock_Task（0x8026eef4）。
   
   
3. 在反汇编文件中找到相应函数。 
   打开 .asm反汇编文件（默认在Huawei_LiteOS/out/\<platform\>目录下，其中的platform为具体的平台名），在.asm文件中找到相应的地址，以mutexDlock_Task（0x8026eef4）为例如下图所示，即可定位到互斥锁pend的位置及调用的接口：
   ![](https://support.huaweicloud.com/maintenance-LiteOS/zh-cn_image_0304776768.png)
   
   
4. 查看单个任务的栈调用信息。 
   如果需要进一步确认任务的调用关系，可以在Shell中运行task命令加ID号查看该任务的栈调用信息，最后再根据上下文判断是否存在死锁。
   
   
 
