uc/os-ii 互斥信号量及mutex.c源码分析
互斥信号量:
- 互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。
解决优先级反转:当高优先级任务需要使用某个共享资源,而恰巧该共享资源又被一个低优先级任务占用时,优先级反转问题就会发生。为了降解优先级反转,内核就必须支持优先级继承,将低优先级任务的优先级提升到高于高优先级任务的优先级,直到低优先级任务处理完毕共享资源。
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);}
#endifif (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 > 0pevent->OSEventName = (INT8U *)"?"; /*事件名称*/
#endifOS_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) */注:Pip变量是保存在OSEventCnt中的,当我们创建信号量的时候,就会给定这个值, 这个值也就是系统能够将等待该互斥信号量的任务提升的最高优先级INT8U mprio; /* 占用信号量的任务自己的优先级 */BOOLEAN rdy; /* Flag indicating task was ready 任务已就绪标志 */OS_TCB *ptcb; /*任务控制块结构体(占用互斥信号量的控制块)*/OS_EVENT *pevent2; /*指向任务未就绪时等待事件的指针*/INT8U y; /*优先级高3位对应的数值*/#if OS_CRITICAL_METHOD == 3 /* 为CPU状态寄存器分配存储 */OS_CPU_SR cpu_sr = 0;
#endif/*进行运行条件检查*/
#if OS_ARG_CHK_EN > 0 /*对μC/OS-III的大部分函数执行参数检查(>0)if (perr == (INT8U *)0) { /* 校验 'perr' */return;}if (pevent == (OS_EVENT *)0) { /* 校验 'pevent' */*perr = OS_ERR_PEVENT_NULL; /*错误指向:空指针return;}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件控制块(互斥信号量) */*perr = OS_ERR_EVENT_TYPE; /*错误指向:不是互斥信号量类型return;}if (OSIntNesting > 0) { /* 如果在中断 */*perr = OS_ERR_PEND_ISR; /* 错误指向:ISR调用这个函数和结果将导致阻塞 */return;}if (OSLockNesting > 0) { /*如果调度程序锁定 */*perr = OS_ERR_PEND_LOCKED; /*错误指向:调度任务锁住阻塞 */return;}
/*$PAGE*/OS_ENTER_CRITICAL(); /*进入临界区*/pip = (INT8U)(pevent->OSEventCnt >> 8); /* Get PIP from mutex 事件计数器高8位保存PIP *//* Is Mutex available? */if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) { /*提取低8位,看是否全为1,来判断互斥信号量是否可用。*/pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* 修改高8位的数据,这个时候高8位数据不变,还是表示继承优先级,低8位清零。 */pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; /* 将申请任务的优先级保存在低8位之中。低八位不可能全 部都为零,所以依旧可以表示信号量是否被占用。*/pevent->OSEventPtr = (void *)OSTCBCur; /* OSEventPtr指针指向当前占用信号的任务控制块*//*获取mutex*/if (OSTCBCur->OSTCBPrio <= pip) { /*申请信号量任务的优先级高于继承优先级(数值上小于)*/OS_EXIT_CRITICAL(); /*退出临界区 */*perr = OS_ERR_PIP_LOWER; /*错误指向:申请优先级高于继承优先级的优先级*/ /*不需要提高优先级*/ } else { /*申请任务的优先级高于继承优先级的优先级*/OS_EXIT_CRITICAL();*perr = OS_ERR_NONE; /*指向:调用成功*/}return;}mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /*取得互斥信号量任务的优先级(因为被占用,所以低8位为优先级)*/ptcb = (OS_TCB *)(pevent->OSEventPtr); /*从信号量中获得占用该信号量的任务*/if (ptcb->OSTCBPrio > pip) { /*占用的任务优先级低于最高继承优先级*/ if (mprio > OSTCBCur->OSTCBPrio) { /*且原先占用该mutex的优先级比提出申请该mutex的任务 的优先级低则提升原任务的优先级至PIP*/y = ptcb->OSTCBY; /*优先级高3位对应的数值(=prio>>3)*/if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0) { /* 看取得信号量的任务是否就绪 (对应就绪表的值) */OSRdyTbl[y] &= ~ptcb->OSTCBBitX; /* 若原任务已就绪,则将其从就 绪表中删除,并置就绪标志rdy*/ if (OSRdyTbl[y] == 0) { OSRdyGrp &= ~ptcb->OSTCBBitY;}rdy = OS_TRUE;} else {//若没有就绪,在等待一个事件pevent2 = ptcb->OSTCBEventPtr; /*指向未就绪任务等待的事件*/if (pevent2 != (OS_EVENT *)0) { /* 任务存在等待事件发生 */if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {pevent2->OSEventGrp &= ~ptcb->OSTCBBitY; /*清除对应的等待表和等待组*/}}rdy = OS_FALSE; /* 标记任务未就绪 */}ptcb->OSTCBPrio = pip; /* 提升占用信号的优先级至pip *//*根据新取得的优先级,修改优先级,更新任务控制块的相关数据。*//*OSRdyGrp |= OSMapTbl[prio >> 3]; 原先的查找表设置
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];*/
//不查表,直接把0x01左移动 OSRdyTbl[]元素即可#if OS_LOWEST_PRIO <= 63 /*改进*/ptcb->OSTCBY = (INT8U)( ptcb->OSTCBPrio >> 3); /*优先级的高三位*/ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x07); /*优先级的低三位*/ptcb->OSTCBBitY = (INT8U)(1u << ptcb->OSTCBY); /*优先级在就绪组的位置*/ptcb->OSTCBBitX = (INT8U)(1u << ptcb->OSTCBX); /*优先级在就绪表的位置*/
#elseptcb->OSTCBY = (INT8U)((ptcb->OSTCBPrio >> 4) & 0xFF);ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x0F);ptcb->OSTCBBitY = (INT16U)(1u << ptcb->OSTCBY);ptcb->OSTCBBitX = (INT16U)(1u << ptcb->OSTCBX);
#endif
if (rdy == OS_TRUE) { /* 如果取得信号量的任务为就绪状态,则继续让新的优先级就绪*/OSRdyGrp |= ptcb->OSTCBBitY; /* 更新就绪数组和就绪表(任务的新优先级) */OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;} else {//如果不是就绪pevent2 = ptcb->OSTCBEventPtr;if (pevent2 != (OS_EVENT *)0) { /* 将其更新ECB的等待数组和等待表(Add to event wait list) */pevent2->OSEventGrp |= ptcb->OSTCBBitY;pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;}}OSTCBPrioTbl[pip] = ptcb; /*优先级数组指针指向取得的任务控制块*/}}//到这说明前面已经把占用信号量任务的优先级提高//当前任务TCB域处理:OSTCBCur->OSTCBStat |= OS_STAT_MUTEX; /*#define OS_STAT_MUTEX ox10u */ /* 将当前任务的状态设为等待互斥信号量*/OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;/* 事件等待标志:事件发生,等待结 束*/OSTCBCur->OSTCBDly = timeout; /* 在任务控制块设置延时时间*/OS_EventTaskWait(pevent); /* 取消申请任务的的就绪状态,设置等待事件 阻塞,直到时间超时或者是事件发生 */OS_EXIT_CRITICAL();OS_Sched(); /* 执行调度,运行最高优先级的就绪任务如果原来低优先级的任务优先级被抬高了,则该任务将被执行 */OS_ENTER_CRITICAL();//原先占用mutex的任务执行完成释放了mutex,并唤醒了等待该mutex的最高优先级的任务switch (OSTCBCur->OSTCBStatPend) { /* 检查调用者是因为超时还是得到Mutex而运行*/case OS_STAT_PEND_OK: /*事件发生,正确等待目标信号量的到来*/ *perr = OS_ERR_NONE; /*正常返回*/break;case OS_STAT_PEND_ABORT: /*异常终止,其它任务把目标信号量删除,任务异常终止*/*perr = OS_ERR_PEND_ABORT; /* 异常终止 */break;case OS_STAT_PEND_TO: /*等待超时*/default:OS_EventTaskRemove(OSTCBCur, pevent); /*将该任务从该等待事件中取消*/*perr = OS_ERR_TIMEOUT; /* 时间超时没有得到互斥信号量 */break;}OSTCBCur->OSTCBStat = OS_STAT_RDY; /* 任务当前状态 *//*#define OS_STAT_RDY ox00u */OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /*等待事件发生,等待结束*/OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* 清除事件控制块指针*//*表示当前任务没有事件阻塞*/
/*EVENT_MULTI_EN表示等待多个事件*/
#if (OS_EVENT_MULTI_EN > 0)OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0; /*清除指向多事件控制块指针*/#endifOS_EXIT_CRITICAL();
}
恢复任务的优先级RdyAtPrio ()
描述:
当释放信号量的任务的优先级为等于互斥信号量最高可提升优先级的时候,
需要通过OSMutex_RdyAtPrio函数来将任务的优先级恢复功能是将ptcb指向的任务控制块的任务的优先级改为prio并且就绪
- 操作流程:
a.让升级了的任务优先级恢复原优先级并使之就绪,首先清0这个任务在就绪表、组,然后获取TCB中的得到相关域。
b.就绪表、组标记为原优先级的就绪状态,优先级指针表存TCB *。
参数: INT8U y;
static void OSMutex_RdyAtPrio (OS_TCB *ptcb, INT8U prio)
{y = ptcb->OSTCBY; /* 获取任务就绪表OSRdyGrp高三位*/ /* 将提升到高优先级从就绪表和就绪数组中清除 */OSRdyTbl[y] &= ~ptcb->OSTCBBitX; /*清除就绪表*/ if (OSRdyTbl[y] == 0) {//为0对应 /*清除就绪组*/OSRdyGrp &= ~ptcb->OSTCBBitY;}ptcb->OSTCBPrio = prio; /*将任务控制块的任务优先级恢复到prio*/
/*更新TCB任务控制块的跟优先级相关的数据成员*/
#if OS_LOWEST_PRIO <= 63ptcb->OSTCBY = (INT8U)((prio >> (INT8U)3) & (INT8U)0x07);ptcb->OSTCBX = (INT8U) (prio & (INT8U)0x07);ptcb->OSTCBBitY = (INT8U)(1u << ptcb->OSTCBY);ptcb->OSTCBBitX = (INT8U)(1u << ptcb->OSTCBX);
#elseptcb->OSTCBY = (INT8U)((prio >> (INT8U)4) & (INT8U)0x0F);ptcb->OSTCBX = (INT8U) (prio & (INT8U)0x0F);ptcb->OSTCBBitY = (INT16U)(1u << ptcb->OSTCBY);ptcb->OSTCBBitX = (INT16U)(1u << ptcb->OSTCBX);#endifOSRdyGrp |= ptcb->OSTCBBitY; /* 更新就绪组 */OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; /*更新就绪表*/OSTCBPrioTbl[prio] = ptcb; /*将对应优先级的数组指正指向该任务控制块*/}#endif /* OS_MUTEX_EN */
无等待的获取互斥信号量(非阻塞OSMutexAccept ()
- 参数: pevent 指向任务控制块的指针
perr 一个指向一个错误代码将返回到您的应用程序:
os_err_none 如果调用成功。
os_err_event_type ‘pevent’不是一个指向互斥信号量
os_err_pevent_null ‘pevent’是一个空指针
os_err_pend_isr 如果从ISR调用这个函数
os_err_pip_lower 申请任务的优先级比继承优先级小 Returns : == OS_TRUE 如果信号量可用,互斥信号量可采集
== OS_FALSE 如果信号量不可用操作流程:
a.进行运行条件检查(函数执行参数检查,验证事件块类型,中断状态)
b.关中段保护临界区
c.获取事件计数器高8位存放pip
d.判断事件计数器低8位数值看信号量是否可用
f.如果可用,到8位保存pip,低8位保存任务的优先级
g.任务获得信号量
#if OS_MUTEX_ACCEPT_EN > 0
BOOLEAN OSMutexAccept (OS_EVENT *pevent, //非阻塞的获取互斥型信号量函数INT8U *perr)
{INT8U pip; /* 优先级继承优先级 (PIP) */
#if OS_CRITICAL_METHOD == 3 /* 为CPU状态寄存器分配存储 */OS_CPU_SR cpu_sr = 0;
#endif#if OS_ARG_CHK_EN > 0 /*对μC/OS-III的大部分函数执行参数检查(>0)*/if (perr == (INT8U *)0) { /* 验证 'perr' */return (OS_FALSE); /*perr为null返回false*/}if (pevent == (OS_EVENT *)0) { /* 验证 'pevent' */*perr = OS_ERR_PEVENT_NULL; /*错误指向:'pevent'是一个空指针*/return (OS_FALSE);}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 验证事件块类型(不为互斥信号类型)*/*perr = OS_ERR_EVENT_TYPE; /*错误指向:perr 不是一个指向互斥信号量*/return (OS_FALSE);}if (OSIntNesting > 0) { /*确保它不是从ISR调用的 */*perr = OS_ERR_PEND_ISR; /*错误指向:从ISR调用这个函数*/return (OS_FALSE); }OS_ENTER_CRITICAL(); /* Get value (0 or 1) of Mutex 关中断(保护临界区*/pip = (INT8U)(pevent->OSEventCnt >> 8); /* Get PIP from mutex 事件计数器高8位保存PIP */if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {/*提取低8位,看是否全为1,来判断互斥信号量是否可用*/pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* 修改高8位的数据,这个时候高8位数据不变,还是表示继承优先级,低8位清零。 */pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; /* 将申请任务的优先级保存在低8位之中。低八位不可能全部都为零,所以依旧可以表示信号量是否被占用。 */pevent->OSEventPtr = (void *)OSTCBCur; /* OSEventPtr指针指向任务控制块 *//*把任务占用信号量*/if (OSTCBCur->OSTCBPrio <= pip) { /* 申请任务优先级高于继承优先级的数值。*/OS_EXIT_CRITICAL(); /* 开中断 */*perr = OS_ERR_PIP_LOWER; /*指向信息: 申请任务优先级高于继承优先级的 数值,无需提高优先级*/} else {//反之 OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; /*无等待的获取互斥信号量成功*/}return (OS_TRUE); /*资源可用,互斥信号量可采集*/}OS_EXIT_CRITICAL();*perr = OS_ERR_NONE; /*无等待的获取互斥信号量成功*/return (OS_FALSE);}
#endif
查询互斥信号量信息 OSMutexQuery ()
功能:查询互斥信号量信息
参数: pevent 是一个指向所需的互斥体相关的事件控制块。
p_mutex_data 是一个指向一个数据结构,包含了互斥信息返回:OS_ERR_NONE 调用成功
OS_ERR_QUERY_ISR If you called this function from an ISR
OS_ERR_PEVENT_NULL If ‘pevent’ is a NULL pointer
OS_ERR_PDATA_NULL ‘p_mutex_data’ 空指针
OS_ERR_EVENT_TYPE 非互斥信号量类型获取数据错误- 操作流程:
a.进行运行条件检查()
b.p_mutex_data结构来存放互斥信号量的信息
c.获取高八位优先继承优先和低八位任务优先级
d.拷贝任务等待信号量表到状态数据结构中
#if OS_MUTEX_QUERY_EN > 0
INT8U OSMutexQuery (OS_EVENT *pevent, /*一个指向所需互斥体相关的事件控制块*/OS_MUTEX_DATA *p_mutex_data) /*包换互斥信息的结构体
{INT8U i;
#if OS_LOWEST_PRIO <= 63 /*优先级*/INT8U *psrc; /*定义8位pevent->OSEventTbl[0]的地址指针*/INT8U *pdest; /*定义8位pdata->OSEventTbl[0]的地址指针*/
#elseINT16U *psrc;INT16U *pdest;
#endif
#if OS_CRITICAL_METHOD == 3 /* 为CPU状态寄存器分配存储 */OS_CPU_SR cpu_sr = 0;
#endifif (OSIntNesting > 0) { /* 如果在中断中 */return (OS_ERR_QUERY_ISR); /* 不能在中断中获取互斥信号量的信息 */}
#if OS_ARG_CHK_EN > 0 /*对μC/OS-III的大部分函数执行参数检查(>0)if (pevent == (OS_EVENT *)0) { /* 校验 'pevent' */return (OS_ERR_PEVENT_NULL);}if (p_mutex_data == (OS_MUTEX_DATA *)0) { /* 校验 'p_mutex_data' */return (OS_ERR_PDATA_NULL); /*错误指向:p_mutex_data 为空}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件块类型 */return (OS_ERR_EVENT_TYPE); /*返回错误:不是互斥信号量类型} OS_ENTER_CRITICAL(); /*关中断(保护临界区)p_mutex_data->OSMutexPIP = (INT8U)(pevent->OSEventCnt >> 8); /*高8位的数据,即继承优先级的数值*/p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);/*低八位的数据,信号量拥有的优先级if (p_mutex_data->OSOwnerPrio == 0xFF) { /*若低八位都为1 p_mutex_data->OSValue = OS_TRUE; /*则表示信号量可用(未被占用)*/} else {p_mutex_data->OSValue = OS_FALSE; /*否则被占用*/}p_mutex_data->OSEventGrp = pevent->OSEventGrp; /* 拷贝任务等待信号量表到状态数据结构中*//*指针遍历逐一赋值*/psrc = &pevent->OSEventTbl[0]; /*指向任务等待表*/ pdest = &p_mutex_data->OSEventTbl[0];/*指向存放等待表的地址*/for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {*pdest++ = *psrc++; /*地址指针下移一个类型地址,获取互斥信号量的值*/}OS_EXIT_CRITICAL(); /*开中断*/return (OS_ERR_NONE);
}
#endif /* OS_MUTEX_QUERY_EN*/
释放互斥信号量OSMutexPost ()
- 参数 : pevent 是一个指向所需的相关联的事件控制块的指针
返回 : OS_ERR_NONE 调用成功
OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量类型的指针
OS_ERR_PEVENT_NULL ‘pevent’是一个空指针
OS_ERR_POST_ISR 中断时不能释放互斥信号量
OS_ERR_NOT_MUTEX_OWNER 完成该任务的任务不是互斥信号量的所有者
OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级
- 操作流程:
a.进行运行条件检查(中断检测,校验’pevent’,校验事件块类型 )
b.如果任务不是互斥信号量的所有者任务,则报错
c.高优先级任务想得到Mutex时,如果Mutex占用者的优先级已经被升高,那么调用OSMutex_RdyAtPrio()函数使优先级升高了的任务恢复原来的优先级。
d.如果有多个任务在等待一个Mutex,那么其中优先级最高的任务获得Mutex,设为就绪
e.如果没有任务等待,低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用,OSEventPtr=0,占用的任务为空。
INT8U OSMutexPost (OS_EVENT *pevent)
{INT8U pip; /* 优先级继承优先级 */INT8U prio;
#if OS_CRITICAL_METHOD == 3 /* 为CPU状态寄存器分配存储 */OS_CPU_SR cpu_sr = 0;
#endifif (OSIntNesting > 0) { /* 如果在中断中 */return (OS_ERR_POST_ISR); /* 错误返回:不能在中断中释放互斥信号量 */}
#if OS_ARG_CHK_EN > 0if (pevent == (OS_EVENT *)0) { /* 校验'pevent */return (OS_ERR_PEVENT_NULL); /*错误指向:'pevent'是个空指针}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件块类型 (不是互斥信号量类型) */return (OS_ERR_EVENT_TYPE); /*错误指向:不是互斥信号量类型}OS_ENTER_CRITICAL(); /*关中断(保护临界区)pip = (INT8U)(pevent->OSEventCnt >> 8); /* 事件计数器高八位取得优先级的继承优先级数值(PIP)*/prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* 提取低8位,取得信号量任务的原先优先级 */if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) { /* 判断是提交信号量的任务是不是取得信号量的任务 */OS_EXIT_CRITICAL(); /*开中断*/return (OS_ERR_NOT_MUTEX_OWNER); /*错误指向:该任务不是互斥信号量的所有者任务*/}if (OSTCBCur->OSTCBPrio == pip) { /* 如果取得信号量的任务的优先级等于继承优先级 *//*判定已经优先级反转过*/OSMutex_RdyAtPrio(OSTCBCur, prio); /* 恢复取得信号量之前的优先级 */}OSTCBPrioTbl[pip] = OS_TCB_RESERVED; /* 将pip优先级对应的任务优先级数组指针赋值 为OS_TCB_RESERVED (占用)*/ if (pevent->OSEventGrp != 0) { /* 有等待任务时 */prio= OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);/*将等待信号量的最高优先级任务的设为就绪*/pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* 高8位保存 pip(继承优先级) */pevent->OSEventCnt |= prio; /*低八位保存刚刚转为就绪任务的优先级*/pevent->OSEventPtr = OSTCBPrioTbl[prio]; /*对应优先级的任务控制块获得互斥信号*/if (prio <= pip) { /* 刚刚转为就绪任务的优先级高于继承优先级,mutex机制已经不起作用 */OS_EXIT_CRITICAL(); /*开中断 OS_Sched(); /* 调度指向优先级最高的任务 */return (OS_ERR_PIP_LOWER); /*返回指向:任务优先级高于pip,mutex机制已经不起作用,会发生 优先级反转*/} else {/*内定的最高优先级pip务必大于所有能访问互斥资源的进程优先级*/OS_EXIT_CRITICAL();OS_Sched(); /* 调度指向优先级最高的任务 */return (OS_ERR_NONE); /*指向:调用成功 }}pevent->OSEventCnt |= OS_MUTEX_AVAILABLE; /* 如果没有任务等待信号量,则将低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用 */ pevent->OSEventPtr = (void *)0; /*设置互斥信号量没有被任务占用*/OS_EXIT_CRITICAL(); /*开中断*/return (OS_ERR_NONE); /*指向:调用成功 */
}
删除互斥信号量 OSMutexDel ()
描述:该函数删除一个互斥信号量和准备所有的任务在它之前。
参数: pervent: 是一个指向所需的互斥体相关的事件控制块。opt:
opt == OS_DEL_NO_PEND 当没有任务等待时才删除互斥项。
opt == OS_DEL_ALWAYS 即使任务正在等待,也会删除互斥体。perr :os_err_none 调用是成功和互斥锁被删除os_err_del_isr 从ISR删除互斥错误os_err_invalid_opt 无效选项指定os_err_task_waiting一个或多个任务等待互斥os_err_event_type 'pevent'不是一个指向互斥信号量os_err_pevent_null '事件'是一个空指针。
操作流程:
a.首先进行运行条件检查,若不满足,则返回空指针和错误代码(函数执行参数检查,校验事件块类型,中断状态)
b.查看是否有等待互斥信号量的任务(判断并标记是否有任务因该信号量阻塞),设置对应标志(OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。
c.按删除条件选择参数opt进行删除
(1)只有在没有任务等待时才删除互斥锁
a.初始化mutex
b.Mutex的ECB指针交还给空闲ECB链表
(2)总是删除互斥锁
b.初始化mutex
a.如果发生了提升优先级,通过OSMutex_RdyAtPro(ptcb,prio)恢复为prio优先级
c.阻塞的任务让所有因该事件被阻塞的任务全部就绪
d升级过优先级的OSTCBPrioTbl[]的TCB指针清0,然后设置ECB的域,返回 给ECB空闲链表,调用OSSched()
(3)无效选项指定(报错break)
#if OS_MUTEX_DEL_EN
OS_EVENT *OSMutexDel (OS_EVENT *pevent, //指向事件控制块的指针(创建信号量的句柄)INT8U opt, //删除条件选择参数optINT8U *perr) //返回应用程序的错误代码的指针
{
BOOLEAN tasks_waiting; /*标记是否有等待互斥信号量的任务*/
OS_EVENT *pevent_return; /*标记互斥信号量删除标志*/INT8U pip; /* 优先级继承优先级 */INT8U prio; /*还没优先级反转前的优先级(原优先级)*/
OS_TCB *ptcb; /*任务控制块*/#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 (pevent);}if (pevent == (OS_EVENT *)0) { /* 校验 'pevent' */*perr = OS_ERR_PEVENT_NULL; /*错误指向:‘事件’是一个空指针*/return (pevent);}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件块类型 */*perr = OS_ERR_EVENT_TYPE; /*'pevent'不是一个指向互斥信号量*/return (pevent);}if (OSIntNesting > 0) { /* 如果在中断 */*perr = OS_ERR_DEL_ISR; /* 错误指向:从ISR删除互斥错误 */return (pevent);}OS_ENTER_CRITICAL(); /*进入临界区,关中断(保护临界区)*/ if (pevent->OSEventGrp != 0) { /* 查看是否有等待互斥信号的任务 *//*OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。OSEventGrp,来表示等待任务表中的任务组。*/tasks_waiting = OS_TRUE; /* Yes 标记是有等待互斥信号量的任务*/ } else {tasks_waiting = OS_FALSE; /* No 标记是无等待互斥信号量的任务*/ }switch (opt) { /*删除的参数条件*/case OS_DEL_NO_PEND: /* 只有在没有任务等待时才删除互斥锁 --- */if (tasks_waiting == OS_FALSE) {
#if OS_EVENT_NAME_EN > 0pevent->OSEventName = (INT8U *)"?"; /*事件名称*/
#endifpip = (INT8U)(pevent->OSEventCnt >> 8); /*事件计数器高8位,取继承优先级的数值*/OSTCBPrioTbl[pip] = (OS_TCB *)0; /*根据继承优先级查找到任务控制表的首地址,设置OSTCKPrioTbl*/ pevent->OSEventType = OS_EVENT_TYPE_UNUSED; /*设置ECB数据结构的事件类型为未设置0*/pevent->OSEventPtr = OSEventFreeList; /*指向下一个ECB为空事件控制链表 */pevent->OSEventCnt = 0; /*初始化ECB计数器*/OSEventFreeList = pevent; /*把该事件控制块返回给空事件控制链表*/OS_EXIT_CRITICAL();*perr = OS_ERR_NONE; /*调用成功,删除互斥信号量*/pevent_return = (OS_EVENT *)0; /* 标志互斥信号量删除 */} else {OS_EXIT_CRITICAL();*perr = OS_ERR_TASK_WAITING; /*一个或多个任务等待互斥*/pevent_return = pevent; /*删除失败返回互斥信号量句柄*/}break;case OS_DEL_ALWAYS: /* 总是删除互斥锁 ---------------- */pip = (INT8U)(pevent->OSEventCnt >> 8); /* 从互斥信号量中获取优先继承优先的数值*/prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* 获取任务的最初优先级,获取取得信号量优先级的任务 */ptcb = (OS_TCB *)pevent->OSEventPtr; /*获取占用该信号的TCB控制块*/if (ptcb != (OS_TCB *)0) { /* 查看是否有任何任务拥有互斥锁,看是否ECB的任务控制块指针是否为空 */if (ptcb->OSTCBPrio == pip) { /*如果提升过优先级 */OSMutex_RdyAtPrio(ptcb, prio); /*恢复之前任务的优先级 */}}while (pevent->OSEventGrp != 0) { /* 遍历互斥信号量的所有等待任务 */(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);/*选择pevent中优先级最高的任务离开等待队列,进入就绪队列*/}
#if OS_EVENT_NAME_EN > 0pevent->OSEventName = (INT8U *)"?";
#endifpip = (INT8U)(pevent->OSEventCnt >> 8);OSTCBPrioTbl[pip] = (OS_TCB *)0; /*对应优先级 TCB指针清空,清除占用 */pevent->OSEventType = OS_EVENT_TYPE_UNUSED; /*设置ECB数据结构的事件类型为未设置*/pevent->OSEventPtr = OSEventFreeList; /*把该事件控制块返回给空事件控制链表 */pevent->OSEventCnt = 0; /*初始化ECB计数器*/OSEventFreeList = pevent; OS_EXIT_CRITICAL();if (tasks_waiting == OS_TRUE) { /* 有任务等待调度 */OS_Sched(); /* 最高优先级任务调度 */}*perr = OS_ERR_NONE; /*调用成功*/pevent_return = (OS_EVENT *)0; /* 设置互斥锁删除标志 */break;default:OS_EXIT_CRITICAL(); /*开中断,退出临界区*/*perr = OS_ERR_INVALID_OPT; /*无效选项指定*/pevent_return = pevent; break;}return (pevent_return); /*返回该事件控制块的指针*/
}
uc/os-ii 互斥信号量及mutex.c源码分析相关推荐
- UART0串口编程(四):UART0串口编程之在UC/OS—II中遭遇的危机
UART0串口编程之在UC/OS-II中遭遇的危机 一.潜在的危机 1.在uc/os操作系统中设计串口编程时,由于ISR和多个任务并发执行,情况比较复杂.尤其是接收状态为被动状态时,只能靠串行口中断来 ...
- Lab 6:uC/OS II
为什么80%的码农都做不了架构师?>>> 目标: 移植uC/OS II到RPi上,实现两个任务的调度.这两个任务能轮流点亮LED,并通过串口发送消息表明自己正在运行 具体步骤: ...
- uc/OS II——多任务设计
uc/OS II--多任务设计 (1)设计 开始任务 [1]/声明 开始任务 任务块 static OS_STK App_TaskStartStk[APP_TASK_START_STK_SIZE]; ...
- 鸿蒙轻内核源码分析:掌握信号量使用差异
摘要:本文带领大家一起剖析鸿蒙轻内核的信号量模块的源代码,包含信号量的结构体.信号量池初始化.信号量创建删除.申请释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十一 信号量Semap ...
- Linux线程同步(三)---互斥锁源码分析
先给自己打个广告,本人的微信公众号:嵌入式Linux江湖,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题. 一 源码分析 1.li ...
- python朴素贝叶斯调参_邹博机器学习升级版II附讲义、参考书与源码下载(数学 xgboost lda hmm svm)...
课程介绍 本课程特点是从数学层面推导最经典的机器学习算法,以及每种算法的示例和代码实现(Python).如何做算法的参数调试.以实际应用案例分析各种算法的选择等. 1.每个算法模块按照"原理 ...
- Nexus-5的B2G OS(FireFox OS)与Android4.4.4系统源码目录对比
1. 前提 1.准备一台Nexus 5手机. 2.准备B2G OS v2.6版的nexus-5系统源码:FireFox OS(B2G)源码获取与Build code. 3.Android4.4.4的系 ...
- linux串口互斥,UART0串口编程之在UC/OS—II中遭遇的危机
一.潜在的危机 1.在uc/os操作系统中设计串口编程时,由于ISR和多个任务并发执行,情况比较复杂.尤其是接收状态为被动状态时,只能靠串行口中断来接收数据. 2.在进行串行通信时,双方遵循相同的通信 ...
- 【 uC/OS II 】uC/OS II 源代码阅读(os_task.c)任务管理
前言 这个任务管理源代码,是整个系统最核心的部分,也是最难的部分,多看几遍吧.其中的核心结构体是: typedef struct os_tcb {OS_STK *OSTCBStkPtr; /* Poi ...
最新文章
- C++ OP相关注意事项
- java高并发(七)发布对象
- gradle 不支持多级子模块_解决gradle多模块依赖在Idea中能运行,gradle build失败的问题。...
- linux脚本中sed -i,Linux Shell 脚本之sed命令详解
- C语言实现舒尔特表格生成器
- bitlocker正在加密 c盘_win10不能分盘,硬盘提示Bitlocker已加密解决方法
- torch.sort
- c语言动态分配内存keil,keil5中结构体分配内存问题
- easyx文字输出汇总
- 针对于“上传文件”和“触发方式” 的解决方案(Antd个例)
- 三体船的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 计算机简单易懂知识,如何选购电脑主板?小白装机简单易懂的电脑主板选购知识指南...
- C语言中printf打印形式(%02X, %2X, %-2X, %.nf, %m.nf, %e, %m.ne, %2d, %-2d, %02d, %.2d)
- TikTok不可思议的崛起
- 【webpack】webpack 入门教程
- PHP反序列化漏洞-从入门到提升
- 完全碰撞问题 台球碰撞
- 数据结构课设——成绩分析问题
- x210:iNand分区情况
- 闭环步进电机和伺服电机的区别