互斥信号量的等待与通知

1 互斥信号量的等待与通知

1.1 设计原理

等待互斥信号量:

  • 信号量未被占有:
    • 标记为已占有。
    • 锁定次数+1。
  • 信号量被自己占有:
    • 锁定次数加1。
  • 信号量被高优先级任务占有:
    • 当前任务进入等待列表。
  • 信号量被低优先级任务占有:
    • 将占有信号量的优先级提升到当前任务的优先级。
    • 当前任务进入等待。

释放信号量:

  • 发生优先级翻转。
  • 未发生优先级翻转。

1.2 设计实现

/*************************************** Copyright (c)******************************************************
** File name            :   tMutex.c
** Latest modified Date :   2016-06-01
** Latest Version       :   0.1
** Descriptions         :   tinyOS的互斥信号量实现
**
**--------------------------------------------------------------------------------------------------------
** Created by           :   01课堂 lishutong
** Created date         :   2016-06-01
** Version              :   1.0
** Descriptions         :   The original version
**
**--------------------------------------------------------------------------------------------------------
** Copyright            :   版权所有,禁止用于商业用途
** Author Blog          :   http://ilishutong.com
**********************************************************************************************************/
#include "tMutex.h"
#include "tinyOS.h"

/**********************************************************************************************************
** Function name        :   tMutexInit
** Descriptions         :   初始化互斥信号量
** parameters           :   mutex 等待初始化的互斥信号量
** Returned value       :   无
***********************************************************************************************************/
void tMutexInit (tMutex * mutex)
{
    tEventInit(&mutex->event, tEventTypeMutex);

    mutex->lockedCount = 0;
    mutex->owner = (tTask *)0;
    mutex->ownerOriginalPrio = TINYOS_PRO_COUNT;
}

/**********************************************************************************************************
** Function name        :   tMutexWait
** Descriptions         :   等待信号量
** parameters           :   mutex 等待的信号量
** parameters           :   waitTicks 最大等待的ticks数,为0表示无限等待
** Returned value       :   等待结果,tErrorResourceUnavaliable.tErrorNoError,tErrorTimeout
***********************************************************************************************************/
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;
        }
    }
}

/**********************************************************************************************************
** Function name        :   tMutexNoWaitGet
** Descriptions         :   获取信号量,如果已经被锁定,立即返回
** parameters           :   tMutex 获取的信号量
** Returned value       :   获取结果, tErrorResourceUnavaliable.tErrorNoError
***********************************************************************************************************/
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;
    }
}

/**********************************************************************************************************
** Function name        :   tMutexNotify
** Descriptions         :   通知互斥信号量可用
** parameters           :   mbox 操作的信号量
** parameters           :   msg 发送的消息
** parameters           :   notifyOption 发送的选项
** Returned value       :   tErrorResourceFull
***********************************************************************************************************/
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;
}

参考资料:

  1. 【李述铜】从0到1自己动手写嵌入式操作系统
<think>好的,我现在需要回答用户关于如何在FreeRTOS中正确使用互斥信号量的问题。首先,我得回顾一下用户提供的引用内容,确保信息准确。根据引用[3],互斥信号量的创建是通过xSemaphoreCreateMutex()函数,它实际上调用xQueueCreateMutex()。而引用[4]提到互斥锁和信号量的区别,强调互斥锁用于资源管理,防止优先级反转。 用户需要正确的使用步骤,我应该分步骤说明。首先是创建互斥量,然后是获取和释放。记得提到优先级继承机制,这在引用[4]中有提到。另外,递归互斥量在引用[1]和[2]中提到,可能需要说明适用场景,比如任务需要多次获取同一个互斥量。 需要注意避免在中断中使用互斥量,这点可能用户会忽略,所以应该强调。此外,正确释放的重要性,避免死锁。引用[2]中的示例代码可能有用,可以建议用户参考具体的实现,比如STM32H743的例子。 可能的相关问题应该包括普通信号量的区别、优先级继承的作用、递归互斥量的使用场景以及常见错误。需要确保问题覆盖用户可能进一步想知道的内容。 最后,检查回答是否符合系统级指令,比如数学公式的格式是否正确,中文回答,结构清晰。确保没有使用Markdown,段落用空行分隔,代码块正确标注,引用标识正确添加。</think>在FreeRTOS中正确使用互斥信号量Mutex Semaphore)需要遵循以下步骤,以确保资源共享的安全性和任务调度的可靠性: --- ### 1. **创建互斥信号量** 使用`xSemaphoreCreateMutex()`函数创建互斥信号量,该函数会返回一个句柄用于后续操作: ```c SemaphoreHandle_t xMutex = xSemaphoreCreateMutex(); ``` 此函数内部通过`xQueueCreateMutex()`实现,创建了一个具有优先级继承机制的互斥量[^3][^4]。 --- ### 2. **获取互斥信号量** 在访问共享资源前,任务需调用`xSemaphoreTake()`获取互斥量: ```c if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) { // 成功获取互斥量,可安全操作共享资源 } ``` - **参数说明**:第二个参数为等待时间(例如100毫秒),若超时未获取则返回`pdFALSE`。 - **优先级继承**:若低优先级任务持有互斥量,而高优先级任务等待,低优先级任务的优先级会被临时提升,避免优先级反转。 --- ### 3. **释放互斥信号量** 操作完成后,必须调用`xSemaphoreGive()`释放互斥量: ```c xSemaphoreGive(xMutex); // 释放互斥量 ``` - **严格配对**:每次`Take`后必须有且仅有一次`Give`,否则会导致死锁。 - **禁止中断中使用**:互斥信号量不能在中断服务程序(ISR)中释放,需使用普通信号量或任务通知替代[^4]。 --- ### 4. **递归互斥信号量的使用** 如果任务需要多次获取同一互斥量(如递归调用),需使用递归互斥量: ```c SemaphoreHandle_t xRecursiveMutex = xSemaphoreCreateRecursiveMutex(); // 获取 xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY); // 释放 xSemaphoreGiveRecursive(xRecursiveMutex); ``` 递归互斥量会记录获取次数,确保释放次数获取次数匹配[^1][^2]。 --- ### 5. **典型应用场景** - **共享外设访问**:如SPI、I2C总线操作。 - **全局变量保护**:多任务同时读写全局变量时。 - **临界区扩展**:当临界区代码较长时,替代`taskENTER_CRITICAL()`以提高系统响应性[^4]。 --- ### 注意事项 - **避免长时间持有**:尽量减少互斥量的持有时间,防止其他任务阻塞。 - **死锁预防**:确保任务按固定顺序获取多个互斥量。 - **资源释放检查**:任务退出前必须确认已释放所有互斥量。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值