uC/OS-III之资源管理--互斥型信号量

时间:2020-12-04 14:43:33

1.uC/OS-III支持一种特殊的二进制信号量–互斥型信号量。

2.互斥型信号量通过安全优先级继承机制(一旦一个具有高优先级的任务H想要访问共享资源,占有该资源的任务的优先级将被提升至与任务H一样),来避免*优先级翻转的问题。

3.互斥型信号量是一种被定义为OS_MUTEX数据类型的内核对象,它的定义位于os.h中。
注:与信号量一样,只有任务才能使用互斥型信号量(中断服务程序不可以)。

typedef  struct  os_mutex            OS_MUTEX;              // 625行

struct os_mutex { // 817行 - 830行
OS_OBJ_TYPE Type; // 互斥信号量的类型,必须设置成OS_OBJ_TYPE_MUTEX
CPU_CHAR *NamePtr; // 互斥信号量的名称
OS_PEND_LIST PendList; // 任务挂起表
#if OS_CFG_DBG_EN > 0u // 调试相关
OS_MUTEX *DbgPrevPtr;
OS_MUTEX *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
OS_TCB *OwnerTCBPtr; // 指向占有该互斥信号量的任务的任务控制块OS_TCB
OS_PRIO OwnerOriginalPrio; // 记录任务占有互斥信号量之前的优先级
OS_NESTING_CTR OwnerNestingCtr; // 互斥信号量的嵌套深度
CPU_TS TS; // 记录上一次被释放的时间
};

4.互斥型信号量通过OSMutexCreate()创建,它的定义位于os_mutex.c中。

void  OSMutexCreate (OS_MUTEX    *p_mutex,
CPU_CHAR *p_name,
OS_ERR *p_err) // 66行 - 118行
{
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器

#ifdef OS_SAFETY_CRITICAL // 系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508 // 系统安全性检查(IEC61508)
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif

#if OS_CFG_ARG_CHK_EN > 0u // 允许参数检查
if (p_mutex == (OS_MUTEX *)0) { // 无效参数
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif

CPU_CRITICAL_ENTER(); // 进入临界区
p_mutex->Type = OS_OBJ_TYPE_MUTEX; // 标记结构体为互斥型信号量
p_mutex->NamePtr = p_name;
p_mutex->OwnerTCBPtr = (OS_TCB *)0;
p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)0; // 当OwnerNestingCtr为0时,互斥信号量可用
p_mutex->TS = (CPU_TS )0;
p_mutex->OwnerOriginalPrio = OS_CFG_PRIO_MAX;
OS_PendListInit(&p_mutex->PendList); // 初始化挂起表

#if OS_CFG_DBG_EN > 0u
OS_MutexDbgListAdd(p_mutex);
#endif
OSMutexQty++; // 系统互斥信号量数目加1

CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
}

5.等待互斥信号量使用OSMutexPend(),它的定义位于os_mutex.c中。

void  OSMutexPend (OS_MUTEX   *p_mutex,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err) // 329行 - 493行
{
OS_PEND_DATA pend_data;
OS_TCB *p_tcb;
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器

#ifdef OS_SAFETY_CRITICAL // 系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用该函数
*p_err = OS_ERR_PEND_ISR;
return;
}
#endif

#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_mutex == (OS_MUTEX *)0) { // 无效的参数
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default: // 无效的选项
*p_err = OS_ERR_OPT_INVALID;
return;
}
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行类型检查
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) { // 确保互斥型已被创建
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif

if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0; // 初始化返回的时间戳参数
}

CPU_CRITICAL_ENTER(); // 进入临界区
if (p_mutex->OwnerNestingCtr == (OS_NESTING_CTR)0) { // 嵌套层数OwnerNestingCtr为0时,表示互斥信号量可用
p_mutex->OwnerTCBPtr = OSTCBCurPtr;
p_mutex->OwnerOriginalPrio = OSTCBCurPtr->Prio;
p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)1;
if (p_ts != (CPU_TS *)0) {
*p_ts = p_mutex->TS; // 返回上次释放互斥信号量的时间
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return;
}
// 互斥信号量不可用
if (OSTCBCurPtr == p_mutex->OwnerTCBPtr) { // 当前任务正在使用互斥信号量
p_mutex->OwnerNestingCtr++; // 嵌套层数加1
if (p_ts != (CPU_TS *)0) {
*p_ts = p_mutex->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_MUTEX_OWNER; // 说明一下当前任务已拥有该互斥信号量
return;
}

if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 非阻塞型
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return;
} else { // 阻塞型
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 调度器是锁定
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
}
// 阻塞型
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
p_tcb = p_mutex->OwnerTCBPtr; // 执行占有该互斥信号量的任务的任务控制块OS_TCB
if (p_tcb->Prio > OSTCBCurPtr->Prio) { // 拥有互斥信号量的任务的优先级低于当前任务的优先级
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY:
OS_RdyListRemove(p_tcb); // 把当前任务从就绪表中删除
p_tcb->Prio = OSTCBCurPtr->Prio; // 提升拥有互斥信号量的任务的优先级
OS_PrioInsert(p_tcb->Prio); // 更新优先级位映射表
OS_RdyListInsertHead(p_tcb); // 插入到任务就绪表中
break;
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_DLY_SUSPENDED:
case OS_TASK_STATE_SUSPENDED:
p_tcb->Prio = OSTCBCurPtr->Prio; // 仅仅提高拥有互斥信号量的任务的优先级
break;
case OS_TASK_STATE_PEND: // 改变在挂起表中的位置
case OS_TASK_STATE_PEND_TIMEOUT:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
OS_PendListChangePrio(p_tcb,
OSTCBCurPtr->Prio); // 改变挂起表中的优先级
break;

default:
OS_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_STATE_INVALID;
return;
}
}

OS_Pend(&pend_data, // ???
(OS_PEND_OBJ *)((void *)p_mutex),
OS_TASK_PEND_ON_MUTEX,
timeout);

OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
OSSched(); // 执行调度程序

CPU_CRITICAL_ENTER(); // 进入临界区
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: // 得到互斥信号量
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: // 等待被禁止
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT: // 等待超时
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
*p_err = OS_ERR_TIMEOUT;
break;
case OS_STATUS_PEND_DEL: // 互斥信号量被删除
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_OBJ_DEL;
break;
default:
*p_err = OS_ERR_STATUS_INVALID;
break;
}
CPU_CRITICAL_EXIT(); // 退出临界区
}

6.任务释放互斥型信号量使用OSMutexPost(),它的定义位于os_mutex.c中。

void  OSMutexPost (OS_MUTEX  *p_mutex,
OS_OPT opt,
OS_ERR *p_err) // 636行 - 727行
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时保存CPU的状态寄存器

#ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用该函数
*p_err = OS_ERR_POST_ISR;
return;
}
#endif

#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_mutex == (OS_MUTEX *)0) { // 无效参数
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行目标类型检查
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) { // 该对象不是互斥型信号量
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif

CPU_CRITICAL_ENTER(); // 进入临界区
if (OSTCBCurPtr != p_mutex->OwnerTCBPtr) { // 当前任务不是拥有该互斥型信号量的任务
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_MUTEX_NOT_OWNER;
return;
}

OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
ts = OS_TS_GET(); // 获取时间戳
p_mutex->TS = ts;
p_mutex->OwnerNestingCtr--; // 减少互斥信号量的嵌套层数
if (p_mutex->OwnerNestingCtr > (OS_NESTING_CTR)0) { // 嵌套层数不为0,表示操作未完成
OS_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_MUTEX_NESTING;
return;
}

p_pend_list = &p_mutex->PendList; // 获取挂起表
if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { // 挂起表为空(没有任务等待该互斥信号量)
p_mutex->OwnerTCBPtr = (OS_TCB *)0;
p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)0;
OS_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return;
}
// 挂起表不为空
if (OSTCBCurPtr->Prio != p_mutex->OwnerOriginalPrio) { // 任务的当前优先级与它之前的优先级不同
OS_RdyListRemove(OSTCBCurPtr); // 把当前任务从就绪表中删除
OSTCBCurPtr->Prio = p_mutex->OwnerOriginalPrio; // 恢复其之前的优先级
OS_PrioInsert(OSTCBCurPtr->Prio); // 更新优先级位映射表
OS_RdyListInsertTail(OSTCBCurPtr); // 插入到就绪表中的尾部
OSPrioCur = OSTCBCurPtr->Prio;
}
// 获取挂起表中的头部的OS_TCB
p_tcb = p_pend_list->HeadPtr->TCBPtr;
p_mutex->OwnerTCBPtr = p_tcb; // 给互斥型信号量一个新的拥有者
p_mutex->OwnerOriginalPrio = p_tcb->Prio;
p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)1;

OS_Post((OS_PEND_OBJ *)((void *)p_mutex), // 发送互斥型信号量
(OS_TCB *)p_tcb,
(void *)0,
(OS_MSG_SIZE )0,
(CPU_TS )ts);

OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度

if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
OSSched(); // 执行调度程序
}
*p_err = OS_ERR_NONE;
}