本文主要介绍μC/OS III
中常用的功能和API
,包括内核中常见的内部接口、事件标志组OS_FLAG_GRP
、计数信号量OS_SEM
、互斥信号量OS_MUTEX
、消息OS_MSG
、消息队列OS_Q
、软件定时器OS_TMR
以及任务相关的API
等。
一. 内核中常见的内部接口
1. OS_TaskBlock
OS_TaskBlock
的实现如下:
[os_core.c OS_TaskBlock函数]
void OS_TaskBlock (OS_TCB *p_tcb,
OS_TICK timeout)
{
#if (OS_CFG_TASK_TICK_EN == DEF_DISABLED)
(void)timeout;
#endif
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
#if (OS_CFG_DYN_TICK_EN == DEF_ENABLED)
OS_TICK tick_ctr;
#endif
//如果timeout入参大于0,
//就把`p_tcb`放入到`OSTickListTimeout`这个`TickList`中
if (timeout > 0u) {
#if (OS_CFG_DYN_TICK_EN == DEF_ENABLED)
tick_ctr = BSP_OS_TickGet();
OS_TickListInsert(&OSTickListTimeout, p_tcb, timeout + (tick_ctr - OSTickCtr));
#else
OS_TickListInsert(&OSTickListTimeout, p_tcb, timeout);
#endif
//设置任务为Pend + TimeOut状态
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
} else {
//设置任务为Pend状态
p_tcb->TaskState = OS_TASK_STATE_PEND;
}
#else
//设置任务为Pend状态
p_tcb->TaskState = OS_TASK_STATE_PEND;
#endif
//把`p_tcb`从就绪队列中移除
OS_RdyListRemove(p_tcb);
}
这个函数判断timeout
的值是否为0,如果不为0的话,把任务加入到OSTickListTimeout
这个TickList
中。并把任务从就绪列表中移除。这个函数时μC/OS III
的内部函数,用户不能主动的调用它。
2. OS_Pend
OS_Pend
的实现如下:
[os_core.c OS_Pend函数]
void OS_Pend (OS_PEND_OBJ *p_obj,
OS_STATE pending_on,
OS_TICK timeout)
{
OS_PEND_LIST *p_pend_list;
OSTCBCurPtr->PendOn = pending_on;
OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
//如果timeout不为0,加入到TickList中,并
//把任务从ReadyList中移除
OS_TaskBlock(OSTCBCurPtr,
timeout);
if (p_obj != (OS_PEND_OBJ *)0) {
p_pend_list = &p_obj->PendList;
OSTCBCurPtr->PendObjPtr = p_obj;
//插入到PendList中
OS_PendListInsertPrio(p_pend_list, OSTCBCurPtr);
} else {
OSTCBCurPtr->PendObjPtr = (OS_PEND_OBJ *)0;
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_PendDbgNameAdd(p_obj,
OSTCBCurPtr);
#endif
}
OS_PEND_OBJ *p_obj
可以是事件标志组OS_FLAG_GRP
、互斥信号量OS_MUTEX
、消息队列OS_Q
、计数信号量OS_SEM
等中的一种。同时也可以为0,有一些Pend
操作可以是没有内核对象的Pend
操作,比如任务自身的消息队列以及任务自身的计数信号量。
OS_STATE pending_on
可以是以下:
OS_STATE | 说明 | 是否有OS_PEND_OBJ |
---|---|---|
OS_TASK_PEND_ON_FLAG | 事件标志组的Pend 操作 | 必须有 |
OS_TASK_PEND_ON_TASK_Q | 任务自身的消息队列的Pend 操作 | 没有 |
OS_TASK_PEND_ON_MUTEX | 互斥信号量的Pend 操作 | 必须有 |
OS_TASK_PEND_ON_Q | 消息队列的Pend 操作 | 必须有 |
OS_TASK_PEND_ON_SEM | 计数信号量的Pend 操作 | 必须有 |
OS_TASK_PEND_ON_TASK_SEM | 任务自身的技术信号量的Pend 操作 | 没有 |
这个函数设置PendOn
和PendStatus
,调用OS_TaskBlock
(如果传入的timeout
不为0,就把这个任务加入到TickList
中。把他从ReadyList
中移除),如果OS_PEND_OBJ *p_obj
不为0,再把这个任务插入到这个内核对象的PendList
中。
3. OS_PendAbort
OS_PendAbort
的实现如下:
[os_core.c OS_PendAbort函数]
void OS_PendAbort (OS_TCB *p_tcb,
CPU_TS ts,
OS_STATUS reason)
{
#if (OS_CFG_TS_EN == DEF_DISABLED)
(void)ts;
#endif
switch (p_tcb->TaskState) {
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_MSG_EN == DEF_ENABLED)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = 0u;
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_tcb->TS = ts;
#endif
//如果存在某个PendList中
//则把他从这个PendList中移除
OS_PendListRemove(p_tcb);
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
//如果存在于某个TickList中
//则把他从这个TickList中移除
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
OS_TickListRemove(p_tcb);
}
#endif
//如果不是suspend状态状态,则把他放入到就绪列表中
OS_RdyListInsert(p_tcb);
p_tcb->TaskState = OS_TASK_STATE_RDY;
p_tcb->PendStatus = reason;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
break;
//这里包含了suspended状态,
//只把它从PendList或者TickList中移除,但不放入就绪列表中
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
#if (OS_MSG_EN == DEF_ENABLED)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = 0u;
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_tcb->TS = ts;
#endif
OS_PendListRemove(p_tcb);
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
OS_TickListRemove(p_tcb);
}
#endif
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->PendStatus = reason;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
break;
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_DLY_SUSPENDED:
default:
break;
}
}
首先会根据任务的状态,来判断其处于什么任务列表中(PendList
、TickList
、PendList + TickList
),然后将其从任务列表中移除,并判断是否处于Suspended
状态,如果不是,再把这个任务加入到就绪列表ReadyList
中。
4. OS_Post
OS_Post
函数的实现如下:
[os_core.c OS_Post函数]
void OS_Post (OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb,
void *p_void,
OS_MSG_SIZE msg_size,
CPU_TS ts)
{
#if (OS_CFG_TS_EN == DEF_DISABLED)
(void)ts;
#endif
#if (OS_MSG_EN == DEF_DISABLED)
(void)p_void;
(void)msg_size;
#endif
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_DLY_SUSPENDED:
break;
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_MSG_EN == DEF_ENABLED)
p_tcb->MsgPtr = p_void;
p_tcb->MsgSize = msg_size;
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_tcb->TS = ts;
#endif
//如果这个任务处于某个PendList中
//把这个任务从这个PendList中移除
if (p_obj != (OS_PEND_OBJ *)0) {
OS_PendListRemove(p_tcb);
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_PendDbgNameRemove(p_obj, p_tcb);
#endif
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
//如果这个任务处于某个TickList中
//把这个任务从这个TickList中移除
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
OS_TickListRemove(p_tcb);
}
#endif
//没有包含suspended状态的任务
//插入到就绪列表ReadyList中
OS_RdyListInsert(p_tcb);
p_tcb->TaskState = OS_TASK_STATE_RDY;
p_tcb->PendStatus = OS_STATUS_PEND_OK;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
break;
//包含有suspend状态的任务,
//只把他们从相应的TickList和PendList中移除
//但是不把他们加入到ReadyList中
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
#if (OS_MSG_EN == DEF_ENABLED)
p_tcb->MsgPtr = p_void;
p_tcb->MsgSize = msg_size;
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_tcb->TS = ts;
#endif
if (p_obj != (OS_PEND_OBJ *)0) {
OS_PendListRemove(p_tcb);
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_PendDbgNameRemove(p_obj, p_tcb);
#endif
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
OS_TickListRemove(p_tcb);
}
#endif
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->PendStatus = OS_STATUS_PEND_OK;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
break;
default:
break;
}
}
这个函数会判断这个任务处于什么样的列表中(PendList
、TickList
、PendList + TickList
),将他们从中移除,如果任务状态不包含Suspended
状态,则把他们加入到就绪列表ReadyList
中。
二. 事件标志组OS_FLAG_GRP
1. 创建事件标志组OSFlagCreate
OSFlagCreate
函数片段1:
[os_flag.c OSFlagCreate函数片段1]
void OSFlagCreate (OS_FLAG_GRP *p_grp,
CPU_CHAR *p_name,
OS_FLAGS flags,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
//p_err的入参判断
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
//不能处于`ISR`中
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
//p_grp的入参判断
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
if (p_grp == (OS_FLAG_GRP *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
这一段代码做一些入参判断,并规定,不能中断处理函数ISR
中调用这个函数。
OSFlagCreate
函数片段2:
[os_flag.c OSFlagCreate函数片段2]
CPU_CRITICAL_ENTER();
//设置内核对象的类型
#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
p_grp->Type = OS_OBJ_TYPE_FLAG;
#endif
//设置内核对象的名字
#if (OS_CFG_DBG_EN == DEF_ENABLED)
p_grp->NamePtr = p_name;
#else
(void)p_name;
#endif
//设置Flags
p_grp->Flags = flags;
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_grp->TS = 0u;
#endif
//初始化`PendList`
OS_PendListInit(&p_grp->PendList);
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_FlagDbgListAdd(p_grp);
OSFlagQty++;
#endif
OS_TRACE_FLAG_CREATE(p_grp, p_name);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
这一段代码主要设置内核对象的类型和名字,并设置Flags
,以及初始化这个事件标志组的PendList
。
2. 删除事件标志组OSFlagDel
OSFlagDel
函数片段1:
[os_flag.c OSFlagDel函数片段1]
OS_OBJ_QTY OSFlagDel (OS_FLAG_GRP *p_grp,
OS_OPT opt,
OS_ERR *p_err)
{
...
#ifdef OS_SAFETY_CRITICAL
//p_err的入参判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_FLAG_DEL_ENTER(p_grp, opt);
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
OS_TRACE_FLAG_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
*p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在中断处理函数`ISR`中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_DEL_ISR;
OS_TRACE_FLAG_DEL_EXIT(OS_ERR_DEL_ISR);
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在`OS`没有运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_grp的入参判断
if (p_grp == (OS_FLAG_GRP *)0) {
OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//p_grp的内核对象类型判断
if (p_grp->Type != OS_OBJ_TYPE_FLAG) {
OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
函数片段1主要做一些入参的出错判断,并规定,不能在中断处理函数中调用,不能在OS
没有运行前调用。
OSFlagDel
函数片段2:
[os_flag.c OSFlagDel函数片段2]
CPU_CRITICAL_ENTER();
p_pend_list = &p_grp->PendList;
nbr_tasks = 0u;
switch (opt) {
//当`PendList`中没有任务时才删除
case OS_OPT_DEL_NO_PEND:
//如果`PendList`没有任务
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_FlagDbgListRemove(p_grp);
OSFlagQty--;
#endif
OS_TRACE_FLAG_DEL(p_grp);
//清零OS_FLAG_GRP里的变量和初始化`PendList`
OS_FlagClr(p_grp);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
} else {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_WAITING;
}
break;
//不管`PendList`中有没有任务都删除
case OS_OPT_DEL_ALWAYS:
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//对`PendList`中的每一个任务都调用`OS_PendAbort`
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//设置状态为OS_STATUS_PEND_DEL
//告诉内核这个任务的Pend操作是被删除的
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_DEL);
nbr_tasks++;
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_FlagDbgListRemove(p_grp);
OSFlagQty--;
#endif
OS_TRACE_FLAG_DEL(p_grp);
//清零OS_FLAG_GRP里的变量和初始化`PendList`
OS_FlagClr(p_grp);
CPU_CRITICAL_EXIT();
//引发一次调度
OSSched();
*p_err = OS_ERR_NONE;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
break;
}
OS_TRACE_FLAG_DEL_EXIT(*p_err);
return (nbr_tasks);
}
OS_OPT_DEL_NO_PEND
指的是,只有PendList
中没有任务的时候才删除这个OS_FLAG_GRP
;
OS_OPT_DEL_ALWAYS
指的是,不管PendList
中有没有任务,把这些任务都调用OS_PendAbort
从相应的PendList
以及TickList
中移除,在调用OS_PendAbort
时最后一个传参是OS_STATUS_PEND_DEL
,因为Pend Abort
操作会使那些任务的Pend
操作从阻塞状态继续向下进行,这个时候,需要告诉内核,他们是由于内核对象的删除才被放入到ReadyList
中(如果这个任务没有suspended
)的,这个会在OSFlagPend
函数中详解。然后再调用OS_FlagClr
清零OS_FLAG_GRP
里的变量和初始化PendList
。最终调用OSSched
引发一次调度。
3. 事件标志组Pend
操作OSFlagPend
OSFlagPend
函数片段1:
[os_flag.c OSFlagPend 函数片段1]
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
{
CPU_BOOLEAN consume;
OS_FLAGS flags_rdy;
OS_OPT mode;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_FLAG_PEND_ENTER(p_grp, flags, timeout, opt, p_ts);
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在中断处理函数ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ISR;
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_ISR);
return ((OS_FLAGS)0);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在系统没运行之前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_grp合法性判断
if (p_grp == (OS_FLAG_GRP *)0) {
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt合法性判断
switch (opt) {
case OS_OPT_PEND_FLAG_CLR_ALL:
case OS_OPT_PEND_FLAG_CLR_ANY:
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY:
case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_CLR_ALL | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
case OS_OPT_PEND_FLAG_CLR_ANY | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
case OS_OPT_PEND_FLAG_SET_ALL | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
case OS_OPT_PEND_FLAG_SET_ANY | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
break;
default:
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_grp的类型正确,确保其已经被创建(create函数)。
if (p_grp->Type != OS_OBJ_TYPE_FLAG) {
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
//consume 的意思是指,如果pend操作等到了相应的bit,
//那么这次Pend操作满足条件后会将相应的bit全部设置为条件不满足
if ((opt & OS_OPT_PEND_FLAG_CONSUME) != 0u) {
consume = DEF_TRUE;
} else {
consume = DEF_FALSE;
}
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
上面的代码规定,不能在ISR
中调用这个函数,不能在OS
运行起来之前调用这个函数。另外这个函数做了一些入参出错判断。
随后就根据opt
的选项来进行相应的处理:
[os_flag.c OSFlagPend 函数片段2]
//只获取`PendFlag`相关的信息
mode = opt & OS_OPT_PEND_FLAG_MASK;
CPU_CRITICAL_ENTER();
switch (mode) {
//SET ALL 指的是,等待所有的bit都为1
case OS_OPT_PEND_FLAG_SET_ALL:
flags_rdy = (p_grp->Flags & flags);
//如果条件全部满足
if (flags_rdy == flags) {
//如果有consume操作,那么把成立的条件全部清空
if (consume == DEF_TRUE) {
p_grp->Flags &= ~flags_rdy;
}
OSTCBCurPtr->FlagsRdy = flags_rdy;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
#endi
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
//条件成立直接返回
return (flags_rdy);
//如果不满足条件
} else {
//如果是非阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//返回出去,不阻塞
return ((OS_FLAGS)0);
//如果是阻塞模式
} else {
//如果锁住了调度器
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
*p_err = OS_ERR_SCHED_LOCKED;
//返回,不调度
return (0u);
}
}
//调用OS_FlagBlock,
//最终调用OS_Pend,将其加入相应的PendList和TickList
OS_FlagBlock(p_grp,
flags,
opt,
timeout);
CPU_CRITICAL_EXIT();
}
break;
//SET ANY值得是,只要有一个bit条件成立就可以
case OS_OPT_PEND_FLAG_SET_ANY:
flags_rdy = (p_grp->Flags & flags);
//如果条件满足
if (flags_rdy != 0u) {
//如果设置了 consume ,清除那个Bit
if (consume == DEF_TRUE) {
p_grp->Flags &= ~flags_rdy;
}
OSTCBCurPtr->FlagsRdy = flags_rdy;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
//条件成立直接返回
return (flags_rdy);
//如果条件不满足
} else {
//如果不阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//直接返回不阻塞
return ((OS_FLAGS)0);
//如果是阻塞模式
} else {
//处于所锁调度的状态
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
*p_err = OS_ERR_SCHED_LOCKED;
//直接返回不调度
return ((OS_FLAGS)0);
}
}
//调用OS_FlagBlock,
//最终调用OS_Pend,将其加入相应的PendList和TickList
OS_FlagBlock(p_grp,
flags,
opt,
timeout);
CPU_CRITICAL_EXIT();
}
break;
#if (OS_CFG_FLAG_MODE_CLR_EN == DEF_ENABLED)
//CLR ALL 指的是,等待到所有bit都为0时。
case OS_OPT_PEND_FLAG_CLR_ALL:
flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);
//如果条件成立,及都为0时
if (flags_rdy == flags) {
//如果设置了consume,把这个成立的条件全部清除(即设为1)
if (consume == DEF_TRUE) {
p_grp->Flags |= flags_rdy;
}
OSTCBCurPtr->FlagsRdy = flags_rdy;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
//条件成立直接返回
return (flags_rdy);
//如果条件不成立
} else {
//如果设置的时非阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//直接返回,不阻塞
return ((OS_FLAGS)0);
//如果设置的时阻塞模式
} else {
//如果锁住了调度器
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
*p_err = OS_ERR_SCHED_LOCKED;
//直接返回不调度
return (0);
}
}
//调用OS_FlagBlock,
//最终调用OS_Pend,将其加入相应的PendList和TickList
OS_FlagBlock(p_grp,
flags,
opt,
timeout);
CPU_CRITICAL_EXIT();
}
break;
//CLR ANY 值得时,只要有一个等待的bit为0,那么就条件成立
case OS_OPT_PEND_FLAG_CLR_ANY:
flags_rdy = (~p_grp->Flags & flags);
//如果条件成立
if (flags_rdy != 0u) {
//如果设置了consume,则把成立的条件bit设置1
if (consume == DEF_TRUE) {
p_grp->Flags |= flags_rdy;
}
OSTCBCurPtr->FlagsRdy = flags_rdy;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
//条件成立直接返回
return (flags_rdy);
//如果条件不成立
} else {
//如果是非阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//直接返回不阻塞
return ((OS_FLAGS)0);
//如果是阻塞模式
} else {
//如果锁住了调度器
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
*p_err = OS_ERR_SCHED_LOCKED;
//直接返回不调度
return (0u);
}
}
//调用OS_FlagBlock,
//最终调用OS_Pend,将其加入相应的PendList和TickList
OS_FlagBlock(p_grp,
flags,
opt,
timeout);
CPU_CRITICAL_EXIT();
}
break;
#endif
default:
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
OS_TRACE_FLAG_PEND_BLOCK(p_grp);
//如果条件不成立 + 阻塞模式 + 调度器没上锁
//进行一次调度
OSSched();
上面的代码片段是根据相应的Pend Flag
来进行判断函数等待条件成立的方式,以及是否是阻塞式的等待,是否包含consume
等。最后如果条件不成立,并且是阻塞模式和调度器没上锁的时候,才会调用一次OSSched
引发一次调度,这个时候当前任务已经处于PendList
或者TickList
中了,而没在ReadyList
。等到其他任务或中断有Post
操作,或者Abort
操作,或者删除操作,或者timeout
超时的话,才会切换到这个任务,继续向下进行:
[os_flag.c OSFlagPend 函数片段3]
CPU_CRITICAL_ENTER();
//检查PendStatus
switch (OSTCBCurPtr->PendStatus) {
//条件满足
case OS_STATUS_PEND_OK:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_FLAG_PEND(p_grp);
//设置为OS_ERR_NONE
*p_err = OS_ERR_NONE;
break;
//Abort 操作
case OS_STATUS_PEND_ABORT:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT:
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
*p_err = OS_ERR_TIMEOUT;
break;
//删除操作
case OS_STATUS_PEND_DEL:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
*p_err = OS_ERR_OBJ_DEL;
break;
//其他非法的值
default:
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_FAILED(p_grp);
*p_err = OS_ERR_STATUS_INVALID;
break;
}
//如果不是条件满足,返回退出
if (*p_err != OS_ERR_NONE) {
OS_TRACE_FLAG_PEND_EXIT(*p_err);
return (0u);
}
//如果是条件满足,才执行以下操作
flags_rdy = OSTCBCurPtr->FlagsRdy;
//如果设置了consume,清除相应的条件
if (consume == DEF_TRUE) {
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY:
p_grp->Flags &= ~flags_rdy;
break;
#if (OS_CFG_FLAG_MODE_CLR_EN == DEF_ENABLED)
case OS_OPT_PEND_FLAG_CLR_ALL:
case OS_OPT_PEND_FLAG_CLR_ANY:
p_grp->Flags |= flags_rdy;
break;
#endif
default:
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
}
CPU_CRITICAL_EXIT();
OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
return (flags_rdy);
}
又跑到这个任务之后,会去检查任务从Pend
处返回到继续运行的状态,如果是因为条件满足而返回的话,那么就再根据consume
的设置而选择清除那些条件。
4. 事件标志组的PendAbort
操作OSFlagPendAbort
这个函数把因为这个事件标志组的Pend
操作而被放入PendList
中或者TickList
中的任务,从PendList
或者TickList
中移除,重新的被放入ReadyList
中(如果这个任务没有被suspended
的话)。OS_OPT_PEND_ABORT_1
值得是只对PendList
中优先级最高的任务有效;OS_OPT_PEND_ABORT_ALL
指的是对PendList
中所有的任务都有效。
OSFlagPendAbort
的定义如下:
[os_flag.c OSFlagPendAbort 函数]
OS_OBJ_QTY OSFlagPendAbort (OS_FLAG_GRP *p_grp,
OS_OPT opt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
OS_OBJ_QTY nbr_tasks;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参的合法性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_OBJ_QTY)0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//ISR中不能调用这个函数
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ABORT_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核没有运行起来时不能调用这个函数
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_grp合法性检查
if (p_grp == (OS_FLAG_GRP *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
switch (opt) {
case OS_OPT_PEND_ABORT_1:
case OS_OPT_PEND_ABORT_ALL:
case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_grp已经通过了create函数,确实存在
if (p_grp->Type != OS_OBJ_TYPE_FLAG) {
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_grp->PendList;
//如果PendList中的函数为空,则直接退出
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_ABORT_NONE;
return (0u);
}
nbr_tasks = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历PendList
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//对每一个任务都调用PendAbort,将他们从相应的PendList和TickList中移除
//并放入到ReadyList中(如果没有suspended)
//最后一个参数是OS_STATUS_PEND_ABORT,告诉内核这些任务是因为Abort操作
//才被放入到`ReadyList`中的
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_ABORT);
nbr_tasks++;
//如果没有OS_OPT_PEND_ABORT_ALL
//那么只对第一个任务操作,就是优先级最高的任务。
if (opt != OS_OPT_PEND_ABORT_ALL) {
break;
}
}
CPU_CRITICAL_EXIT();
//如果没加OS_OPT_POST_NO_SCHED
//则引发一次调度
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
OSSched();
}
*p_err = OS_ERR_NONE;
return (nbr_tasks);
}
OSFlagPendAbort
是将PendList
中的所有任务从PendList
(和TickList
)中移除,并放入ReadyList
中(如果任务没有suspended
的话)。
5. 获取当前任务的就绪标志OSFlagPendGetFlagsRdy
OSFlagPendGetFlagsRdy
函数实现如下:
[os_flag.c OSFlagPendGetFlagsRdy 函数]
OS_FLAGS OSFlagPendGetFlagsRdy (OS_ERR *p_err)
{
OS_FLAGS flags;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_FLAGS)0);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核没有运行之前不能调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//ISR中不能调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ISR;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
//获取OSTCBCurPtr->FlagsRdy
flags = OSTCBCurPtr->FlagsRdy;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return (flags);
}
这个函数在ISR
中不能调用,同时也不能在内核运行前调用。
6. 事件标志组的Post
操作OSFlagPost
OSFlagPost
函数实现如下:
[os_flag.c OSFlagPost 函数片段1]
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,
OS_ERR *p_err)
{
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
OS_OPT mode;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
CPU_TS ts;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_FLAG_POST_ENTER(p_grp, flags, opt);
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核运行前不能调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_FLAG_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_grp入参合法性判断
if (p_grp == (OS_FLAG_GRP *)0) {
OS_TRACE_FLAG_POST_FAILED(p_grp);
OS_TRACE_FLAG_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_grp已经经过了create函数
if (p_grp->Type != OS_OBJ_TYPE_FLAG) {
OS_TRACE_FLAG_POST_FAILED(p_grp);
OS_TRACE_FLAG_POST_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
OS_TRACE_FLAG_POST(p_grp);
这个函数片段1做一些合法性的判断,他不能在内核运行前调用,和Pend
操作不同的是,他可以在ISR
中调用。
[os_flag.c OSFlagPost 函数片段2]
switch (opt) {
//如果是SET操作
case OS_OPT_POST_FLAG_SET:
case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED:
CPU_CRITICAL_ENTER();
//把相应的位置1
p_grp->Flags |= flags;
break;
//如果是CLR操作
case OS_OPT_POST_FLAG_CLR:
case OS_OPT_POST_FLAG_CLR | OS_OPT_POST_NO_SCHED:
CPU_CRITICAL_ENTER();
//把相应的位置0
p_grp->Flags &= ~flags;
break;
default:
*p_err = OS_ERR_OPT_INVALID;
OS_TRACE_FLAG_POST_EXIT(*p_err);
return (0u);
}
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_grp->TS = ts;
#endif
p_pend_list = &p_grp->PendList;
//如果PendList中为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
OS_TRACE_FLAG_POST_EXIT(*p_err);
//返回
return (p_grp->Flags);
}
p_tcb = p_pend_list->HeadPtr;
//遍历PendList中的每一个任务
while (p_tcb != (OS_TCB *)0) {
p_tcb_next = p_tcb->PendNextPtr;
mode = p_tcb->FlagsOpt & OS_OPT_PEND_FLAG_MASK;
switch (mode) {
//全部Bit设为1才满足
case OS_OPT_PEND_FLAG_SET_ALL:
flags_rdy = (p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy == p_tcb->FlagsPend) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
//只要有一个Bit为1就满足
case OS_OPT_PEND_FLAG_SET_ANY:
flags_rdy = (p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy != 0u) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
#if (OS_CFG_FLAG_MODE_CLR_EN == DEF_ENABLED)
//全部Bit为0才满足
case OS_OPT_PEND_FLAG_CLR_ALL:
flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy == p_tcb->FlagsPend) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
//主要有一个为0才满足
case OS_OPT_PEND_FLAG_CLR_ANY:
flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy != 0u) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
#endif
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_FLAG_PEND_OPT;
OS_TRACE_FLAG_POST_EXIT(*p_err);
return (0u);
}
p_tcb = p_tcb_next;
}
CPU_CRITICAL_EXIT();
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
OSSched();
}
CPU_CRITICAL_ENTER();
flags_cur = p_grp->Flags;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
OS_TRACE_FLAG_POST_EXIT(*p_err);
return (flags_cur);
}
如果PendList
列表为空,则直接返回,否则遍历整个PendList
,对其中所有满足条件的任务调用OS_FlagTaskRdy
函数,其定义如下:
[os_flag.c OS_FlagTaskRdy 函数]
void OS_FlagTaskRdy (OS_TCB *p_tcb,
OS_FLAGS flags_rdy,
CPU_TS ts)
{
#if (OS_CFG_TS_EN == DEF_DISABLED)
(void)ts;
#endif
p_tcb->FlagsRdy = flags_rdy;
//设置为OS_STATUS_PEND_OK,告诉内核这个任务的Pend等待的条件已满足
p_tcb->PendStatus = OS_STATUS_PEND_OK;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_tcb->TS = ts;
#endif
switch (p_tcb->TaskState) {
//如果是Pend或者Pend + TimeOut,没有suspended
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
//如果是OS_TASK_STATE_PEND_TIMEOUT从TickList中移除
OS_TickListRemove(p_tcb);
}
#endif
//如果没有suspend插入到就绪队列表中
OS_RdyListInsert(p_tcb);
//设置任务为就绪态
p_tcb->TaskState = OS_TASK_STATE_RDY;
break;
//如果是suspended
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
break;
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_DLY_SUSPENDED:
case OS_TASK_STATE_SUSPENDED:
default:
break;
}
//从PendList中移除
OS_PendListRemove(p_tcb);
}
OS_FlagTaskRdy
这个函数就是从PendList
(可能也从TickList
)中把这个任务移除,如果没有Suspended
并加入就绪列表ReadyList
中。
将每个任务都调用OS_FlagTaskRdy
之后,如果没有特别的声明OS_OPT_POST_NO_SCHED
这个选项,那么就调用OSSched
引发一次调度。
三. 计数信号量OS_SEM
1. 创建计数信号量OSSemCreate
创建信号量OSSemCreate
的实现如下:
[os_sem.c OSSemCreate 函数]
void OSSemCreate (OS_SEM *p_sem,
CPU_CHAR *p_name,
OS_SEM_CTR cnt,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性的判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用这个函数
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem入参合法性判断
if (p_sem == (OS_SEM *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
CPU_CRITICAL_ENTER();
#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
p_sem->Type = OS_OBJ_TYPE_SEM;
#endif
p_sem->Ctr = cnt;
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_sem->TS = 0u;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
p_sem->NamePtr = p_name;
#else
(void)p_name;
#endif
//初始化PendList
OS_PendListInit(&p_sem->PendList);
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_SemDbgListAdd(p_sem);
OSSemQty++;
#endif
OS_TRACE_SEM_CREATE(p_sem, p_name);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
可以总结为如下:
- 不能在中断中使用;
- 设置默认计数
ctr
和名字NamePtr
; - 初始化这个计数信号量的
PendList
;
2. 删除计数信号量OSSemDel
删除信号量OSSemDel
函数的实现如下:
[os_sem.c OSSemDel 函数]
OS_OBJ_QTY OSSemDel (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
{
OS_OBJ_QTY nbr_tasks;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_SEM_DEL_ENTER(p_sem, opt);
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
OS_TRACE_SEM_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
*p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中使用
if (OSIntNestingCtr > 0u) {
OS_TRACE_SEM_DEL_EXIT(OS_ERR_DEL_ISR);
*p_err = OS_ERR_DEL_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核运行之前不能调用这个函数
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_SEM_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem入参合法性判断
if (p_sem == (OS_SEM *)0) {
OS_TRACE_SEM_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_sem已经经过create函数
if (p_sem->Type != OS_OBJ_TYPE_SEM) {
OS_TRACE_SEM_DEL_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->PendList;
nbr_tasks = 0u;
switch (opt) {
//当PendList中没有任务时才删除
case OS_OPT_DEL_NO_PEND:
//如果PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_SemDbgListRemove(p_sem);
OSSemQty--;
#endif
OS_TRACE_SEM_DEL(p_sem);
//初始化OS_SEM中的成员,并初始化PendList
OS_SemClr(p_sem);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
} else {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_WAITING;
}
break;
//不管PendList中是否为空都删除
case OS_OPT_DEL_ALWAYS:
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历整个PendList
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//对每个任务调用OS_PendAbort
//最后一个入参是OS_STATUS_PEND_DEL
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_DEL);
nbr_tasks++;
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_SemDbgListRemove(p_sem);
OSSemQty--;
#endif
OS_TRACE_SEM_DEL(p_sem);
//初始化OS_SEM中的成员,并初始化PendList
OS_SemClr(p_sem);
CPU_CRITICAL_EXIT();
//调用OSSched,引发一次调度
OSSched();
*p_err = OS_ERR_NONE;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
break;
}
OS_TRACE_SEM_DEL_EXIT(*p_err);
return (nbr_tasks);
}
他的中opt
可以为OS_OPT_DEL_NO_PEND
和OS_OPT_DEL_ALWAYS
。
OS_OPT_DEL_NO_PEND
意思是指只有在这个计数信号量的PendList
中没有任务的时候,才会删除。当PendList
为空时,调用OS_SemClr
把OS_SEM
中的各个变量清0,并初始化PendList
。OS_OPT_DEL_ALWAYS
意思是指不管PendList
有没有任务都会被删除,如果有任务的话,这些任务的Pend
操作都会直接Pass
。调用OS_PendAbort
,这个函数的实现如下(这里最后一个入参为OS_STATUS_PEND_DEL
):
把这些任务全部从相应的PendList
和TickList
中移除并放入ReadyList
之后,再调用OS_SemClr
初始化OS_SEM
中的变量和PendList
。最后调用一次OSSched
引发一次调度。
3. 计数信号量的Pend
操作OSSemPend
OSSemPend
函数片段1
[os_sem.c OSSemPend 函数 片段1]
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
CPU_SR_ALLOC();
#if (OS_CFG_TS_EN == DEF_DISABLED)
(void)p_ts;
#endif
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_SEM_PEND_ENTER(p_sem, timeout, opt, p_ts);
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用这个函数
if (OSIntNestingCtr > 0u) {
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_PEND_ISR);
*p_err = OS_ERR_PEND_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行之前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem的合法性判断
if (p_sem == (OS_SEM *)0) {
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt的合法性判断
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_sem已经经过create函数
if (p_sem->Type != OS_OBJ_TYPE_SEM) {
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
代码片段1,主要是入参的合法性判断,规定这个函数不能在ISR
中运行,不能在内核运行前调用。
[os_sem.c OSSemPend 函数 片段2]
CPU_CRITICAL_ENTER();
//如果Ctr > 0,即满足等待条件
if (p_sem->Ctr > 0u) {
//自减操作
p_sem->Ctr--;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_sem->TS;
}
#endif
ctr = p_sem->Ctr;
OS_TRACE_SEM_PEND(p_sem);
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(OS_ERR_NONE);
//错误码设置为OS_ERR_NONE
*p_err = OS_ERR_NONE;
//直接返回
return (ctr);
}
//下面是不满足等待条件
//如果是非阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
//错误码设置为OS_ERR_PEND_WOULD_BLOCK
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//立即返回
return (ctr);
//如果是阻塞模式
} else {
//如果锁住了调度器
if (OSSchedLockNestingCtr > 0u) {
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_SCHED_LOCKED);
//错误码设置为OS_ERR_SCHED_LOCKED
*p_err = OS_ERR_SCHED_LOCKED;
//直接返回
return (0u);
}
}
//如果不满足条件,阻塞模式,并且没有锁住调度器
//调用OS_Pend,加入PendList(如果timeout不为0并加入TickList)
//从ReadyList中移除
OS_Pend((OS_PEND_OBJ *)((void *)p_sem),
OS_TASK_PEND_ON_SEM,
timeout);
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_BLOCK(p_sem);
//调用OSSched引发一次调度
OSSched();
这部分代码是判断是否满足条件(Ctr > 0
),满足条件就对Ctr
做自减操作,如果不满足,根据入参选项是否阻塞做相应的处理。如果阻塞的话,把任务加入PendList
(如果Timeout
不为零也加入TickList
),并从ReadyList
中移除,然后调用OSSched
进行一次调度。
[os_sem.c OSSemPend 函数 片段3]
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus) {
//PEND_OK,即满足条件
case OS_STATUS_PEND_OK:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND(p_sem);
//错误码设置为OS_ERR_NONE
*p_err = OS_ERR_NONE;
break;
//PEND_ABORT,即为调用了Abort函数
case OS_STATUS_PEND_ABORT:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
//错误码设置为OS_ERR_PEND_ABORT
*p_err = OS_ERR_PEND_ABORT;
break;
//PEND_TIMEOUT,超时
case OS_STATUS_PEND_TIMEOUT:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
//错误码设置为OS_ERR_TIMEOUT
*p_err = OS_ERR_TIMEOUT;
break;
//PEND_TIMEOUT,删除了计数信号量
case OS_STATUS_PEND_DEL:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
//错误码设置为OS_ERR_OBJ_DEL
*p_err = OS_ERR_OBJ_DEL;
break;
//非预期值
default:
OS_TRACE_SEM_PEND_FAILED(p_sem);
//错误码设置为OS_ERR_STATUS_INVALID
*p_err = OS_ERR_STATUS_INVALID;
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(*p_err);
return (0u);
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(*p_err);
return (ctr);
}
当CPU
又重新执行此任务时,会有三种情况:条件满足、对计数信号量执行了Abort
操作、删除了计数信号量。分别对这三种情况设置不同的错误码。
4. 计数信号量的Abort
操作OSSemPendAbort
这个函数把因为这个计数信号量的Pend
操作而被放入PendList
中或者TickList
中的任务,从PendList
或者TickList
中移除,重新的被放入ReadyList
中(如果这个任务没有被suspended
的话)。OS_OPT_PEND_ABORT_1
值得是只对PendList
中优先级最高的任务有效;OS_OPT_PEND_ABORT_ALL
指的是对PendList
中所有的任务都有效。
OSSemPendAbort
的函数实现如下:
[os_sem.c OSSemPendAbort 函数]
OS_OBJ_QTY OSSemPendAbort (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
OS_OBJ_QTY nbr_tasks;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在中断处理函数中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ABORT_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem入参合法性判断
if (p_sem == (OS_SEM *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt入参合法性判断
switch (opt) {
case OS_OPT_PEND_ABORT_1:
case OS_OPT_PEND_ABORT_ALL:
case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_sem经过了create函数
if (p_sem->Type != OS_OBJ_TYPE_SEM) {
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->PendList;
//如果PendList为空,直接返回
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_ABORT_NONE;
return (0u);
}
nbr_tasks = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历PendList中的每一个任务
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//将每一个任务都从PendList中移除
//如果这个任务也处于TickList中,也将其移除
//如果这个任务没有suspended,把他放入ReadyList中
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_ABORT);
nbr_tasks++;
if (opt != OS_OPT_PEND_ABORT_ALL) {
break;
}
}
CPU_CRITICAL_EXIT();
//如果没有特殊声明OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//引发一次调度
OSSched();
}
*p_err = OS_ERR_NONE;
return (nbr_tasks);
}
这个函数不能在ISR
中被调用,也不能在内核运行前被调用。遍历PendList
,把其中每一个任务从PendList
中移除,如果这个任务也存在于TickList
中,也将其移除,如果这个任务没有被Suspended
,那么再将他插入就绪列表ReadyList
中。最后如果没有特殊声明OS_OPT_POST_NO_SCHED
,那么将调用OSSched
进行一次调度。
5. 计数信号量的Post
操作OSSemPost
OSSemPost
函数实现如下:
[os_sem.c OSSemPost 函数]
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
CPU_TS ts;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_SEM_POST_ENTER(p_sem, opt);
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_SEM_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem入参合法性判断
if (p_sem == (OS_SEM *)0) {
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt合法性判断
switch (opt) {
case OS_OPT_POST_1:
case OS_OPT_POST_ALL:
case OS_OPT_POST_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_sem经过了create函数
if (p_sem->Type != OS_OBJ_TYPE_SEM) {
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
OS_TRACE_SEM_POST(p_sem);
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->PendList;
//如果PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
//如果Ctr为-1
if (p_sem->Ctr == (OS_SEM_CTR)-1) {
CPU_CRITICAL_EXIT();
//设置错误码为OS_ERR_SEM_OVF
*p_err = OS_ERR_SEM_OVF;
OS_TRACE_SEM_POST_EXIT(*p_err);
return (0u);
}
//增加一个计数信号量
p_sem->Ctr++;
ctr = p_sem->Ctr;
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_sem->TS = ts;
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
OS_TRACE_SEM_POST_EXIT(*p_err);
return (ctr);
}
p_tcb = p_pend_list->HeadPtr;
//如果PendList不为空,遍历整个PendList
while (p_tcb != (OS_TCB *)0) {
p_tcb_next = p_tcb->PendNextPtr;
//对任务调用OS_Post,将其从PendList中移除
//如果他也处于TickList中,也将其移除
//如果没有被Suspended,将其加入ReadyList
OS_Post((OS_PEND_OBJ *)((void *)p_sem),
p_tcb,
(void *)0,
0u,
ts);
//如果没有设置OS_OPT_POST_ALL
//每一次Post操作只Post一个任务
if ((opt & OS_OPT_POST_ALL) == 0u) {
break;
}
p_tcb = p_tcb_next;
}
CPU_CRITICAL_EXIT();
//如果没有特殊声明OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//引发一次调度
OSSched();
}
*p_err = OS_ERR_NONE;
OS_TRACE_SEM_POST_EXIT(*p_err);
return (0u);
}
如果PendList
为空,则将Ctr
加1。如果PendList
不为空,再检查是否设置了OS_OPT_POST_ALL
,如果设置了,就代表一次Post
操作,就可以将所有PendList
中的任务全部放入到ReadyList
中(前提是这个任务没有被Suspended
);如果没有设置OS_OPT_POST_ALL
,那么每一次Post
操作只能将一个任务移出PendList
放入到ReadyList
。最后如果没有特别声明OS_OPT_POST_NO_SCHED
,就调用一次OSSched
进行一次调度。
6. 设置计数信号量的个数OSSemSet
OSSemSet
函数的实现如下:
[os_sem.c OSSemSet 函数]
void OSSemSet (OS_SEM *p_sem,
OS_SEM_CTR cnt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_SET_ISR;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_sem入参合法性检查
if (p_sem == (OS_SEM *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_sem已经经过create函数
if (p_sem->Type != OS_OBJ_TYPE_SEM) {
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
*p_err = OS_ERR_NONE;
CPU_CRITICAL_ENTER();
//如果Ctr > 0
if (p_sem->Ctr > 0u) {
//直接设置个数
p_sem->Ctr = cnt;
//如果Ctr <=0
} else {
p_pend_list = &p_sem->PendList;
//如果PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
//直接设置个数
p_sem->Ctr = cnt;
//如果PendList不为空
} else {
//出错,返回错误码OS_ERR_TASK_WAITING
*p_err = OS_ERR_TASK_WAITING;
}
}
CPU_CRITICAL_EXIT();
}
这个函数在不能在ISR
中调用,也不能在PendList
不为空时调用。一般用在给计数信号量设置一个初始值,或者重置信号量的值。不能把他当作Post
一样的操作来用。
四. 互斥信号量Mutex
1. 创建互斥信号量OSMutexCreate
OSMutexCreate
函数的实现如下:
[os_mutex.c OSMutexCreate 函数]
void OSMutexCreate (OS_MUTEX *p_mutex,
CPU_CHAR *p_name,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_mutex合法性检查
if (p_mutex == (OS_MUTEX *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
CPU_CRITICAL_ENTER();
#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
//初始化PendObj类型
p_mutex->Type = OS_OBJ_TYPE_MUTEX;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
p_mutex->NamePtr = p_name;
#else
(void)p_name;
#endif
p_mutex->MutexGrpNextPtr = (OS_MUTEX *)0;
p_mutex->OwnerTCBPtr = (OS_TCB *)0;
p_mutex->OwnerNestingCtr = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
p_mutex->TS = 0u;
#endif
//初始化PendList
OS_PendListInit(&p_mutex->PendList);
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_MutexDbgListAdd(p_mutex);
OSMutexQty++;
#endif
OS_TRACE_MUTEX_CREATE(p_mutex, p_name);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
这个函数不能在ISR
中调用,主要初始化OS_SEM
里的成员(名字,PendObj
类型等),并初始化PendList
。
2. 删除互斥信号量OSMutexDel
OSMutexDel
函数片段1:
[os_mutex.c OSMutexDel 函数片段1]
OS_OBJ_QTY OSMutexDel (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err)
{
OS_OBJ_QTY nbr_tasks;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
OS_TCB *p_tcb_owner;
CPU_TS ts;
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
OS_PRIO prio_new;
#endif
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_MUTEX_DEL_ENTER(p_mutex, opt);
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
OS_TRACE_MUTEX_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
*p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不准在ISR中调用
if (OSIntNestingCtr > 0u) {
OS_TRACE_MUTEX_DEL_EXIT(OS_ERR_DEL_ISR);
*p_err = OS_ERR_DEL_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_MUTEX_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_mutex入参合法性判断
if (p_mutex == (OS_MUTEX *)0) {
OS_TRACE_MUTEX_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_mutex已经经过create函数
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) {
OS_TRACE_MUTEX_DEL_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
函数片段1规定这个函数不能在ISR
中调用,不能在内核运行前调用,并做了一些入参的合法性判断。
[os_mutex.c OSMutexDel 函数片段2]
CPU_CRITICAL_ENTER();
p_pend_list = &p_mutex->PendList;
nbr_tasks = 0u;
switch (opt) {
//只有当PendList中没有任务的时候才删除
case OS_OPT_DEL_NO_PEND:
//如果PendList中没有任务
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_MutexDbgListRemove(p_mutex);
OSMutexQty--;
#endif
OS_TRACE_MUTEX_DEL(p_mutex);
//如果这个Mutex属于某个任务
if (p_mutex->OwnerTCBPtr != (OS_TCB *)0) {
//把这个Mutex从这个任务的MutexGroup中移除
OS_MutexGrpRemove(p_mutex->OwnerTCBPtr, p_mutex);
}
//调用OS_MutexClr,初始化Mutex内部的成员变量和PendList
OS_MutexClr(p_mutex);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
//如果PendList中有任务
} else {
CPU_CRITICAL_EXIT();
//设置错误码为OS_ERR_TASK_WAITING
*p_err = OS_ERR_TASK_WAITING;
}
break;
//不管PendList是否为空,都删除
case OS_OPT_DEL_ALWAYS:
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历PendList中的每一个任务
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//把这些任务从PendList或者TickList中移除
//如果这些任务没有suspended,加入ReadyList
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_DEL);
nbr_tasks++;
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_MutexDbgListRemove(p_mutex);
OSMutexQty--;
#endif
OS_TRACE_MUTEX_DEL(p_mutex);
//这个Mutex的所有者
p_tcb_owner = p_mutex->OwnerTCBPtr;
if (p_tcb_owner != (OS_TCB *)0) {
//把这个Mutex从其所有者的MutexGroup中移除
OS_MutexGrpRemove(p_tcb_owner, p_mutex);
}
//因为这个Mutex被删除之后,这个任务就相当于获取到了这个Mutex(不是阻塞状态了)
//那么就应该把这个任务的优先级设置为,
//在这个任务的MutexGrop中的所有Mutex的PendList中的所有任务的最高优先级
if (p_tcb_owner != (OS_TCB *)0) {
if (p_tcb_owner->Prio != p_tcb_owner->BasePrio) {
prio_new = OS_MutexGrpPrioFindHighest(p_tcb_owner);
prio_new = (prio_new > p_tcb_owner->BasePrio) ? p_tcb_owner->BasePrio : prio_new;
OS_TaskChangePrio(p_tcb_owner, prio_new);
OS_TRACE_MUTEX_TASK_PRIO_DISINHERIT(p_tcb_owner, p_tcb_owner->Prio);
}
}
//调用OS_MutexClr清除Mutex的成员变量,初始化PendList
OS_MutexClr(p_mutex);
CPU_CRITICAL_EXIT();
//调用OSSched,引发一次调度
OSSched();
*p_err = OS_ERR_NONE;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
break;
}
OS_TRACE_MUTEX_DEL_EXIT(*p_err);
return (nbr_tasks);
}
上面的代码片段2可以总结为:
OS_OPT_DEL_NO_PEND
意思只有PendList
中没有任务的话才删除。如果这个Mutex
被一个任务所拥有,先把这个Mutex
从其拥有着的MutexGroup
中移除。调用OS_MutexClr
清除Mutex
的成员变量,初始化PendList
。OS_OPT_DEL_ALWAYS
指的是不管PendList
中有没有任务都删除。先遍历PendList
中的每一个任务,调用OS_PendAbort
把这些任务都从PendList
中移除,如果他们处于TickList
中,再把他们从TickList
中移除。最后如果没有被suspended
的话,把他们放入ReadyList
中。如果这个Mutex
被一个人任务所拥有,先把这个Mutex
从其所有的任务的MutexGroup
中移除,再把这个任务的优先级设置到其MutexGroup
中的所有PendList
中的任务的最高优先级,来防止优先级反转的问题。然后调用OS_MutexClr
清除Mutex
的成员变量,初始化PendList
。最后,调用OSSched
引发一次调度。
3. 互斥信号量的Pend
操作OSMutexPend
OSMutexPend
的代码片段1:
[os_mutex.c OSMutexPend 函数片段1]
void OSMutexPend (OS_MUTEX *p_mutex,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_TCB *p_tcb;
CPU_SR_ALLOC();
#if (OS_CFG_TS_EN == DEF_DISABLED)
(void)p_ts;
#endif
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OS_TRACE_MUTEX_PEND_ENTER(p_mutex, timeout, opt, p_ts);
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_PEND_ISR);
*p_err = OS_ERR_PEND_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核运行前不能调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_mutex入参合法性判断
if (p_mutex == (OS_MUTEX *)0) {
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
//opt入参合法性判断
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return;
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_mutex经过了create函数
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) {
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
代码片段1做了规定这个函数不能在ISR
中调用,不能在内核运行前调用,并做了一些入参合法性的判断。
[os_mutex.c OSMutexPend 函数片段2]
CPU_CRITICAL_ENTER();
//如果这个mutex不属于任何任务
if (p_mutex->OwnerNestingCtr == 0u) {
//设置这个mutex属于当前任务
p_mutex->OwnerTCBPtr = OSTCBCurPtr;
//设置OwnerNestingCtr为1
p_mutex->OwnerNestingCtr = 1u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_mutex->TS;
}
#endif
//把这个mutex插入到当前任务的MutexGroup列表的最前面
OS_MutexGrpAdd(OSTCBCurPtr, p_mutex);
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_PEND(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_NONE);
//获取锁,设置错误码为OS_ERR_NONE
*p_err = OS_ERR_NONE;
//返回
return;
}
//如果这个mutex属于当前任务
if (OSTCBCurPtr == p_mutex->OwnerTCBPtr) {
//如果OwnerNestingCtr为-1说明没有Pend就做了Post操作
if (p_mutex->OwnerNestingCtr == (OS_NESTING_CTR)-1) {
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_MUTEX_OVF);
//错误码设为OS_ERR_MUTEX_OVF
*p_err = OS_ERR_MUTEX_OVF;
return;
}
//当前任务对这个Mutex的Pend嵌套操作,把OwnerNestingCtr++
p_mutex->OwnerNestingCtr++;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_mutex->TS;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_MUTEX_OWNER);
//设置错误码为OS_ERR_MUTEX_OWNER
*p_err = OS_ERR_MUTEX_OWNER;
//返回出去
return;
}
//这里以下是没有拿到Mutex
//如果是非阻塞
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
//设置错误码为OS_ERR_PEND_WOULD_BLOCK
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//返回出去
return;
//如果是阻塞
} else {
//如果锁住了调度器
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
OS_TRACE_MUTEX_PEND_EXIT(OS_ERR_SCHED_LOCKED);
//错误码设置为OS_ERR_SCHED_LOCKED
*p_err = OS_ERR_SCHED_LOCKED;
//返回
return;
}
}
//下面是没有拿到Mutex,而且是阻塞模式,调度器没上锁
p_tcb = p_mutex->OwnerTCBPtr;
//解决优先级反转的问题
//如果等待Mutex的任务的优先级比拥有Mutex任务的优先级高
if (p_tcb->Prio > OSTCBCurPtr->Prio) {
//那么把拥有Mutex的任务的优先级提升至和等待Mutex的任务的优先级一样
OS_TaskChangePrio(p_tcb, OSTCBCurPtr->Prio);
OS_TRACE_MUTEX_TASK_PRIO_INHERIT(p_tcb, p_tcb->Prio);
}
//没有拿到Mutex,阻塞模式,调度器没上锁
//调用OS_Pend,把当前任务从ReadyList中移除,
//加入到PendList中,如果TimeOut不为0,再加入到TickList中
OS_Pend((OS_PEND_OBJ *)((void *)p_mutex),
OS_TASK_PEND_ON_MUTEX,
timeout);
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_PEND_BLOCK(p_mutex);
//引发一次调度
OSSched();
上面的代码片段2可以总结为如下:
- 任务拿到了
Mutex
。把Mutex
的拥有着设置为这个任务,这个Mutex
的OwnerNestingCtr
设置为1,并把这个Mutex
插入到这个任务的MutexGroup
链表的头部,然后直接返回出去。 - 如果任务没有拿到
Mutex
。如果是非阻塞状态,设置好错误码之后就返回出去,如果是阻塞状态,看调度器是否是锁住的,如果是锁着的,设置好错误码返回。如果没锁住,解决优先级反转的问题,然后调用OS_Pend
把当前任务从ReadyList
中移除,加入到PendList
中,如果TimeOut不为0,再加入到TickList
中。 - 最后调用
OSSched
引发一次调度。
等到函数继续向下进行时,说明这个任务已经拿到了这个Mutex
,或者因为其他原因这个任务被放入到了就绪列表中:
[os_mutex.c OSMutexPend 函数片段3]
CPU_CRITICAL_ENTER();
//判断任务又重新运行的原因
switch (OSTCBCurPtr->PendStatus) {
//获取到了Mutex
case OS_STATUS_PEND_OK:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_MUTEX_PEND(p_mutex);
*p_err = OS_ERR_NONE;
break;
//因为Abort操作
case OS_STATUS_PEND_ABORT:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
*p_err = OS_ERR_PEND_ABORT;
break;
//因为超时
case OS_STATUS_PEND_TIMEOUT:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
*p_err = OS_ERR_TIMEOUT;
break;
//因为Mutex被删除
case OS_STATUS_PEND_DEL:
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
*p_err = OS_ERR_OBJ_DEL;
break;
default:
OS_TRACE_MUTEX_PEND_FAILED(p_mutex);
*p_err = OS_ERR_STATUS_INVALID;
break;
}
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_PEND_EXIT(*p_err);
}
根据不同的原因设置好不同的错误码,返回出去。
4. 互斥信号量的PendAbort
操作OSMutexPendAbort
这个函数把因为这个互斥信号量的Pend
操作而被放入PendList
中或者TickList
中的任务,从PendList
或者TickList
中移除,重新的被放入ReadyList
中(如果这个任务没有被suspended
的话)。OS_OPT_PEND_ABORT_1
值得是只对PendList
中优先级最高的任务有效;OS_OPT_PEND_ABORT_ALL
指的是对PendList
中所有的任务都有效。
OSMutexPendAbort
函数实现如下:
OS_OBJ_QTY OSMutexPendAbort (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
OS_TCB *p_tcb_owner;
CPU_TS ts;
OS_OBJ_QTY nbr_tasks;
OS_PRIO prio_new;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_OBJ_QTY)0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ABORT_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_mutex入参的合法性判断
if (p_mutex == (OS_MUTEX *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt的入参合法性判断
switch (opt) {
case OS_OPT_PEND_ABORT_1:
case OS_OPT_PEND_ABORT_ALL:
case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_mutex经过了Create函数
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) {
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_mutex->PendList;
//如果其PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
//设置好错误码,直接返回
*p_err = OS_ERR_PEND_ABORT_NONE;
return (0u);
}
nbr_tasks = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历整个PendList
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//对每一个任务都调用OS_PendAbort
//将这个任务从其
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_ABORT);
p_tcb_owner = p_mutex->OwnerTCBPtr;
prio_new = p_tcb_owner->Prio;
//如果这个Mutex的所属者的优先级被提升过,
//而且和这个任务的优先级相同(这个任务的优先级在这个PendList中最高)
//又因为这个任务被从这个PendList中移除了,
//所以得从其拥有着的MutexGroup中所有的PendList中另寻最高优先级
if ((p_tcb_owner->Prio != p_tcb_owner->BasePrio) &&
(p_tcb_owner->Prio == p_tcb->Prio)) {
//另寻最高优先级
prio_new = OS_MutexGrpPrioFindHighest(p_tcb_owner);
prio_new = (prio_new > p_tcb_owner->BasePrio) ? p_tcb_owner->BasePrio : prio_new;
}
//如果另寻的最高优先级不跟其拥有着的优先级相同
if(prio_new != p_tcb_owner->Prio) {
//更改其拥有者的优先级
OS_TaskChangePrio(p_tcb_owner, prio_new);
OS_TRACE_MUTEX_TASK_PRIO_DISINHERIT(p_tcb_owner, p_tcb_owner->Prio);
}
nbr_tasks++;
if (opt != OS_OPT_PEND_ABORT_ALL) {
break;
}
}
CPU_CRITICAL_EXIT();
//如果没有特殊设置OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//引发一次调度
OSSched();
}
*p_err = OS_ERR_NONE;
return (nbr_tasks);
}
5. 互斥信号量的Post
操作OSMutexPost
OSMutexPost
函数代码片段1:
[os_mutex.c OSMutexPost 函数代码片段1 ]
void OSMutexPost (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
OS_PRIO prio_new;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OS_TRACE_MUTEX_POST_ENTER(p_mutex, opt);
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
OS_TRACE_MUTEX_POST_FAILED(p_mutex);
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_POST_ISR);
*p_err = OS_ERR_POST_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_mutex入参的合法性判断
if (p_mutex == (OS_MUTEX *)0) {
OS_TRACE_MUTEX_POST_FAILED(p_mutex);
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
//opt入参的合法性判断
switch (opt) {
case OS_OPT_POST_NONE:
case OS_OPT_POST_NO_SCHED:
break;
default:
OS_TRACE_MUTEX_POST_FAILED(p_mutex);
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return;
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_mutex已经经过了create函数
if (p_mutex->Type != OS_OBJ_TYPE_MUTEX) {
OS_TRACE_MUTEX_POST_FAILED(p_mutex);
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
CPU_CRITICAL_ENTER();
函数代码片段1规定这个函数不能在ISR
中使用,不能在内核运行前使用。并做了入参的合法性判断。
[os_mutex.c OSMutexPost 函数代码片段2 ]
//如果Post操作的任务不是Mutex的拥有者,肯定是错误的
if (OSTCBCurPtr != p_mutex->OwnerTCBPtr) {
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_POST_FAILED(p_mutex);
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_MUTEX_NOT_OWNER);
//设置错误码为OS_ERR_MUTEX_NOT_OWNER
*p_err = OS_ERR_MUTEX_NOT_OWNER;
//返回
return;
}
OS_TRACE_MUTEX_POST(p_mutex);
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
p_mutex->TS = ts;
#else
ts = 0u;
#endif
//把OwnerNestingCtr做自减操作
p_mutex->OwnerNestingCtr--;
//如果OwnerNestingCtr还大于0,说明Mutex出与嵌套操作中
if (p_mutex->OwnerNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_MUTEX_NESTING);
//设置错误码为OS_ERR_MUTEX_NESTING
*p_err = OS_ERR_MUTEX_NESTING;
//返回
return;
}
//把这个Mutex从当前任务的MutexGroup中移除
OS_MutexGrpRemove(OSTCBCurPtr, p_mutex);
p_pend_list = &p_mutex->PendList;
//如果PendList中没任务
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
//嵌套次数和拥有者的值都清0,代表没有嵌套次数,没有拥有者
p_mutex->OwnerTCBPtr = (OS_TCB *)0;
p_mutex->OwnerNestingCtr = 0u;
CPU_CRITICAL_EXIT();
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_NONE);
//设置错误码并返回
*p_err = OS_ERR_NONE;
return;
}
//下面是如果PendList中有任务
//如果当前任务的优先级被提升过
//而且,当前任务的MutexGroup变动过,
//所以得从其新的MutexGroup中需要寻找最新的最高优先级
if (OSTCBCurPtr->Prio != OSTCBCurPtr->BasePrio) {
//新的优先级
prio_new = OS_MutexGrpPrioFindHighest(OSTCBCurPtr);
//新的优先级和基础优先级,取优先级最高的
//注意,Prio的值越高,优先级越低
prio_new = (prio_new > OSTCBCurPtr->BasePrio) ? OSTCBCurPtr->BasePrio : prio_new;
//如果新算出来的优先级小于原来的优先级
if (prio_new > OSTCBCurPtr->Prio) {
//先把这个任务从ReadyList中移出来
OS_RdyListRemove(OSTCBCurPtr);
//再更改优先级
OSTCBCurPtr->Prio = prio_new;
OS_TRACE_MUTEX_TASK_PRIO_DISINHERIT(OSTCBCurPtr, prio_new);
//更改后再插入到ReadyList中,插入到新优先级任务列表的尾部
OS_PrioInsert(prio_new);
OS_RdyListInsertTail(OSTCBCurPtr);
OSPrioCur = prio_new;
}
}
//寻找Mutex中PendList的下一个任务,把他当作Mutex的拥有者
p_tcb = p_pend_list->HeadPtr;
p_mutex->OwnerTCBPtr = p_tcb;
p_mutex->OwnerNestingCtr = 1u;
//把Mutex加入到拥有者的PendList中
OS_MutexGrpAdd(p_tcb, p_mutex);
//调用OS_Post,把其拥有者从PendList(TickList)中移除
//再把他加入ReadyList
OS_Post((OS_PEND_OBJ *)((void *)p_mutex),
p_tcb,
(void *)0,
0u,
ts);
CPU_CRITICAL_EXIT();
//如果没有特别声明OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//引发一次调度
OSSched();
}
OS_TRACE_MUTEX_POST_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
}
这段代码解释了OSMutexPost
真正做的事情,一个是因为优先级反转问题的处理,一个是释放这个Mutex
,为其找到新的拥有者。并把拥有者从PendList
(也可能有TickList
)中移除,如果没有被suspended
的话放入ReadyList
中,这里需要注意,如果一个任务拿到了一个Mutex
,但是他却被suspended
了,那么其他等待这个Mutex
的任务将都得不到运行。
五. 消息队列OS_Q
μC/OS III
中有一个消息池OSMsgPool
,用来暂存消息。他是一个单向不循环列表。
1. 创建消息队列OSQCreate
OSQCreate
函数实现如下:
[os_q.c OSQCreate 函数]
void OSQCreate (OS_Q *p_q,
CPU_CHAR *p_name,
OS_MSG_QTY max_qty,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q入参合法性判断
if (p_q == (OS_Q *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
if (max_qty == 0u) {
*p_err = OS_ERR_Q_SIZE;
return;
}
#endif
CPU_CRITICAL_ENTER();
#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
p_q->Type = OS_OBJ_TYPE_Q;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
p_q->NamePtr = p_name;
#else
(void)p_name;
#endif
//初始化消息队列OS_MSG_Q
OS_MsgQInit(&p_q->MsgQ,
max_qty);
//初始化消息队列中的PendList
OS_PendListInit(&p_q->PendList);
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_QDbgListAdd(p_q);
OSQQty++;
#endif
OS_TRACE_Q_CREATE(p_q, p_name);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
这个函数不能在ISR
中调用,将Type
设置为OS_OBJ_TYPE_Q
,初始化消息队列和PendList
。
2. 删除消息队列OSQDel
OSQDel
函数实现如下:
[os_q.c OSQDel 函数]
OS_OBJ_QTY OSQDel (OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err)
{
OS_OBJ_QTY nbr_tasks;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_Q_DEL_ENTER(p_q, opt);
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
OS_TRACE_Q_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
*p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在`ISR`中使用
if (OSIntNestingCtr > 0u) {
OS_TRACE_Q_DEL_EXIT(OS_ERR_DEL_ISR);
*p_err = OS_ERR_DEL_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前使用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_Q_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q入参合法性判断
if (p_q == (OS_Q *)0) {
OS_TRACE_Q_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_q已经经过create函数
if (p_q->Type != OS_OBJ_TYPE_Q) {
OS_TRACE_Q_DEL_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_q->PendList;
nbr_tasks = 0u;
switch (opt) {
//只有当PendList为空时才删除
case OS_OPT_DEL_NO_PEND:
//如果PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_QDbgListRemove(p_q);
OSQQty--;
#endif
OS_TRACE_Q_DEL(p_q);
//调用OS_QClr初始化OS_MSG_Q和PendList
OS_QClr(p_q);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
//如果PendList不为空
} else {
CPU_CRITICAL_EXIT();
//设置错误码为OS_ERR_TASK_WAITING
*p_err = OS_ERR_TASK_WAITING;
}
break;
//不管PendList是否为空都删除
case OS_OPT_DEL_ALWAYS:
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//遍历整个PendList
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//将每一个任务都调用OS_PendAbort
//将任务从PendList中移除
//如果任务同时还处于TickList,也将其移除
//如果没有被suspended,将其让如ReadyList
//表明任务被移到ReadyList是因为OS_STATUS_PEND_DEL
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_DEL);
nbr_tasks++;
}
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_QDbgListRemove(p_q);
OSQQty--;
#endif
OS_TRACE_Q_DEL(p_q);
//初始化OS_MSG_Q和PendList
OS_QClr(p_q);
CPU_CRITICAL_EXIT();
//引发一次调度
OSSched();
*p_err = OS_ERR_NONE;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
break;
}
OS_TRACE_Q_DEL_EXIT(*p_err);
return (nbr_tasks);
}
OS_OPT_DEL_NO_PEND
指的是只有PendList
为空时才删除。OS_OPT_DEL_ALWAYS
指的是不管PendList
是否为空都删除。当PendList
中有任务时,将所有任务都从PendList
中移除,如果这个任务同时还处于TickList
中,也将其移除,最后如果这个任务没有被suspended
,将其放入到ReadyList
中。然后调用OS_QClr
初始化OS_MSG_Q
和PendList
。最后调用OSSched
进行一次调度。
3. 冲掉消息队列里的所有消息OSQFlush
OSQFlush
函数的实现如下:
[os_q.c OSQFlush 函数]
OS_MSG_QTY OSQFlush (OS_Q *p_q, OS_ERR *p_err)
{
OS_MSG_QTY entries;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参非法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用这个函数
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_FLUSH_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用这个函数
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q的入参合法性判断
if (p_q == (OS_Q *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_q已经经过create函数
if (p_q->Type != OS_OBJ_TYPE_Q) {
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
//释放消息队列中的所有的消息
entries = OS_MsgQFreeAll(&p_q->MsgQ);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return (entries);
}
这个函数不能在内核运行前调用,不能再ISR
中调用。最后调用OS_MsgQFreeAll
:
[os_msg.c OS_MsgQFreeAll 函数]
OS_MSG_QTY OS_MsgQFreeAll (OS_MSG_Q *p_msg_q)
{
OS_MSG *p_msg;
OS_MSG_QTY qty;
qty = p_msg_q->NbrEntries;
if (p_msg_q->NbrEntries > 0u) {
p_msg = p_msg_q->InPtr;
p_msg->NextPtr = OSMsgPool.NextPtr;
OSMsgPool.NextPtr = p_msg_q->OutPtr;
OSMsgPool.NbrUsed -= p_msg_q->NbrEntries;
OSMsgPool.NbrFree += p_msg_q->NbrEntries;
p_msg_q->NbrEntries = 0u;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
p_msg_q->NbrEntriesMax = 0u;
#endif
p_msg_q->InPtr = (OS_MSG *)0;
p_msg_q->OutPtr = (OS_MSG *)0;
}
return (qty);
}
4. 消息队列的Pend
操作OSQPend
OSQPend
函数片段1:
[os_q.c OSQPend 函数片段1]
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
{
void *p_void;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参的合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((void *)0);
}
#endif
OS_TRACE_Q_PEND_ENTER(p_q, timeout, opt, p_msg_size, p_ts);
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在`ISR`中调用
if (OSIntNestingCtr > 0u) {
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_PEND_ISR);
*p_err = OS_ERR_PEND_ISR;
return ((void *)0);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核运行前不能调用
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_Q_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return ((void *)0);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q的入参合法性判断
if (p_q == (OS_Q *)0) {
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((void *)0);
}
//p_msg_size,消息长度的入参合法性判断
if (p_msg_size == (OS_MSG_SIZE *)0) {
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_PTR_INVALID);
*p_err = OS_ERR_PTR_INVALID;
return ((void *)0);
}
//opt的入参合法性判断
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return ((void *)0);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_q已经经过了create函数
if (p_q->Type != OS_OBJ_TYPE_Q) {
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return ((void *)0);
}
#endif
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
片段1主要是入参的合法性判断,并规定不能在ISR
中调用,不能在内核运行前调用。
[os_q.c OSQPend 函数片段2]
CPU_CRITICAL_ENTER();
//从消息队列里获取一个消息
p_void = OS_MsgQGet(&p_q->MsgQ,
p_msg_size,
p_ts,
p_err);
//如果获取到了消息
if (*p_err == OS_ERR_NONE) {
OS_TRACE_Q_PEND(p_q);
CPU_CRITICAL_EXIT();
OS_TRACE_Q_PEND_EXIT(OS_ERR_NONE);
//直接返回
return (p_void);
}
//下面是没有获取到消息
//如果是非阻塞模式
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
//设置相应错误码
*p_err = OS_ERR_PEND_WOULD_BLOCK;
//返回出去
return ((void *)0);
//如果是阻塞模式
} else {
//如果调度器被锁住了
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
OS_TRACE_Q_PEND_FAILED(p_q);
OS_TRACE_Q_PEND_EXIT(OS_ERR_SCHED_LOCKED);
//设置好错误码
*p_err = OS_ERR_SCHED_LOCKED;
//直接返回
return ((void *)0);
}
}
//没有获取消息,阻塞模式,调度器没上锁
//调用OS_Pend把当前任务从ReadyList移动到PendList。
//如果TimeOut不为0,同时插入到TickList中
OS_Pend((OS_PEND_OBJ *)((void *)p_q),
OS_TASK_PEND_ON_Q,
timeout);
CPU_CRITICAL_EXIT();
OS_TRACE_Q_PEND_BLOCK(p_q);
//调用OSSched,引发一次调度
OSSched();
片段2是先尝试从消息队列中获取消息,如果获取到了消息,直接返回出去;如果没有获取到消息,再判断是阻塞模式还是非阻塞模式,如果是非阻塞模式,设置好错误码直接返回。如果是阻塞模式并且调度器没上锁的话,就把这个任务从ReadyList
中移除,加入到这个消息队列的PendList
中。如果TimeOut
不为0,再加入到TickList
中。最后调用OSSched
引发一次调度。
下面的片段3,是这个任务从新又被放入到ReadyList
之后才能运行到的:
[os_q.c OSQPend 函数片段3]
CPU_CRITICAL_ENTER();
//重新被放入ReadyList的原因
switch (OSTCBCurPtr->PendStatus) {
//得到了消息
case OS_STATUS_PEND_OK:
p_void = OSTCBCurPtr->MsgPtr;
*p_msg_size = OSTCBCurPtr->MsgSize;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_Q_PEND(p_q);
//设置错误码为OS_ERR_NONE
*p_err = OS_ERR_NONE;
break;
//因为PendAbort的操作
case OS_STATUS_PEND_ABORT:
p_void = (void *)0;
*p_msg_size = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_Q_PEND_FAILED(p_q);
//设置错误码为OS_ERR_PEND_ABORT
*p_err = OS_ERR_PEND_ABORT;
break;
//因为超时
case OS_STATUS_PEND_TIMEOUT:
p_void = (void *)0;
*p_msg_size = 0u;
OS_TRACE_Q_PEND_FAILED(p_q);
//设置错误码为OS_ERR_TIMEOUT
*p_err = OS_ERR_TIMEOUT;
break;
//因为删除了消息队列
case OS_STATUS_PEND_DEL:
p_void = (void *)0;
*p_msg_size = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_Q_PEND_FAILED(p_q);
*p_err = OS_ERR_OBJ_DEL;
break;
//异常原因
default:
p_void = (void *)0;
*p_msg_size = 0u;
OS_TRACE_Q_PEND_FAILED(p_q);
*p_err = OS_ERR_STATUS_INVALID;
break;
}
CPU_CRITICAL_EXIT();
OS_TRACE_Q_PEND_EXIT(*p_err);
return (p_void);
}
最后任务又被放回到ReadyList
之后,根据不同的原因选择不同的错误码。
5. 消息队列的PendAbort
操作OSQPendAbort
OSQPendAbort
函数的实现如下:
[os_q.c OSQPendAbort 函数]
OS_OBJ_QTY OSQPendAbort (OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_TS ts;
OS_OBJ_QTY nbr_tasks;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//函数不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_PEND_ABORT_ISR;
return (0u);
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//函数不能再内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q入参合法性判断
if (p_q == (OS_Q *)0) {
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
//opt合法性判断
switch (opt) {
case OS_OPT_PEND_ABORT_1:
case OS_OPT_PEND_ABORT_ALL:
case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_q已经经过create函数
if (p_q->Type != OS_OBJ_TYPE_Q) {
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER();
p_pend_list = &p_q->PendList;
//如果PendList为空
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
//设置错误码为OS_ERR_PEND_ABORT_NONE
*p_err = OS_ERR_PEND_ABORT_NONE;
//返回
return (0u);
}
nbr_tasks = 0u;
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
//如果PendList不为空,遍历整个PendList
while (p_pend_list->HeadPtr != (OS_TCB *)0) {
p_tcb = p_pend_list->HeadPtr;
//调用OS_PendAbort将每个任务从PendList和TickList中移除
//如果没有被suspended,那么再将他们放入到ReadyList中
OS_PendAbort(p_tcb,
ts,
OS_STATUS_PEND_ABORT);
nbr_tasks++;
if (opt != OS_OPT_PEND_ABORT_ALL) {
break;
}
}
CPU_CRITICAL_EXIT();
//如果没有特殊声明OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//调用OSSched
OSSched();
}
*p_err = OS_ERR_NONE;
return (nbr_tasks);
}
不能在ISR
中调用,不能在内核运行前调用。将PendList
中的所有任务从PendList
中移除,并从其TickList
中移除,如果没有被suspended
再把他们放入到ReadyList
中。如果没有特殊声明OS_OPT_POST_NO_SCHED
,调用OSSched
引发一次调度。
6. 消息队列的Post
操作OSQPost
[os_q.c OSQPost 函数]
void OSQPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
OS_ERR *p_err)
{
OS_OPT post_type;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
CPU_TS ts;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OS_TRACE_Q_POST_ENTER(p_q, p_void, msg_size, opt);
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用这个函数
if (OSRunning != OS_STATE_OS_RUNNING) {
OS_TRACE_Q_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_q入参合法性判断
if (p_q == (OS_Q *)0) {
OS_TRACE_Q_POST_FAILED(p_q);
OS_TRACE_Q_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
//opt入参合法性判断
switch (opt) {
case OS_OPT_POST_FIFO:
case OS_OPT_POST_LIFO:
case OS_OPT_POST_FIFO | OS_OPT_POST_ALL:
case OS_OPT_POST_LIFO | OS_OPT_POST_ALL:
case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED:
case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED:
case OS_OPT_POST_FIFO | (OS_OPT)(OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED):
case OS_OPT_POST_LIFO | (OS_OPT)(OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED):
break;
default:
OS_TRACE_Q_POST_FAILED(p_q);
OS_TRACE_Q_POST_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return;
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN == DEF_ENABLED)
//确保p_q已经经过了create函数
if (p_q->Type != OS_OBJ_TYPE_Q) {
OS_TRACE_Q_POST_FAILED(p_q);
OS_TRACE_Q_POST_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
#if (OS_CFG_TS_EN == DEF_ENABLED)
ts = OS_TS_GET();
#else
ts = 0u;
#endif
OS_TRACE_Q_POST(p_q);
CPU_CRITICAL_ENTER();
p_pend_list = &p_q->PendList;
//如果PendList中没有任务
if (p_pend_list->HeadPtr == (OS_TCB *)0) {
//放入消息的方向
if ((opt & OS_OPT_POST_LIFO) == 0u) {
post_type = OS_OPT_POST_FIFO;
} else {
post_type = OS_OPT_POST_LIFO;
}
//把消息插入到消息队列中,
//占用OSMsgPool消息池一个位置
OS_MsgQPut(&p_q->MsgQ,
p_void,
msg_size,
post_type,
ts,
p_err);
CPU_CRITICAL_EXIT();
OS_TRACE_Q_POST_EXIT(*p_err);
//返回出去
return;
}
//下面是如果PendList不为空,
//那么说明消息队列里没有消息
p_tcb = p_pend_list->HeadPtr;
while (p_tcb != (OS_TCB *)0) {
p_tcb_next = p_tcb->PendNextPtr;
//把这个消息给每一个人任务
//并且把这个任务从PendList或者TickList移除
//如果这个任务没有suspended,那么这个任务就被放入到ReadyList中
OS_Post((OS_PEND_OBJ *)((void *)p_q),
p_tcb,
p_void,
msg_size,
ts);
//如果声明了OS_OPT_POST_ALL,那么就是把告诉PendList中的所有任务
if ((opt & OS_OPT_POST_ALL) == 0u) {
break;
}
p_tcb = p_tcb_next;
}
CPU_CRITICAL_EXIT();
//如果没有特殊声明OS_OPT_POST_NO_SCHED
if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
//引发一次调度
OSSched();
}
*p_err = OS_ERR_NONE;
OS_TRACE_Q_POST_EXIT(*p_err);
}
如果PendList
中没有任务,那么就把消息放入到全局的消息池OSMsgPool
中,并插入到消息队列里,根据插入的防线过的不同:OS_OPT_POST_FIFO
是插入到队列的头部(先进队列的先出去);OS_OPT_POST_LIFO
是插入到队列的尾部(后进队列的先出去)。
如果PendList
中有任务,说明消息队列中没有消息,那么就直接把这个消息发送给PendList
中的任务。如果是声明了OS_OPT_POST_ALL
,那么就告诉PendList
中的所有任务,反之就只告诉PendList
中的第一任务。这些得到消息的任务,会被移除PendList
(TickList
)。如果没有被suspended
,会被加入到ReadyList
中。最后调用一次OSSched
进行一次调度(如果没有特殊声明OS_OPT_POST_NO_SCHED
的话)。
六. 任务相关的API
1. 创建任务OSTaskCreate
OSTaskCreate
是创建一个任务,具体的内容在μC/OS III-任务调度Ⅰ:调度过程和调度点中有详细介绍。
2. 删除任务OSTaskDel
OSTaskDel
是删除一个任务这个函数的定义为:
[os_task.c OSTaskDel() 片段 1 ]
void OSTaskDel (OS_TCB *p_tcb, OS_ERR *p_err)
{
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
OS_TCB *p_tcb_owner;
OS_PRIO prio_new;
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
//p_err入参合法性检查
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
return;
}
#endif
CPU_SR_ALLOC();
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TASK_DEL_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在系统运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
#if (OS_CFG_TASK_IDLE_EN == DEF_ENABLED)
//不能删除空闲任务
if (p_tcb == &OSIdleTaskTCB) {
*p_err = OS_ERR_TASK_DEL_IDLE;
return;
}
#endif
上面的片段代码说明:不能在中断处理函数中删除一个任务;不能在μC/OS III
没有处于运行状态的时候删除一个任务;不能删除空闲任务IDLE TASK
;
[os_task.c OSTaskDel() 片段 2 ]
if (p_tcb == (OS_TCB *)0) { /* Delete 'Self'? */
CPU_CRITICAL_ENTER();
p_tcb = OSTCBCurPtr; /* Yes. */
CPU_CRITICAL_EXIT();
}
如果传入的p_tcb
为0的话,就是删除本身。
[os_task.c OSTaskDel() 片段 3 ]
switch (p_tcb->TaskState) {
//如果任务初始Ready状态
case OS_TASK_STATE_RDY:
//从ReadyList中删除
OS_RdyListRemove(p_tcb);
break;
case OS_TASK_STATE_SUSPENDED:
break;
//如果有Dly,那么从TickList中删除
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_DLY_SUSPENDED:
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
OS_TickListRemove(p_tcb);
#endif
break;
//如果有Pend
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
switch (p_tcb->PendOn) {
case OS_TASK_PEND_ON_NOTHING:
case OS_TASK_PEND_ON_TASK_Q:
case OS_TASK_PEND_ON_TASK_SEM:
break;
//如果Pend操作不是任务自身的Pend
//有内核对象的Pend
case OS_TASK_PEND_ON_FLAG:
case OS_TASK_PEND_ON_Q:
case OS_TASK_PEND_ON_SEM:
//如果PendList中移除
OS_PendListRemove(p_tcb);
break;
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
//Mutex特殊处理
case OS_TASK_PEND_ON_MUTEX:
p_tcb_owner = ((OS_MUTEX *)((void *)p_tcb->PendObjPtr))->OwnerTCBPtr;
prio_new = p_tcb_owner->Prio;
//如果PendList中移除
OS_PendListRemove(p_tcb);
//解决MutexGroup优先级的问题
if ((p_tcb_owner->Prio != p_tcb_owner->BasePrio) &&
(p_tcb_owner->Prio == p_tcb->Prio)) {
prio_new = OS_MutexGrpPrioFindHighest(p_tcb_owner);
prio_new = (prio_new > p_tcb_owner->BasePrio) ? p_tcb_owner->BasePrio : prio_new;
}
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
if (prio_new != p_tcb_owner->Prio) {
OS_TaskChangePrio(p_tcb_owner, prio_new);
OS_TRACE_MUTEX_TASK_PRIO_DISINHERIT(p_tcb_owner, p_tcb_owner->Prio);
}
break;
#endif
default:
break;
}
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
if ((p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) ||
(p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED)) {
//如果有TimeOut,那么从TickList中移除
OS_TickListRemove(p_tcb);
}
#endif
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
return;
}
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
//因为整个任务被删除了,她所占用的Mutex全被释放
//所有要MutexGroup里的所有Mutex做Post操作
if(p_tcb->MutexGrpHeadPtr != (OS_MUTEX *)0) {
OS_MutexGrpPostAll(p_tcb);
}
#endif
#if (OS_CFG_TASK_Q_EN == DEF_ENABLED)
//释放这个任务相关的MsgQ
(void)OS_MsgQFreeAll(&p_tcb->MsgQ);
#endif
//调用OSTaskDelHook钩子函数
OSTaskDelHook(p_tcb);
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_TaskDel(p_tcb);
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
OS_TaskDbgListRemove(p_tcb);
#endif
OSTaskQty--;
OS_TRACE_TASK_DEL(p_tcb);
#if (OS_CFG_TASK_STK_REDZONE_EN != DEF_ENABLED)
OS_TaskInitTCB(p_tcb);
#endif
p_tcb->TaskState = (OS_STATE)OS_TASK_STATE_DEL;
*p_err = OS_ERR_NONE;
CPU_CRITICAL_EXIT();
//最后引发一次调度
OSSched();
}
判断通过判断p_tcb->TaskState
的值,来判断当前任务处于什么状态中,便可以知道这个任务处于哪个任务列表中,将其从相应的任务列表中删除。并且在Mutex
中需要注意优先级反转的问题。以及对这个任务中的MutexGroup
所有的Mutex
做一次Post
,释放这个函数相关的消息队列MsgQ
中的所有消息。然后调用任务函数的钩子函数OSTaskDelHook
,最后调用OSSched
引发一次调度,从就绪列表中找到优先级最高的任务开始执行。
3. 更改任务优先级OSTaskChangePrio
[os_task.c OSTaskChangePrio函数]
void OSTaskChangePrio (OS_TCB *p_tcb,
OS_PRIO prio_new,
OS_ERR *p_err)
{
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
OS_PRIO prio_high;
#endif
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_tcb入参合法性判断,不能对已经删除的任务做此操作
if ((p_tcb != (OS_TCB *)0) && (p_tcb->TaskState == OS_TASK_STATE_DEL)) {
*p_err = OS_ERR_STATE_INVALID;
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TASK_CHANGE_PRIO_ISR;
return;
}
#endif
//不能更改成空闲任务的优先级
if (prio_new >= (OS_CFG_PRIO_MAX - 1u)) {
*p_err = OS_ERR_PRIO_INVALID;
return;
}
CPU_CRITICAL_ENTER();
//如果p_tcb为0,那么就是更改自己的优先级
if (p_tcb == (OS_TCB *)0) {
if (OSRunning != OS_STATE_OS_RUNNING) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
p_tcb = OSTCBCurPtr;
}
#if (OS_CFG_MUTEX_EN == DEF_ENABLED)
p_tcb->BasePrio = prio_new;
//MutexGroup的优先级问题
if (p_tcb->MutexGrpHeadPtr != (OS_MUTEX *)0) {
if (prio_new > p_tcb->Prio) {
prio_high = OS_MutexGrpPrioFindHighest(p_tcb);
if (prio_new > prio_high) {
prio_new = prio_high;
}
}
}
#endif
//更改优先级
OS_TaskChangePrio(p_tcb, prio_new);
OS_TRACE_TASK_PRIO_CHANGE(p_tcb, prio_new);
CPU_CRITICAL_EXIT();
//如果内核已经运行了起来
if (OSRunning == OS_STATE_OS_RUNNING) {
//引发一次调度
OSSched();
}
*p_err = OS_ERR_NONE;
}
这个函数是更改一个任务的优先级。同样的,在中断处理函数中不能调用,p_tcb
为0时,是更改当前任务的优先级。同时也得考虑Mutex
的情况。OS_TaskChangePrio
这个函数是更改过优先级之后,需要把当前任务所在的列表中的位置进行更改,因为某些列表的排序是根据优先级进行排序的(PendList
、ReadyList
)。最后如果OS
处于运行状态的话,调用一次OSSched
引发一次调度。
4. 暂停任务OSTaskSuspend
OSTaskSuspend
函数的实现如下:
[os_task.c OSTaskSuspend 函数]
void OSTaskSuspend (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err的入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TASK_SUSPEND_ISR;
return;
}
#endif
#if (OS_CFG_TASK_IDLE_EN == DEF_ENABLED)
//不能暂停空闲状态
if (p_tcb == &OSIdleTaskTCB) {
*p_err = OS_ERR_TASK_SUSPEND_IDLE;
return;
}
#endif
OS_TRACE_TASK_SUSPEND(p_tcb);
CPU_CRITICAL_ENTER();
//如果p_tcb为0
if (p_tcb == (OS_TCB *)0) {
//如果内核没运行
if (OSRunning != OS_STATE_OS_RUNNING) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
//暂停自己
p_tcb = OSTCBCurPtr;
}
//如果是自己
if (p_tcb == OSTCBCurPtr) {
//如果是锁住了调度器,则不能暂停自己,因为没办法调度到新的任务
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
}
*p_err = OS_ERR_NONE;
switch (p_tcb->TaskState) {
//如果任务是ready状态
case OS_TASK_STATE_RDY:
//设置状态为suspended
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
//设置suspended嵌套次数为1
p_tcb->SuspendCtr = 1u;
//从readyList中移除
OS_RdyListRemove(p_tcb);
CPU_CRITICAL_EXIT();
break;
//如果是delay状态
case OS_TASK_STATE_DLY:
//设置状态delay + suspended
p_tcb->TaskState = OS_TASK_STATE_DLY_SUSPENDED;
p_tcb->SuspendCtr = 1u;
CPU_CRITICAL_EXIT();
break;
//如果是pend状态
case OS_TASK_STATE_PEND:
//设置状态pend + suspended
p_tcb->TaskState = OS_TASK_STATE_PEND_SUSPENDED;
//设置suspended嵌套次数为1
p_tcb->SuspendCtr = 1u;
CPU_CRITICAL_EXIT();
break;
//如果是pend + timeout
case OS_TASK_STATE_PEND_TIMEOUT:
//设置状态pend + timeout + suspended
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED;
//设置suspended嵌套次数为1
p_tcb->SuspendCtr = 1u;
CPU_CRITICAL_EXIT();
break;
//如果已经包含了suspended,那么是嵌套的暂停
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_DLY_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
//如果之前的嵌套次数是-1,肯定有错误
if (p_tcb->SuspendCtr == (OS_NESTING_CTR)-1) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_SUSPEND_CTR_OVF;
return;
}
//增加嵌套次数
p_tcb->SuspendCtr++;
CPU_CRITICAL_EXIT();
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
return;
}
//如果内核处于运行状态
if (OSRunning == OS_STATE_OS_RUNNING) {
//进行一次调度
OSSched();
}
}
不能在ISR
中调用,如果是从ready
状态暂停,把任务从ReadyList
中移除。给任务的状态加上suspended
。如果之前不包含suspended
状态,那么把嵌套次数设置为1,如果之前包含suspended
状态,那么把嵌套暂停次数增加1。最后如果内核已经运行,调用一次OSSched
进行一次调度。
5. 恢复暂停的任务OSTaskResume
OSTaskResume
函数的实现:
[os_task.c OSTaskResume 函数]
void OSTaskResume (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//ISR中不能调用这个函数
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TASK_RESUME_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//内核运行前不能调用这个函数
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
CPU_CRITICAL_ENTER();
//p_tcb入参合法性判断,不能对自己做恢复暂停的操作
if ((p_tcb == (OS_TCB *)0) ||
(p_tcb == OSTCBCurPtr)) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_RESUME_SELF;
return;
}
CPU_CRITICAL_EXIT();
#endif
CPU_CRITICAL_ENTER();
*p_err = OS_ERR_NONE;
//如果任务不处于暂停状态,不包含suspended状态
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_SUSPENDED;
break;
//处于suspended状态
case OS_TASK_STATE_SUSPENDED:
p_tcb->SuspendCtr--;
//嵌套暂停全部恢复
if (p_tcb->SuspendCtr == 0u) {
//设置状态为ready态
p_tcb->TaskState = OS_TASK_STATE_RDY;
//插入到ReadyList中
OS_RdyListInsert(p_tcb);
}
CPU_CRITICAL_EXIT();
break;
//delay + suspended状态
case OS_TASK_STATE_DLY_SUSPENDED:
p_tcb->SuspendCtr--;
//嵌套暂停全部恢复
if (p_tcb->SuspendCtr == 0u) {
//改成Dleay状态
p_tcb->TaskState = OS_TASK_STATE_DLY;
}
CPU_CRITICAL_EXIT();
break;
//pend + suspended状态
case OS_TASK_STATE_PEND_SUSPENDED:
p_tcb->SuspendCtr--;
//嵌套暂停全部恢复
if (p_tcb->SuspendCtr == 0u) {
//改成pend状态
p_tcb->TaskState = OS_TASK_STATE_PEND;
}
CPU_CRITICAL_EXIT();
break;
//Pend + timeout + suspended状态
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
p_tcb->SuspendCtr--;
//嵌套暂停全部恢复
if (p_tcb->SuspendCtr == 0u) {
//设置成pend + timeout
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
}
CPU_CRITICAL_EXIT();
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
return;
}
OS_TRACE_TASK_RESUME(p_tcb);
//引发一次调度
OSSched();
}
在ISR
中不能调用这个函数,内核运行前不能调用这个函数,不能给自己做恢复暂停操作(事实上这个是不可能的,如果做了的话,一定有BUG
)。如果任务只是suspended
状态,恢复之后把任务放入到ReadyList
中。如果还有其他的状态(Delay
、Pend
、Pend + Timeout
),则只去掉suspended
,而不放入ReadyList
中。
6. 任务自身的信号量、消息队列的操作
任务自身的计数信号量操作、消息队列的操作,只跟这个任务有关系,OS_PEND_OBJ
内核对象:
任务自身的计数信号量函数 | 说明 |
---|---|
OSTaskSemPend | 任务自身计数信号量的Pend 操作 |
OSTaskSemPendAbort | 任务自身计数信号量的PendAbort 操作 |
OSTaskSemPost | 任务自身计数信号量的Pos 操作 |
OSTaskSemSet | 任务自身计数信号量的Set 操作 |
任务自身的消息队列函数 | 说明 |
---|---|
OSTaskQFlush | 冲掉任务自身消息队列中的所有消息 |
OSTaskQPend | 任务自身消息队列的Pend 操作 |
OSTaskQPendAbort | 任务自身消息队列的PendAbort 操作 |
OSTaskQPost | 任任务自身消息队列的Post 操作 |
七. 时间相关的API
1. 延时函数OSTimeDly
OSTimeDly
函数的实现如下:
[os_time.c OSTimeDly函数]
void OSTimeDly (OS_TICK dly,
OS_OPT opt,
OS_ERR *p_err)
{
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
CPU_SR_ALLOC();
#endif
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TIME_DLY_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
//内核调度上锁时
if (OSSchedLockNestingCtr > 0u) {
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
//opt入参合法性判断
switch (opt) {
case OS_OPT_TIME_DLY:
case OS_OPT_TIME_TIMEOUT:
case OS_OPT_TIME_PERIODIC:
if (dly == 0u) {
*p_err = OS_ERR_TIME_ZERO_DLY;
return;
}
break;
case OS_OPT_TIME_MATCH:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return;
}
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
CPU_CRITICAL_ENTER();
//将让如插入到OSTickListDly中
OS_TickListInsertDly(OSTCBCurPtr,
dly,
opt,
p_err);
if (*p_err != OS_ERR_NONE) {
CPU_CRITICAL_EXIT();
return;
}
OS_TRACE_TASK_DLY(dly);
//将任务从ReadyList中移除
OS_RdyListRemove(OSTCBCurPtr);
CPU_CRITICAL_EXIT();
//引发一次调度
OSSched();
#endif
}
这个函数不能在ISR
中调用,不能在内核运行前调用。把任务放入到OSTickListDly
中。最后调用OSSched
进行一次调度。
2. 按时分秒毫秒的延时函数OSTimeDlyHMSM
[os_time.c OSTimeDlyHMSM 函数]
void OSTimeDlyHMSM (CPU_INT16U hours,
CPU_INT16U minutes,
CPU_INT16U seconds,
CPU_INT32U milli,
OS_OPT opt,
OS_ERR *p_err)
{
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
CPU_BOOLEAN opt_invalid;
CPU_BOOLEAN opt_non_strict;
#endif
OS_OPT opt_time;
OS_RATE_HZ tick_rate;
OS_TICK ticks;
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
CPU_SR_ALLOC();
#endif
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在ISR中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TIME_DLY_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
//内核调度上锁时
if (OSSchedLockNestingCtr > 0u) {
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
opt_time = opt & OS_OPT_TIME_MASK;
switch (opt_time) {
case OS_OPT_TIME_DLY:
case OS_OPT_TIME_TIMEOUT:
case OS_OPT_TIME_PERIODIC:
if (milli == 0u) {
if (seconds == 0u) {
if (minutes == 0u) {
if (hours == 0u) {
*p_err = OS_ERR_TIME_ZERO_DLY;
return;
}
}
}
}
break;
case OS_OPT_TIME_MATCH:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return;
}
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
opt_invalid = DEF_BIT_IS_SET_ANY(opt, ~OS_OPT_TIME_OPTS_MASK);
if (opt_invalid == DEF_YES) {
*p_err = OS_ERR_OPT_INVALID;
return;
}
opt_non_strict = DEF_BIT_IS_SET(opt, OS_OPT_TIME_HMSM_NON_STRICT);
if (opt_non_strict != DEF_YES) {
if (milli > 999u) {
*p_err = OS_ERR_TIME_INVALID_MILLISECONDS;
return;
}
if (seconds > 59u) {
*p_err = OS_ERR_TIME_INVALID_SECONDS;
return;
}
if (minutes > 59u) {
*p_err = OS_ERR_TIME_INVALID_MINUTES;
return;
}
if (hours > 99u) {
*p_err = OS_ERR_TIME_INVALID_HOURS;
return;
}
} else {
if (minutes > 9999u) {
*p_err = OS_ERR_TIME_INVALID_MINUTES;
return;
}
if (hours > 999u) {
*p_err = OS_ERR_TIME_INVALID_HOURS;
return;
}
}
#endif
tick_rate = OSCfg_TickRate_Hz;
//换算时间成Tick
ticks = ((((OS_TICK)hours * (OS_TICK)3600u) + ((OS_TICK)minutes * (OS_TICK)60u) + (OS_TICK)seconds) * tick_rate)
+ ((tick_rate * ((OS_TICK)milli + ((OS_TICK)500u / tick_rate))) / (OS_TICK)1000u);
//如果延时超过一个tick
if (ticks > 0u) {
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
CPU_CRITICAL_ENTER();
//将让如插入到OSTickListDly中
OS_TickListInsertDly(OSTCBCurPtr,
ticks,
opt_time,
p_err);
if (*p_err != OS_ERR_NONE) {
CPU_CRITICAL_EXIT();
return;
}
OS_TRACE_TASK_DLY(ticks);
//从ReadyList中移除
OS_RdyListRemove(OSTCBCurPtr);
CPU_CRITICAL_EXIT();
//引发一次调度
OSSched();
#endif
*p_err = OS_ERR_NONE;
//没超过一个ticks
} else {
*p_err = OS_ERR_TIME_ZERO_DLY;
}
}
其实和OSTimeDly
基本相同,只不过把时分秒换算成Ticks
。
3. 从延时状态恢复OSTimeDlyResume
OSTimeDlyResume
函数实现:
[os_time.c OSTimeDlyResume 函数]
void OSTimeDlyResume (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
//p_err入参合法性判断
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN == DEF_ENABLED)
//不能在`ISR`中调用
if (OSIntNestingCtr > 0u) {
*p_err = OS_ERR_TIME_DLY_RESUME_ISR;
return;
}
#endif
#if (OS_CFG_ARG_CHK_EN == DEF_ENABLED)
//p_tcb合法性判断
if (p_tcb == (OS_TCB *)0) {
*p_err = OS_ERR_TCB_INVALID;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN == DEF_ENABLED)
//不能在内核运行前调用
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
CPU_CRITICAL_ENTER();
switch (p_tcb->TaskState) {
//如果没有delay状态,返回出错
case OS_TASK_STATE_RDY:
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
//delay状态
case OS_TASK_STATE_DLY:
p_tcb->TaskState = OS_TASK_STATE_RDY;
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
//如果TickList中移除
OS_TickListRemove(p_tcb);
//插入到ReadyList
OS_RdyListInsert(p_tcb);
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
break;
//如果delay + suspended
case OS_TASK_STATE_DLY_SUSPENDED:
//状态改为suspended
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
#if (OS_CFG_TASK_TICK_EN == DEF_ENABLED)
//只从TickList中移除,不放入ReadyList
OS_TickListRemove(p_tcb);
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_SUSPENDED;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
break;
}
//引发一次调度
OSSched();
}
这个函数不能在ISR
中调用,不能在内核运行前调用。如果没有delay
,则返回出错。如果是只有delay
状态,把任务从TickList
中移除,放入到ReadyList
。如果是delay + suspended
,只从TickList
中移除,不放回ReadyList
。最后调用OSSched
引发一次调度。
4. 时间戳的获取和设置
OSTimeGet
获取时间戳OSTimeSet
设置时间戳