互斥信号量:
- 互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。
解决优先级反转:当高优先级任务需要使用某个共享资源,而恰巧该共享资源又被一个低优先级任务占用时,优先级反转问题就会发生。为了降解优先级反转,内核就必须支持优先级继承,将低优先级任务的优先级提升到高于高优先级任务的优先级,直到低优先级任务处理完毕共享资源。
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;
- OSEventTbl[]任务组 和 OSEventGrp任务表 很像前面讲到的OSRdyTbl[]和OSRdyGrp,前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
- OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。(EventWaitListInit()来初始化)
- 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) */
注&#