uc/os-ii 互斥信号量及mutex.c源码分析

本文详细介绍了μC/OS-II中的互斥信号量,包括其在解决优先级反转问题中的作用,以及OS_MUTEX.C中的关键函数如OSMutexCreate(), OSMutexPend(), OSMutexPost()等。分析了信号量在任务调度、优先级管理以及资源控制中的应用,同时探讨了如何避免死锁和饥饿现象。" 114165837,10537234,使用命令行配置DHCP服务器,"['网络管理', '服务器配置', '命令行工具', 'DHCP服务']
摘要由CSDN通过智能技术生成

互斥信号量:


  • 互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。
    解决优先级反转:当高优先级任务需要使用某个共享资源,而恰巧该共享资源又被一个低优先级任务占用时,优先级反转问题就会发生。为了降解优先级反转,内核就必须支持优先级继承,将低优先级任务的优先级提升到高于高优先级任务的优先级,直到低优先级任务处理完毕共享资源。

OS_MUTEX.C中µC/OS-Ⅱ提供对互斥信号量进行管理的函数

这里写图片描述

  • 事件控制块ECB结构
typedef struct {
    INT8U   OSEventType;                      /* 事件类型  */
    INT8U   OSEventGrp;                    /*事件等待组*/
    INT16U  OSEventCnt;             /* 计数器(当事件是信号量时) */
    void   *OSEventPtr;             /*空事件块时用来链接空事件链表
                               事件请求成功后指向占用任务的TCB指针*/
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; /*事件等待表*/
} OS_EVENT;

这里写图片描述

  1. OSEventTbl[]任务组 和 OSEventGrp任务表 很像前面讲到的OSRdyTbl[]和OSRdyGrp,前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
  2. OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。(EventWaitListInit()来初始化)
  3. OSEventCnt被分成了低8位和高8位两部分:
    低8位用来存放信号值(该值为((INT16U)0x00FFu)时,信号为有效,否则信号为无效)
    高8位用来存放为了减少出现优先级反转现象而要提升的优先级别prio。

mutex中涉及的TCB

这里写图片描述

  • OSTCBCur-指向“当前任务控制块”的指针;
    (当前占用cpu的任务)
    OSTCBFreeList-“空任务控制块”链表的表头指针;
    OSTCBHighRdy -指向“将要运行最高优先级任务控制块”的指针;
    OSTCBList-“已使用任务控制块”链表的表头指针;
  • OSTCBTbl[]-任务控制块数组,所有的任务控制块都保存在这个数组中。
  • 具体做法为:系统在调用函数OSInit()对ucos进行初始化时,就现在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],然后把各个元素连接成链表,从而形成一个空的任务块链表。
    这里写图片描述
    这里写图片描述
  • OSTCBPrioTbl[]-任务控制块优先级表,按任务的优先级别将这些指针存放在数组的各个元素里,专门用来存放指向各任务控制块的指针。访问任务控制块时,不需要遍历任务控制块链表。
  • OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* 放置一个非空指针防止被 其他人占用(设置OSTCKPrioTbl为有效)

建立互斥信号量OSMutexCreate()

  • 1.创建一个互斥型信号量
    a.首先进行运行条件检查(函数执行参数检查,校验优先级,中断状态)
    b.检查PIP优先级是否被占用(该优先级是否有任务)
    c.检查是否有可用的空闲事件控制块

    a.b.c 都满足则建立互斥信号量
    d.取走了一个空闲ECB,调整空闲ECB链表指针
    e.紧接着将ECB设置成Mutex类型
    f.将事件控制块的计数器Cnt高8位设置为优先级,低8位全为1表示互斥信号量可用
    g.任务列表初始化完毕后,返回ECB指针。

这里写图片描述

*建立一个互斥型信号量 
*描述:建立一个互斥型信号量 
*参数:prio:当存取(访问)互斥型信号量时它的优先级。换言之,当任务需要信号量, 
*                     而另一优先级更高的任务想得到信号量,就改变当前任务的优先级,变为更高 
*                     假定你改变的优先级值小于任务竞争这个信号量的任务的值(即优先级更高) 
*      Perr:指向返回应用程序的错误代码的指针: 
*                               OS_ERR_NONE          如果调用成功 
*                               OS_ERR_CREATE_ISR   如果想从ISR中建立互斥
*                               OS_PRIO_EXIST       如果优先级继承优先级的优先级已经存在(优先级继承算法或优先级天花板算法) 
*                               OS_ERR_PEVENT_NULL  没有事件控制块可用 
*                               OS_PRIO_INVALID    如果你指定的优先级大于最大值(无效优先级) 
        INT8U:8位无符号char型变量
*********************************************************************************************************
*/

OS_EVENT  *OSMutexCreate (INT8U   prio,                             //prio是取得信号量的任务需要提升到的优先级
                          INT8U  *perr)                             //用于输出错误信息 
{
    OS_EVENT  *pevent;                                          //指向事件控制块的指针(创建信号量的句柄)
#if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
    OS_CPU_SR  cpu_sr = 0;                                  //得到当前处理器状态字的值,并将其保存在C函数局部变量之中
#endif



#if OS_ARG_CHK_EN > 0                                       //对μC/OS-III的大部分函数执行参数检查(>0)
    if (perr == (INT8U *)0) {                              /*校验 'perr'                          */
        return ((OS_EVENT *)0);
    }
    if (prio >= OS_LOWEST_PRIO) {                          /* 校验优先级,如果优先级无效                            */
        *perr = OS_ERR_PRIO_INVALID;                        //错误指向:无效优先级
        return ((OS_EVENT *)0);
    }
#endif
    if (OSIntNesting > 0) {                                /* 如果在中断中(调度器加锁)             */
        *perr = OS_ERR_CREATE_ISR;                         /* 错误指向:不允许在中断中建立互斥      */
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();                                    /*进入临界区,关中断(保护临界区)
    if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* 判断需要提升到的优先级上是否有建立任务    */
        OS_EXIT_CRITICAL();                                /* 任务已优先存在,开中断,退出临界区*/
        *perr = OS_ERR_PRIO_EXIST;                         /* 错误指向:需要提升优先级已经有任务         */
        return ((OS_EVENT *)0);
    }//通过任务优先级号快速找到当前任务在任务控制块中的首地址(即OSTCBStkPtr地址)
    OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* 放置一个非空指针防止被其他人占用(设置OSTCKPrioTbl为有效)                  */
    pevent             = OSEventFreeList;                  /* 获取下一个空闲事件控制块       */
    if (pevent == (OS_EVENT *)0) {                         /* 如果ECB是可获取的,是可用的              */
        OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* 释放占有的优先级 ,释放表项         */
        OS_EXIT_CRITICAL();                                 //退出临界区
        *perr              = OS_ERR_PEVENT_NULL;           /* 错误指向:没有事件控制块可用             */
        return (pevent);
    }
    OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* 取走了一个空闲ECB,调整空闲ECB链表指针       */
    OS_EXIT_CRITICAL();                                  /*开中断*/
    pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;        /*设置ECB数据结构的事件类型为互斥信号量*/
    pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8) | OS_MUTEX_AVAILABLE; /* 将优先级数值存在事件计数值OSEventCnt的高8位 */
                                            /*AVAILABLE的值存储在低8位,其值为0x00FF。若低八位全为1,表示该互斥信号量可以用,否则就是不可用*/
                                            /*低8位在没有任务占用时为0xFF,有任务占用时,用于保存占用任务的优先级*/
    pevent->OSEventPtr     = (void *)0;                   /*只有在所定义的事件是邮箱或者消息队列时才使用(指向下一个ECB的指针),断开与空闲链表的联系*/
#if OS_EVENT_NAME_EN > 0
    pevent->OSEventName    = (INT8U *)"?";                  /*事件名称*/
#endif
    OS_EventWaitListInit(pevent);                       /*初始化ECB的任务等待表表,全为0,没有任务在等待事件 */
    *perr                  = OS_ERR_NONE;               /*指向:调用成功(互斥信号量创建成功)
    return (pevent);                                    
}

/*$PAGE*/

等待(申请)互斥信号量OSMutexPend()

  • 描述: pevent :是一个指向所需的相关联的事件控制块的指针
    timeout:等待超时时限
    如果非0,您的任务将等待资源达到该参数指定的时间量。
    如果为0,任务将永远在指定的位置等待互斥信号量,直到资源可用为止。
    perr : 指向错误代码 OS_ERR_NONE 调用成功,任务拥有互斥锁。
    OS_ERR_TIMEOUT 互斥锁在指定的“超时”内不可用。
    OS_ERR_PEND_ABORT 互斥锁上的等待被异常中止。.
    OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量
    OS_ERR_PEVENT_NULL ‘pevent’ 是空指针
    OS_ERR_PEND_ISR ISR调用这个函数时错误.
    OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级(互斥机制失效)
    OS_ERR_PEND_LOCKED 调度器上锁不能等待信号量
  • 操作流程:
    a.进行运行条件检查
    b.足运行条件后,首先检查是否有Mutex可用,如果有可用的Mutex,则占用这个Mutex,调用者获得共享资源的使用权;
    c.如果没有可用的Mutex,则需要检查Mutex的占用者是否需要提升优先级;(从ECB中取得占用的TCB指针和优先级,判断只有当占用ECB的优先级比升级优先级和当前请求优先级都低的时候才进行升级,)
    d.对需要提升优先级的占用者进行一系列处理;
    对占用任务判定是否就绪,就绪就更新新优先级的就绪组和就绪表,未就绪则更新ECB的等待数和等待表
    f.挂起调用者,并调用调度函数切换最高优先级任务;
    g.当调用者再次运行时,需要检查调用者是因为超时还是得到Mutex而运行的,并作相应处理。
#define  OS_MUTEX_KEEP_LOWER_8   ((INT16U)0x00FFu)   /*设置低8位全为1*/
#define  OS_MUTEX_KEEP_UPPER_8   ((INT16U)0xFF00u)   /*设置高8位全为1*/
#define  OS_MUTEX_AVAILABLE      ((INT16U)0x00FFu)
    INT8U      pip;                                              /* 优先级继承优先级 (PIP)      */

      注&#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值