同时等待多个内核对象
在前面,我们了解到多任务是如何等待单个内核对象的,这里的内核对象包括信号量,互斥信号量,事件标志组或者消息队列等。本次将讨论任务是如何同时等待多个内核对象的。然而,UCOSIII仅允许任务同时等待多个信号量和多个消息队列。换言之,UCOSIII不支持同时等待多个事件标志组或互斥信号量。
UCOSIII中一个任务可以同时等待任意数量的信号量或者消息队列,当只要等到其中的任意一个的时候就会导致该任务进入就绪态,如下图所示:
在图中,一个任务可以同时等待任意数量的信号量或者消息队列。第一个信号量或者消息队列的发布会导致该任务进入就绪态并和就绪表中的其他任务竞争CPU的时间。在图中,一个任务可调函数OSPendMulti()来等待多个对象。并且可以根据需要指定一个等待超时值,该值对所有对象都有效。如果在指定的超时时限内,没有任何一个对象被发布,那么这个任务将返回一个错误代码,表示等待超时了。
OSPendMulti()函数
函数OSPendMulti()用来等待多个内核对象,调用OSPendMulti()时,如果这些对象中有多个可用,则所有可用的信号量和消息都将返回给调用者;如果没有任何对象可用,则OSPendMulti()将挂起当前任务,直到以下任一情况发生:
1 对象变为可用
2 可以设定的超时时间
3 一个或多个任务被删除或被终止
4 一个或多个对象被删除
如果一个对象变为可用,并且有多个任务在等待这个对象,则UCOSIII将恢复优先级最高的那个任务,函数OSPendMulti()原型:
OS_OBJ_QTY OSPendMulti (OS_PEND_DATA *p_pend_data_tbl, //指向OS_PEND_DATA表的指针
OS_OBJ_QTY tbl_size, //所等待的内核对象数量
OS_TICK timeout, //设定一个等待超时值(时钟节拍数)
OS_OPT opt, //来选择是否使用阻塞模式
OS_ERR *p_err)
{
CPU_BOOLEAN valid;
OS_OBJ_QTY nbr_obj_rdy;
CPU_SR_ALLOC();
valid = OS_PendMultiValidate(p_pend_data_tbl, /* -------- Validate objects to be OS_SEM or OS_Q ------- */
tbl_size);
if (valid == DEF_FALSE) {
*p_err = OS_ERR_OBJ_TYPE; /* Invalid, not OS_SEM or OS_Q */
return ((OS_OBJ_QTY)0);
}
CPU_CRITICAL_ENTER();
nbr_obj_rdy = OS_PendMultiGetRdy(p_pend_data_tbl, /* --------- SEE IF OBJECT(s) HAVE BEEN POSTED ---------- */
tbl_size);
if (nbr_obj_rdy > (OS_OBJ_QTY)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return ((OS_OBJ_QTY)nbr_obj_rdy);
}
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK; /* No */
return ((OS_OBJ_QTY)0);
} else {
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend when the scheduler is locked */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_OBJ_QTY)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); /* Lock the scheduler/re-enable interrupts */
/* ------ NO OBJECT READY, PEND ON MULTIPLE OBJECTS ----- */
OS_PendMultiWait(p_pend_data_tbl, /* Suspend task until object posted or timeout occurs */
tbl_size,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
OSSched(); /* Find next highest priority task ready */
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: /* We got one of the objects posted to */
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: /* Indicate that the multi-pend was aborted */
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn't get semaphore within timeout */
*p_err = OS_ERR_TIMEOUT;
break;
case OS_STATUS_PEND_DEL: /* Indicate that an object pended on has been deleted */
*p_err = OS_ERR_OBJ_DEL;
break;
default:
*p_err = OS_ERR_STATUS_INVALID;
break;
}
OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
CPU_CRITICAL_EXIT();
return ((OS_OBJ_QTY)1);
}
p_pend_data_tbl:指向OS_PEND_DATA表的指针,调用者通过该表来查询函数的调用结果。
tbl_siae:指定了OS_PEND_DATA数组的大小。
timeout:通过这个参数,可以设置一个等待超时值。
opt:用来选择是否使用阻塞模式
虽然我们不需要定义一个同时等待多个内核对象,但是我们需要定义一个数组,用来将需要等待的内核对象放入其中:也就是OS_PEND_DATA类型的数组:
struct os_pend_data {
OS_PEND_DATA *PrevPtr;
OS_PEND_DATA *NextPtr;
OS_TCB *TCBPtr;
OS_PEND_OBJ *PendObjPtr; //指向需要等待的内核对象
OS_PEND_OBJ *RdyObjPtr;
void *RdyMsgPtr;
OS_MSG_SIZE RdyMsgSize;
CPU_TS RdyTS;
};
在这个结构体中,PendObjPtr就是指向需要等待的内核对象。所以,我们需要定义一个OS_PEND_DATA类型的数组,再向每个数组元素的PendObjPtr成员对象赋需要等待的内核对象的地址。比如:
OS_SEM Test_Sem1; //信号量1
OS_SEM Test_Sem2; //信号量2
OS_Q Test_Q; //消息队列
OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM]; //定义数组
pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem1;
pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem2;
pend_multi_tbl[2].PendObjPtr=(OS_PEND_OBJ*)&Test_Q;
小结
UCOSIII允许任务同时等待多个内核对象。
OSPendMulti()仅允许同时等待多个信号量和消息队列,不允许同时等待时间标志组和互斥信号量。
如果OSPendMulti()被调用时已经有对象被发送了,那么UCOSIII将指定表中哪些对象被发送了。
OSPendMulti()是一个有可能很长临界段代码的复杂函数。