zoukankan      html  css  js  c++  java
  • 等待多个内核对象

      前面的等待内核对象,均是等待一个内核对象,这一章要讲解的是同时等待多个内核对象。这里的多个内核对象是指多值信号量和消息队列的任意组合。

      如果想要使用“等待多个内核对象”,就必须事先使能“等待多个内核对象”。“等待多个内核对象”的使能位于“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()

      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);                                          //返回,验证成功
    }
    OS_PendMultiValidate()

      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()

      如果 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++;                                      //处理下一个等待对象
        }
    }
    OS_PendMultiWait
  • 相关阅读:
    Linux_文件系统、磁盘分区_RHEL7
    Linux_LVM、RAID_RHEL7
    Linux_LVM、RAID_RHEL7
    Linux_系统时间管理
    简单聊聊HDFS RBF第二阶段工作近期的一些进展
    LinkedBlockingQueue和ArrayBlockingQueue之间的比较
    LinkedBlockingQueue和ArrayBlockingQueue之间的比较
    公司如何使用开源软件
    公司如何使用开源软件
    ListenableFuture和CompletableFuture简单小结
  • 原文地址:https://www.cnblogs.com/tianxxl/p/10382044.html
Copyright © 2011-2022 走看看