等待多个内核对象
Posted 飞起的小田
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等待多个内核对象相关的知识,希望对你有一定的参考价值。
前面的等待内核对象,均是等待一个内核对象,这一章要讲解的是同时等待多个内核对象。这里的多个内核对象是指多值信号量和消息队列的任意组合。
如果想要使用“等待多个内核对象”,就必须事先使能“等待多个内核对象”。“等待多个内核对象”的使能位于“os_cfg.h”。
#define OS_CFG_PEND_MULTI_EN 1u //使能/禁用等待多个内核对象
另外,值得注意,等待多个内核对象的内核对象指的是多值信号量或消息队列,要等待这两种对象,均须先使能它们,分别为 OS_CFG_SEM_EN 和 OS_CFG_Q_EN,均位于“os_cfg.h”
OSPendMulti ()
OSPendMulti () 函数用于等待多个内核对象(多值信号量或消息队列)。OSPendMulti ()函数的信息如下表所示。
OSPendMulti () 函数的定义位于“os_pend_multi.c”:
OS_OBJ_QTY OSPendMulti (OS_PEND_DATA *p_pend_data_tbl, //等待对象(数组) 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(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变 //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR) //,开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能(默认使能)了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_PEND_ISR; //错误类型为“在中断函数中定时” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能(默认使能)了参数检测 if (p_pend_data_tbl == (OS_PEND_DATA *)0) { //如果参数 p_pend_data_tbl 为空 *p_err = OS_ERR_PTR_INVALID; //错误类型为“等待对象不可用” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } if (tbl_size == (OS_OBJ_QTY)0) { //如果 tbl_size 为0 *p_err = OS_ERR_PTR_INVALID; //错误类型为“等待对象不可用” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } switch (opt) { //根据选项分类处理 case OS_OPT_PEND_BLOCKING: //如果选项在预期内 case OS_OPT_PEND_NON_BLOCKING: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif /* 证实等待对象是否只有多值信号量或消息队列 */ valid = OS_PendMultiValidate(p_pend_data_tbl, tbl_size); if (valid == DEF_FALSE) { //如果等待对象不是只有多值信号量或消息队列 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } /* 如果等待对象确实只有多值信号量或消息队列 */ /*$PAGE*/ CPU_CRITICAL_ENTER(); //关中断 nbr_obj_rdy = OS_PendMultiGetRdy(p_pend_data_tbl, //查看是否有对象被提交了 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) { //如果选择了不阻塞任务 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为“渴望阻塞” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } else { //如果选择了阻塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } } OS_CRITICAL_ENTER_CPU_EXIT(); //锁调度器,重开中断 OS_PendMultiWait(p_pend_data_tbl, //挂起当前任务等到有对象被发布或超时 tbl_size, timeout); OS_CRITICAL_EXIT_NO_SCHED(); //解锁调度器(无调度) OSSched(); //调度任务 /* 任务等到了(一个)对象后得以继续运行 */ CPU_CRITICAL_ENTER(); //关中断 switch (OSTCBCurPtr->PendStatus) { //根据当前任务的等待状态分类处理 case OS_STATUS_PEND_OK: //如果任务已经等到了对象 *p_err = OS_ERR_NONE; //错误类型为“无错误” break; //跳出 case OS_STATUS_PEND_ABORT: //如果任务的等待被中止 *p_err = OS_ERR_PEND_ABORT; //错误类型为“等待对象被中止” break; //跳出 case OS_STATUS_PEND_TIMEOUT: //如果等待超时 *p_err = OS_ERR_TIMEOUT; //错误类型为“等待超时” break; //跳出 case OS_STATUS_PEND_DEL: //如果任务等待的对象被删除 *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); //返回被发布对象数目为1 }
OSPendMulti () 函数中,会调用 OS_PendMultiValidate () 函数验证等待对象是否均属于多值信号量或消息队列,如果不是,就返回,不继续执行等待。
OS_PendMultiValidate () 函数的定义位于“os_pend_multi.c”:
CPU_BOOLEAN OS_PendMultiValidate (OS_PEND_DATA *p_pend_data_tbl, //等待对象 OS_OBJ_QTY tbl_size) //等待对象数目 { OS_OBJ_QTY i; OS_OBJ_QTY ctr; #if OS_CFG_SEM_EN > 0u OS_SEM *p_sem; #endif #if OS_CFG_Q_EN > 0u OS_Q *p_q; #endif for (i = 0u; i < tbl_size; i++) { //逐个判断等待对象 if (p_pend_data_tbl->PendObjPtr == (OS_PEND_OBJ *)0) { //如果等待对象不存在 return (DEF_FALSE); //返回,验证失败 } ctr = 0u; //置0 ctr #if OS_CFG_SEM_EN > 0u //如果使能了多值信号量 p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr);//获取等待对象 if (p_sem->Type == OS_OBJ_TYPE_SEM) { //如果该对象是多值信号量类型 ctr++; //ctr 为1 } #endif #if OS_CFG_Q_EN > 0u //如果使能了消息队列 p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); //获取等待对象 if (p_q->Type == OS_OBJ_TYPE_Q) { //如果该对象是消息队列类型 ctr++; //ctr 为1 } #endif if (ctr == (OS_OBJ_QTY)0) { //如果 ctr = 0 return (DEF_FALSE); //返回,验证失败 } p_pend_data_tbl++; //验证下一个等待对象 } return (DEF_TRUE); //返回,验证成功 }
在 OSPendMulti () 函数中,还会调用 OS_PendMultiGetRdy () 函数查看是否有等待对象已被发布可供立即使用。
OS_PendMultiGetRdy () 函数的定义位于“os_pend_multi.c”:
OS_OBJ_QTY OS_PendMultiGetRdy (OS_PEND_DATA *p_pend_data_tbl, //等待对象 OS_OBJ_QTY tbl_size) //等待对象数目 { OS_OBJ_QTY i; OS_OBJ_QTY nbr_obj_rdy; #if OS_CFG_Q_EN > 0u OS_ERR err; OS_MSG_SIZE msg_size; OS_Q *p_q; void *p_void; CPU_TS ts; #endif #if OS_CFG_SEM_EN > 0u OS_SEM *p_sem; #endif nbr_obj_rdy = (OS_OBJ_QTY)0; for (i = 0u; i < tbl_size; i++) { //逐个检测等待对象 p_pend_data_tbl->RdyObjPtr = (OS_PEND_OBJ *)0; //清零等待对象的所有数据 p_pend_data_tbl->RdyMsgPtr = (void *)0; p_pend_data_tbl->RdyMsgSize = (OS_MSG_SIZE )0; p_pend_data_tbl->RdyTS = (CPU_TS )0; p_pend_data_tbl->NextPtr = (OS_PEND_DATA *)0; p_pend_data_tbl->PrevPtr = (OS_PEND_DATA *)0; p_pend_data_tbl->TCBPtr = (OS_TCB *)0; #if OS_CFG_Q_EN > 0u //如果使能了消息队列 p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); //将该对象视为消息队列处理 if (p_q->Type == OS_OBJ_TYPE_Q) { //如果该对象确为消息队列类型 p_void = OS_MsgQGet(&p_q->MsgQ, //从该消息队列获取消息 &msg_size, &ts, &err); if (err == OS_ERR_NONE) { //如果获取消息成功 p_pend_data_tbl->RdyObjPtr = p_pend_data_tbl->PendObjPtr; p_pend_data_tbl->RdyMsgPtr = p_void; //保存接收到的消息 p_pend_data_tbl->RdyMsgSize = msg_size; p_pend_data_tbl->RdyTS = ts; nbr_obj_rdy++; } } #endif #if OS_CFG_SEM_EN > 0u //如果使能了多值信号量 p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); //将该对象视为多值信号量处理 if (p_sem->Type == OS_OBJ_TYPE_SEM) { //如果该对象确为多值信号量类型 if (p_sem->Ctr > 0u) { //如果该信号量可用 p_sem->Ctr--; //等待任务可以获得信号量 p_pend_data_tbl->RdyObjPtr = p_pend_data_tbl->PendObjPtr; p_pend_data_tbl->RdyTS = p_sem->TS; nbr_obj_rdy++; } } #endif p_pend_data_tbl++; //检测下一个等待对象 } return (nbr_obj_rdy); //返回被发布的对象的数目 }
如果 OS_PendMultiGetRdy () 函数发现已有等待对象可用,OSPendMulti () 函数就会返回,继续运行任务。如果发现没有可用等待对象,就会继续调用 OS_PendMultiWait() 函数阻 塞 当 前 运 行 任 务 , 等 待 内 核 对 象 。
OS_PendMultiWait() 函 数 的 定 义 也 位 于“os_pend_multi.c”:
void OS_PendMultiWait (OS_PEND_DATA *p_pend_data_tbl, //等待对象 OS_OBJ_QTY tbl_size, //等待对象数目 OS_TICK timeout) //超时(单位:时钟节拍) { OS_OBJ_QTY i; OS_PEND_LIST *p_pend_list; #if OS_CFG_Q_EN > 0u OS_Q *p_q; #endif #if OS_CFG_SEM_EN > 0u OS_SEM *p_sem; #endif OSTCBCurPtr->PendOn = OS_TASK_PEND_ON_MULTI; //等待对象不可用,开始等待 OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK; OSTCBCurPtr->PendDataTblEntries = tbl_size; OSTCBCurPtr->PendDataTblPtr = p_pend_data_tbl; OS_TaskBlock(OSTCBCurPtr, //阻塞当前运行任务 timeout); for (i = 0u; i < tbl_size; i++) { //逐个将等待对象插入等待列表 p_pend_data_tbl->TCBPtr = OSTCBCurPtr; //所有的等待对象都绑定当前任务 #if OS_CFG_SEM_EN > 0u //如果使能了多值信号量 p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr);//将对象视为多值信号量处理 if (p_sem->Type == OS_OBJ_TYPE_SEM) { //如果该对象确为多值信号量 p_pend_list = &p_sem->PendList; //获取该信号量的等待列表 OS_PendListInsertPrio(p_pend_list, //将当前任务插入该等待列表 p_pend_data_tbl); } #endif #if OS_CFG_Q_EN > 0u //如果使能了消息队列 p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); //将对象视为消息队列处理 if (p_q->Type == OS_OBJ_TYPE_Q) { //如果该对象确为消息队列 p_pend_list = &p_q->PendList; //获取该消息队列的等待列表 OS_PendListInsertPrio(p_pend_list, //将当前任务插入该等待列表 p_pend_data_tbl); } #endif p_pend_data_tbl++; //处理下一个等待对象 } }
以上是关于等待多个内核对象的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段