互斥信号量的原理和创建

对于解决多任务共享资源冲突的问题,大致有三种解决办法,一种是关中断,这种一般冲突的是中断;第二种是调度锁,缺点和第一种一样,如果资源占用时间太长的话,会影响实时性;第三种是计数信号量,如上图,如果在嵌套的时候,同时使用了信号量并且与该信号量一样,那会被卡死,同样如果任何任务都能notify,那么安全性就会大大降低,对线程管理还说,也会有优先级反转的问题

下图是优先级反转的框图,大致是低优先级占有资源,高优先级意图访问资源,但是中间有中优先级任务,这就导致了优先级最高的任务,反而最后运行

typedef struct  _tMutex
{
    // 事件控制块
    tEvent event;
    // 已被锁定的次数
    uint32_t lockedCount;
    // 拥有者
    tTask * owner;
    // 拥有者原始的优先级
    uint32_t ownerOriginalPrio;
}tMutex;

对于互斥量的构造来说,比较复杂,相比信号量来说,多了owner和ownerOriginalPrio,分别用于确定拥有者和优先级继承的

void tMutexInit (tMutex * mutex)
{
    tEventInit(&mutex->event, tEventTypeMutex);
    mutex->lockedCount = 0;
    mutex->owner = (tTask *)0;
    mutex->ownerOriginalPrio = TINYOS_PRO_COUNT;
}

初始化比较简单,对每个构造进行初始化信息即可

互斥信号量的等待和通知

uint32_t tMutexWait (tMutex * mutex, uint32_t waitTicks)
{
    uint32_t status = tTaskEnterCritical();
    if (mutex->lockedCount <= 0)
    {
        // 如果没有锁定,则使用当前任务锁定
        mutex->owner = currentTask;
        mutex->ownerOriginalPrio = currentTask->prio;
        mutex->lockedCount++;
        tTaskExitCritical(status);
        return tErrorNoError;
    }
    else
    {
        // 信号量已经被锁定
        if (mutex->owner == currentTask)
        {
            // 如果是信号量的拥有者再次wait,简单增加计数
            mutex->lockedCount++;
            tTaskExitCritical(status);
            return tErrorNoError;
        }
        else
        {
            // 如果是信号量拥有者之外的任务wait,则要检查下是否需要使用
            // 优先级继承方式处理
            if (currentTask->prio < mutex->owner->prio)
            {
                tTask * owner = mutex->owner;
                // 如果当前任务的优先级比拥有者优先级更高,则使用优先级继承
                // 提升原拥有者的优先
                if (owner->state == TINYOS_TASK_STATE_RDY)
                {
                    // 任务处于就绪状态时,更改任务在就绪表中的位置
                    tTaskSchedUnRdy(owner);
                    owner->prio = currentTask->prio;
                    tTaskSchedRdy(owner);
                }
                else
                {
                    // 其它状态,只需要修改优先级
                    owner->prio = currentTask->prio;
                }
            }
            // 当前任务进入等待队列中
            tEventWait(&mutex->event, currentTask, (void *)0, tEventTypeMutex, waitTicks);
            tTaskExitCritical(status);
            // 执行调度, 切换至其它任务
            tTaskSched();
            return currentTask->waitEventResult;
        }
    }
}
uint32_t tMutexNoWaitGet (tMutex * mutex)
{
    uint32_t status = tTaskEnterCritical();

    if (mutex->lockedCount <= 0)
    {
        // 如果没有锁定,则使用当前任务锁定
        mutex->owner = currentTask;
        mutex->ownerOriginalPrio = currentTask->prio;
        mutex->lockedCount++;
        tTaskExitCritical(status);
        return tErrorNoError;
    }
    else
    {
        // 信号量已经被锁定
        if (mutex->owner == currentTask)
        {
            // 如果是信号量的拥有者再次wait,简单增加计数
            mutex->lockedCount++;
            tTaskExitCritical(status);
            return tErrorNoError;
        }
        tTaskExitCritical(status);
        return tErrorResourceUnavaliable;
    }
}
uint32_t tMutexNotify (tMutex * mutex)
{
    uint32_t status = tTaskEnterCritical();

    if (mutex->lockedCount <= 0)
    {
        // 锁定计数为0,信号量未被锁定,直接退出
        tTaskExitCritical(status);
        return tErrorNoError;
    }
    if (mutex->owner != currentTask)
    {
        // 不是拥有者释放,认为是非法
        tTaskExitCritical(status);
        return tErrorOwner;
    }
    if (--mutex->lockedCount > 0)
    {
        // 减1后计数仍不为0, 直接退出,不需要唤醒等待的任务
        tTaskExitCritical(status);
        return tErrorNoError;
    }
    // 是否有发生优先级继承
    if (mutex->ownerOriginalPrio != mutex->owner->prio)
    {
        // 有发生优先级继承,恢复拥有者的优先级
        if (mutex->owner->state == TINYOS_TASK_STATE_RDY)
        {
            // 任务处于就绪状态时,更改任务在就绪表中的位置
            tTaskSchedUnRdy(mutex->owner);
            currentTask->prio = mutex->ownerOriginalPrio;
            tTaskSchedRdy(mutex->owner);
        }
        else
        {
            // 其它状态,只需要修改优先级
            currentTask->prio = mutex->ownerOriginalPrio;
        }
    }

    // 检查是否有任务等待
    if (tEventWaitCount(&mutex->event) > 0)
    {
        // 如果有的话,则直接唤醒位于队列首部(最先等待)的任务
        tTask * task = tEventWakeUp(&mutex->event, (void *)0, tErrorNoError);
        mutex->owner = task;
        mutex->ownerOriginalPrio = task->prio;
        mutex->lockedCount++;
        // 如果这个任务的优先级更高,就执行调度,切换过去
        if (task->prio < currentTask->prio)
        {
            tTaskSched();
        }
    }
    tTaskExitCritical(status);
    return tErrorNoError;
}

tMutexWait中,大致逻辑是先对计数判断,非正即未锁定,那么就使用当前任务锁定,如果已经被锁定了那么就执行下一步判断,也就是当前任务是信号量的拥有者还是其他,是信号量的拥有者的话就简单加加一下,是其它任务的话,就需要当前任务的优先级比拥有者优先级更高,则使用优先级继承,插入等待队列中,这里有一个细节,由于我们修改了拥有者的优先级,那么如果拥有者处于就绪态,我们就需要更改其位置

tMutexNoWaitGet中,与tMutexWait类似,不同的是,tMutexNoWaitGet并没有相关等待队列的功能,当然也不会有优先级继承

tMutexNotify中,前面是一些判断,主要是合法判断,比如计数为0即未锁定,则直接退出,以及只能通过拥有者释放等,后面就是优先级继承的操作,先是恢复优先级,仿照tMutexWait中,处于就绪态就更改位置,方便运行;最后就是取出任务运行

互斥信号量的删除和状态查询

typedef struct  _tMutexInfo
{
    // 等待的任务数量
    uint32_t taskCount;
    // 拥有者任务的优先级
    uint32_t ownerPrio;
    // 继承优先级
    uint32_t inheritedPrio;
    // 当前信号量的拥有者
    tTask * owner;
    // 锁定次数
    uint32_t lockedCount;
}tMutexInfo;

uint32_t tMutexDestroy (tMutex * mutex)
{
    uint32_t count = 0;
    uint32_t status = tTaskEnterCritical();
    // 信号量是否已经被锁定,未锁定时没有任务等待,不必处理
    if (mutex->lockedCount > 0)
    {
        // 是否有发生优先级继承?如果发生,需要恢复拥有者的原优先级
        if (mutex->ownerOriginalPrio != mutex->owner->prio)
        {
            // 有发生优先级继承,恢复拥有者的优先级
            if (mutex->owner->state == TINYOS_TASK_STATE_RDY)
            {
                // 任务处于就绪状态时,更改任务在就绪表中的位置
                tTaskSchedUnRdy(mutex->owner);
                mutex->owner->prio = mutex->ownerOriginalPrio;
                tTaskSchedRdy(mutex->owner);
            }
            else
            {
                // 其它状态,只需要修改优先级
                mutex->owner->prio = mutex->ownerOriginalPrio;
            }
        }
        // 然后,清空事件控制块中的任务
        count = tEventRemoveAll(&mutex->event, (void *)0, tErrorDel);
        // 清空过程中可能有任务就绪,执行一次调度
        if (count > 0)
        {
            tTaskSched();
        }
    }

    tTaskExitCritical(status);
    return count;
}
void tMutexGetInfo (tMutex * mutex, tMutexInfo * info)
{
    uint32_t status = tTaskEnterCritical();
    // 拷贝需要的信息
    info->taskCount = tEventWaitCount(&mutex->event);
    info->ownerPrio = mutex->ownerOriginalPrio;
    if (mutex->owner != (tTask *)0)
    {
        info->inheritedPrio = mutex->owner->prio;
    }
    else
    {
        info->inheritedPrio = TINYOS_PRO_COUNT;
    }
    info->owner = mutex->owner;
    info->lockedCount = mutex->lockedCount;
    tTaskExitCritical(status);
}

新增tMutexInfo结构体,在tMutexGetInfo获取想要的信息,例如等待任务数量,原本优先级和继承优先级等

删除函数tMutexDestroy比起其他会更复杂一些,因为删除的内容不止是等待列表,还有互斥量的持有者,也需要考虑优先级继承的问题