任务消息队列

  任务消息队列跟任务信号量一样,均隶属于某一个特定任务,不需单独创建,任务在则在,只有该任务才可以接收这个任务消息队列的消息,其他任务只能给这个任务消息队列发送消息,却不能接收。任务消息队列与前面讲解的(普通)消息队列极其相似,只是任务消息队列已隶属于一个特定任务,所以它不具有等待列表,省去了等待任务插入和移除列表的动作,所以工作原理相对更简单一点,效率也比较高一些。 

  如果想要使用任务消息队列,就必须事先使能任务消息队列。消息队列的使能位于“os_cfg.h”。

#define OS_CFG_TASK_Q_EN                1u   //使能/禁用函数 OSTaskQXXXX()                                        

  特别声明,任务消息队列和(普通)消息队列公用一个消息池。一般任务消息队列或普通消息队列的最大消息容量不要超过消息池的消息数目。 

OSTaskQPost ()
  OSTaskQPost () 函数用于向任务消息队列发布一个消息。OSTaskQPost () 函数的信息如下表所示。 

  OSTaskQPost () 函数的定义也位于“os_task.c :

#if OS_CFG_TASK_Q_EN > 0u
void  OSTaskQPost (OS_TCB       *p_tcb,
                   void         *p_void,
                   OS_MSG_SIZE   msg_size,
                   OS_OPT        opt,
                   OS_ERR       *p_err)
{
    CPU_TS   ts;



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                                  /* ---------------- VALIDATE ARGUMENTS ------------------ */
    switch (opt) {                                          /* User must supply a valid option                        */
        case OS_OPT_POST_FIFO:
        case OS_OPT_POST_LIFO:
        case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED:
        case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return;
    }
#endif

    ts = OS_TS_GET();                                       /* Get timestamp                                          */

#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
        OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_MSG,      /* Post to ISR queue                                      */
                    (void      *)p_tcb,
                    (void      *)p_void,
                    (OS_MSG_SIZE)msg_size,
                    (OS_FLAGS   )0,
                    (OS_OPT     )opt,
                    (CPU_TS     )ts,
                    (OS_ERR    *)p_err);
        return;
    }
#endif

    OS_TaskQPost(p_tcb,
                 p_void,
                 msg_size,
                 opt,
                 ts,
                 p_err);
}
#endif
OSTaskQPost()

  其实,不管是否使能了中断延迟发布,最终都是调用 OS_TaskQPost () 函数进行发布任务消息。只是使能了中断延迟发布的发布过程会比较曲折,中间会有许多插曲,这是中断管理范畴的内容,留到后面再作介绍。

  OS_TaskQPost () 函数的定义位于“os_task.c :

#if OS_CFG_TASK_Q_EN > 0u
void  OS_TaskQPost (OS_TCB       *p_tcb,
                    void         *p_void,
                    OS_MSG_SIZE   msg_size,
                    OS_OPT        opt,
                    CPU_TS        ts,
                    OS_ERR       *p_err)
{
    CPU_SR_ALLOC();



    OS_CRITICAL_ENTER();
    if (p_tcb == (OS_TCB *)0) {                             /* Post msg to 'self'?                                    */
        p_tcb = OSTCBCurPtr;
    }
   *p_err  = OS_ERR_NONE;                                   /* Assume we won't have any errors                        */
    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:
             OS_MsgQPut(&p_tcb->MsgQ,                       /* Deposit the message in the queue                       */
                        p_void,
                        msg_size,
                        opt,
                        ts,
                        p_err);
             OS_CRITICAL_EXIT();
             break;

        case OS_TASK_STATE_PEND:
        case OS_TASK_STATE_PEND_TIMEOUT:
        case OS_TASK_STATE_PEND_SUSPENDED:
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
             if (p_tcb->PendOn == OS_TASK_PEND_ON_TASK_Q) { /* Is task waiting for a message to be sent to it?        */
                 OS_Post((OS_PEND_OBJ *)0,
                         p_tcb,
                         p_void,
                         msg_size,
                         ts);
                 OS_CRITICAL_EXIT_NO_SCHED();
                 if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0u) {
                     OSSched();                             /* Run the scheduler                                      */
                 }
             } else {
                 OS_MsgQPut(&p_tcb->MsgQ,                   /* No,  Task is pending on something else ...             */
                            p_void,                         /*      ... Deposit the message in the task's queue       */
                            msg_size,
                            opt,
                            ts,
                            p_err);
                 OS_CRITICAL_EXIT();
             }
             break;

        default:
             OS_CRITICAL_EXIT();
            *p_err = OS_ERR_STATE_INVALID;
             break;
    }
}
#endif
OS_TaskQPost()

  OS_TaskQPost () 函数中,会调用 OS_MsgQPut () 函数从消息池获取一个消息插入到消息队列。

  OS_MsgQPut () 函数的定义位于“os_msg.c”。

void  OS_MsgQPut (OS_MSG_Q     *p_msg_q,   //消息队列指针
                  void         *p_void,    //消息指针
                  OS_MSG_SIZE   msg_size,  //消息大小(单位:字节)
                  OS_OPT        opt,       //选项
                  CPU_TS        ts,        //消息被发布时的时间戳
                  OS_ERR       *p_err)     //返回错误类型
{
    OS_MSG  *p_msg;
    OS_MSG  *p_msg_in;



#ifdef OS_SAFETY_CRITICAL                //如果使能了安全检测
    if (p_err == (OS_ERR *)0) {          //如果错误类型实参为空
        OS_SAFETY_CRITICAL_EXCEPTION();  //执行安全检测异常函数
        return;                          //返回,停止执行
    }
#endif

    if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) { //如果消息队列已没有可用空间
       *p_err = OS_ERR_Q_MAX;                             //错误类型为“队列已满”
        return;                                           //返回,停止执行
    }

    if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) {  //如果消息池没有可用消息
       *p_err = OS_ERR_MSG_POOL_EMPTY;         //错误类型为“消息池没有消息”  
        return;                                //返回,停止执行
    }
    /* 从消息池获取一个消息(暂存于 p_msg )*/
    p_msg             = OSMsgPool.NextPtr;          //将消息控制块从消息池移除               
    OSMsgPool.NextPtr = p_msg->NextPtr;             //指向下一个消息(取走首个消息)
    OSMsgPool.NbrFree--;                            //消息池可用消息数减1
    OSMsgPool.NbrUsed++;                            //消息池被用消息数加1
    if (OSMsgPool.NbrUsedMax < OSMsgPool.NbrUsed) { //更新消息被用最大数目的历史记录
        OSMsgPool.NbrUsedMax = OSMsgPool.NbrUsed;
    }
    /* 将获取的消息插入到消息队列 */
    if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) {             //如果消息队列目前没有消息
        p_msg_q->InPtr         = p_msg;                     //将其入队指针指向该消息
        p_msg_q->OutPtr        = p_msg;                     //出队指针也指向该消息
        p_msg_q->NbrEntries    = (OS_MSG_QTY)1;             //队列的消息数为1
        p_msg->NextPtr         = (OS_MSG *)0;               //该消息的下一个消息为空
    } else {                                                //如果消息队列目前已有消息
        if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) { //如果用FIFO方式插入队列,
            p_msg_in           = p_msg_q->InPtr;            //将消息插入到入队端,入队
            p_msg_in->NextPtr  = p_msg;                     //指针指向该消息。
            p_msg_q->InPtr     = p_msg;
            p_msg->NextPtr     = (OS_MSG *)0;
        } else {                                            //如果用LIFO方式插入队列,
            p_msg->NextPtr     = p_msg_q->OutPtr;           //将消息插入到出队端,出队
            p_msg_q->OutPtr    = p_msg;                     //指针指向该消息。
        }
        p_msg_q->NbrEntries++;                              //消息队列的消息数目加1
    }
    if (p_msg_q->NbrEntriesMax < p_msg_q->NbrEntries) {     //更新改消息队列的最大消息
        p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries;       //数目的历史记录。
    }
    p_msg->MsgPtr  = p_void;                                //给该消息填写消息内容
    p_msg->MsgSize = msg_size;                              //给该消息填写消息大小
    p_msg->MsgTS   = ts;                                    //填写发布该消息时的时间戳
   *p_err          = OS_ERR_NONE;                           //错误类型为“无错误”
}
#endif
OS_MsgQPut()

  另外,OS_TaskQPost () 函数还调用了 OS_Post() 函数发布内核对象。OS_Post() 函数是一个底层的发布函数,它不仅仅用来发布任务消息队列,还可以发布多值信号量、互斥信号量、事件标志组、(普通)消息队列或任务信号量。

  OS_Post() 函数的定义位于“os_core.c 

void  OS_Post (OS_PEND_OBJ  *p_obj,     //内核对象类型指针
               OS_TCB       *p_tcb,     //任务控制块
               void         *p_void,    //消息
               OS_MSG_SIZE   msg_size,  //消息大小
               CPU_TS        ts)        //时间戳
{
    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 (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {    //如果任务在等待多个信号量或消息队列
                 OS_Post1(p_obj,                              //标记哪个内核对象被发布
                          p_tcb,
                          p_void,
                          msg_size,
                          ts);
             } else {                                         //如果任务不是在等待多个信号量或消息队列
#if (OS_MSG_EN > 0u)                                          //如果使能了任务队列或消息队列
                 p_tcb->MsgPtr  = p_void;                     //保存消息到等待任务
                 p_tcb->MsgSize = msg_size;                   
#endif
                 p_tcb->TS      = ts;                         //保存时间戳到等待任务
             }
             if (p_obj != (OS_PEND_OBJ *)0) {                 //如果内核对象为空
                 OS_PendListRemove(p_tcb);                    //从等待列表移除该等待任务
#if OS_CFG_DBG_EN > 0u                                        //如果使能了调试代码和变量 
                 OS_PendDbgNameRemove(p_obj,                  //移除内核对象的调试名
                                      p_tcb);
#endif
             }
             OS_TaskRdy(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;

        case OS_TASK_STATE_PEND_SUSPENDED:                    //如果任务在无期限等待中被挂起
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:            //如果任务在有期限等待中被挂起
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {    //如果任务在等待多个信号量或消息队列
                 OS_Post1(p_obj,                              //标记哪个内核对象被发布
                          p_tcb,
                          p_void,
                          msg_size,
                          ts);
             } else {                                         //如果任务不在等待多个信号量或消息队列
#if (OS_MSG_EN > 0u)                                          //如果使能了调试代码和变量
                 p_tcb->MsgPtr  = p_void;                     //保存消息到等待任务
                 p_tcb->MsgSize = msg_size;                     
#endif
                 p_tcb->TS      = ts;                         //保存时间戳到等待任务
             }
             OS_TickListRemove(p_tcb);                        //从节拍列表移除该等待任务
             if (p_obj != (OS_PEND_OBJ *)0) {                 //如果内核对象为空
                 OS_PendListRemove(p_tcb);                    //从等待列表移除该等待任务
#if OS_CFG_DBG_EN > 0u                                        //如果使能了调试代码和变量 
                 OS_PendDbgNameRemove(p_obj,                  //移除内核对象的调试名
                                      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;                                           //直接跳出
    }
}
OS_Post()

OSTaskQPend ()

  OSTaskQPost () 任务消息队列发布消息函数相对应,OSTaskQPend () 函数用于等待获取任务消息队列的消息

  OSTaskQPend () 函数的定义也位于“os_task.c”:

#if OS_CFG_TASK_Q_EN > 0u
void  *OSTaskQPend (OS_TICK       timeout,
                    OS_OPT        opt,
                    OS_MSG_SIZE  *p_msg_size,
                    CPU_TS       *p_ts,
                    OS_ERR       *p_err)
{
    OS_MSG_Q     *p_msg_q;
    void         *p_void;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((void *)0);
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Can't Pend from an ISR                                 */
       *p_err = OS_ERR_PEND_ISR;
        return ((void *)0);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                                  /* ---------------- VALIDATE ARGUMENTS ------------------ */
    if (p_msg_size == (OS_MSG_SIZE *)0) {                   /* User must supply a valid destination for msg size      */
       *p_err = OS_ERR_PTR_INVALID;
        return ((void *)0);
    }
    switch (opt) {                                          /* User must supply a valid option                        */
        case OS_OPT_PEND_BLOCKING:
        case OS_OPT_PEND_NON_BLOCKING:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return ((void *)0);
    }
#endif

    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS  )0;                                /* Initialize the returned timestamp                      */
    }

    CPU_CRITICAL_ENTER();
    p_msg_q = &OSTCBCurPtr->MsgQ;                           /* Any message waiting in the message queue?              */
    p_void  = OS_MsgQGet(p_msg_q,
                         p_msg_size,
                         p_ts,
                         p_err);
    if (*p_err == OS_ERR_NONE) {
#if OS_CFG_TASK_PROFILE_EN > 0u
        if (p_ts != (CPU_TS *)0) {
            OSTCBCurPtr->MsgQPendTime = OS_TS_GET() - *p_ts;
            if (OSTCBCurPtr->MsgQPendTimeMax < OSTCBCurPtr->MsgQPendTime) {
                OSTCBCurPtr->MsgQPendTimeMax = OSTCBCurPtr->MsgQPendTime;
            }
        }
#endif
        CPU_CRITICAL_EXIT();
        return (p_void);                                    /* Yes, Return oldest message received                    */
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
       *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
        CPU_CRITICAL_EXIT();
        return ((void *)0);
    } else {                                                /* Yes                                                    */
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /*     Can't block when the scheduler is locked           */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return ((void *)0);
        }
    }
                                                            /* Lock the scheduler/re-enable interrupts                */
    OS_CRITICAL_ENTER_CPU_EXIT();
    OS_Pend((OS_PEND_DATA *)0,                              /* Block task pending on Message                          */
            (OS_PEND_OBJ  *)0,
            (OS_STATE      )OS_TASK_PEND_ON_TASK_Q,
            (OS_TICK       )timeout);
    OS_CRITICAL_EXIT_NO_SCHED();

    OSSched();                                              /* Find the next highest priority task ready to run       */

    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* Extract message from TCB (Put there by Post)           */
             p_void      = OSTCBCurPtr->MsgPtr;
            *p_msg_size  = OSTCBCurPtr->MsgSize;
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = OSTCBCurPtr->TS;
#if OS_CFG_TASK_PROFILE_EN > 0u
                OSTCBCurPtr->MsgQPendTime = OS_TS_GET() - OSTCBCurPtr->TS;
                if (OSTCBCurPtr->MsgQPendTimeMax < OSTCBCurPtr->MsgQPendTime) {
                    OSTCBCurPtr->MsgQPendTimeMax = OSTCBCurPtr->MsgQPendTime;
                }
#endif
             }
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   = (CPU_TS  )0;
             }
            *p_err      =  OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get event within TO            */
        default:
             p_void     = (void      *)0;
            *p_msg_size = (OS_MSG_SIZE)0;
             if (p_ts  != (CPU_TS *)0) {
                *p_ts   =  OSTCBCurPtr->TS;
             }
            *p_err      =  OS_ERR_TIMEOUT;
             break;
    }
    CPU_CRITICAL_EXIT();
    return (p_void);                                        /* Return received message                                */
}
#endif
OSTaskQPend()

  OSTaskQPend () 函数中,会调用 OS_MsgQGet () 函数从任务消息队列获取一个消息。

  OS_MsgQGet () 函数的定义位于“os_msg.c”:

void  *OS_MsgQGet (OS_MSG_Q     *p_msg_q,     //消息队列
                   OS_MSG_SIZE  *p_msg_size,  //返回消息大小
                   CPU_TS       *p_ts,        //返回某些操作的时间戳
                   OS_ERR       *p_err)       //返回错误类型
{
    OS_MSG  *p_msg;
    void    *p_void;



#ifdef OS_SAFETY_CRITICAL               //如果使能(默认禁用)了安全检测
    if (p_err == (OS_ERR *)0) {         //如果错误类型实参为空
        OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数
        return ((void *)0);             //返回空消息,停止执行
    }
#endif

    if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) {  //如果消息队列没有消息
       *p_msg_size = (OS_MSG_SIZE)0;             //返回消息长度为0
        if (p_ts != (CPU_TS *)0) {               //如果 p_ts 非空
           *p_ts  = (CPU_TS  )0;                 //清零 p_ts
        }
       *p_err = OS_ERR_Q_EMPTY;                  //错误类型为“队列没消息”
        return ((void *)0);                      //返回空消息,停止执行
    }
    /* 如果消息队列有消息 */
    p_msg           = p_msg_q->OutPtr;          //从队列的出口端提取消息           
    p_void          = p_msg->MsgPtr;            //提取消息内容
   *p_msg_size      = p_msg->MsgSize;           //提取消息长度
    if (p_ts != (CPU_TS *)0) {                  //如果 p_ts 非空
       *p_ts  = p_msg->MsgTS;                   //获取消息被发布时的时间戳
    }

    p_msg_q->OutPtr = p_msg->NextPtr;           //修改队列的出队指针

    if (p_msg_q->OutPtr == (OS_MSG *)0) {       //如果队列没有消息了
        p_msg_q->InPtr      = (OS_MSG   *)0;    //清零出队指针
        p_msg_q->NbrEntries = (OS_MSG_QTY)0;    //清零消息数
    } else {                                    //如果队列还有消息
        p_msg_q->NbrEntries--;                  //队列的消息数减1
    }
    /* 从消息队列提取完消息信息后,将消息释放回消息池供继续使用 */
    p_msg->NextPtr    = OSMsgPool.NextPtr;      //消息插回消息池
    OSMsgPool.NextPtr = p_msg;
    OSMsgPool.NbrFree++;                        //消息池的可用消息数加1
    OSMsgPool.NbrUsed--;                        //消息池的已用消息数减1

   *p_err             = OS_ERR_NONE;            //错误类型为“无错误”
    return (p_void);                            //返回罅隙内容
}
OS_MsgQGet()

  OSTaskQPend () 函数会调用一个更加底层的等待函数来执行当前任务对消息队列的等待,该函数就是 OS_Pend()。与 OS_Post() 函数一样,OS_Pend() 函数不仅仅用来等待任务消息队列,还可以等待多值信号量、互斥信号量、事件标志组、(普通)消息队列或任务信号量。

  OS_Pend() 函数的定义位于“os_core.c”。

void  OS_Pend (OS_PEND_DATA  *p_pend_data,  //待插入等待列表的元素
               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;             //正常等待中

    OS_TaskBlock(OSTCBCurPtr,                                //阻塞当前运行任务,
                 timeout);                                   //如果 timeout 非0,把任务插入的节拍列表

    if (p_obj != (OS_PEND_OBJ *)0) {                         //如果等待对象非空
        p_pend_list             = &p_obj->PendList;          //获取对象的等待列表到 p_pend_list
        p_pend_data->PendObjPtr = p_obj;                     //保存要等待的对象
        OS_PendDataInit((OS_TCB       *)OSTCBCurPtr,         //初始化 p_pend_data(待插入等待列表)
                        (OS_PEND_DATA *)p_pend_data,
                        (OS_OBJ_QTY    )1);
        OS_PendListInsertPrio(p_pend_list,                   //按优先级将 p_pend_data 插入到等待列表
                              p_pend_data);
    } else {                                                 //如果等待对象为空
        OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY    )0; //清零当前任务的等待域数据
        OSTCBCurPtr->PendDataTblPtr     = (OS_PEND_DATA *)0; 
    }
#if OS_CFG_DBG_EN > 0u                                       //如果使能了调试代码和变量 
    OS_PendDbgNameAdd(p_obj,                                 //更新信号量的 DbgNamePtr 元素为其等待
                      OSTCBCurPtr);                          //列表中优先级最高的任务的名称。
#endif
}
OS_Pend()

OSTaskQPendAbort ()

  OSTaskQPendAbort () 函 数 用 于 中 止 任 务 对 其 任 务 消 息 队 列 的 等 待 。 要 使 用OSTaskQPendAbort () 函 数 , 除 了 要 先 使 能 前 面 的 OS_CFG_TASK_Q_EN 外 , 还 得 使 能OS_CFG_TASK_Q_PEND_ABORT_EN(位于“os_cfg.h”)

#define OS_CFG_TASK_Q_PEND_ABORT_EN     1u   //使能/禁用函数 OSTaskQPendAbort() 

  OSTaskQPendAbort () 函数的信息如下表所示:

  OSTaskQPendAbort () 函数的定义位于“os_task.c”: 

#if (OS_CFG_TASK_Q_EN > 0u) && (OS_CFG_TASK_Q_PEND_ABORT_EN > 0u)
CPU_BOOLEAN  OSTaskQPendAbort (OS_TCB  *p_tcb,
                               OS_OPT   opt,
                               OS_ERR  *p_err)
{
    CPU_TS         ts;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (DEF_FALSE);
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* See if called from ISR ...                             */
       *p_err = OS_ERR_PEND_ABORT_ISR;                      /* ... can't Pend Abort from an ISR                       */
        return (DEF_FALSE);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                                  /* ---------------- VALIDATE ARGUMENTS ------------------ */
    switch (opt) {                                          /* User must supply a valid option                        */
        case OS_OPT_POST_NONE:
        case OS_OPT_POST_NO_SCHED:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return (DEF_FALSE);
    }
#endif

    CPU_CRITICAL_ENTER();
#if OS_CFG_ARG_CHK_EN > 0u
    if ((p_tcb == (OS_TCB *)0) ||                           /* Pend abort self?                                       */
        (p_tcb == OSTCBCurPtr)) {
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_ABORT_SELF;                     /* ... doesn't make sense                                 */
        return (DEF_FALSE);
    }
#endif

    if (p_tcb->PendOn != OS_TASK_PEND_ON_TASK_Q) {          /* Is task waiting for a message?                         */
        CPU_CRITICAL_EXIT();                                /* No                                                     */
       *p_err = OS_ERR_PEND_ABORT_NONE;
        return (DEF_FALSE);
    }

    OS_CRITICAL_ENTER_CPU_EXIT();
    ts = OS_TS_GET();                                       /* Get timestamp of when the abort occurred               */
    OS_PendAbort((OS_PEND_OBJ *)0,                          /* Abort the pend                                         */
                 p_tcb,
                 ts);
    OS_CRITICAL_EXIT_NO_SCHED();
    if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
        OSSched();                                          /* Run the scheduler                                      */
    }
   *p_err = OS_ERR_NONE;
    return (DEF_TRUE);
}
#endif
OSTaskQPendAbort()

  OSTaskQPendAbort () 函数会调用一个更加底层的中止等待函数来执行当前任务对其任务消息队列的等待,该函数就是 OS_PendAbort()OS_PendAbort() 函数不仅仅用来中止对任务消息队列的等待,还可以中止对多值信号量、互斥信号量、事件标志组、(普通)消息队列或任务信号量的等待。

  OS_PendAbort() 函数的定义位于“os_core.c”:

void  OS_PendAbort (OS_PEND_OBJ  *p_obj,   //被等待对象的类型
                    OS_TCB       *p_tcb,   //任务控制块指针
                    CPU_TS        ts)      //等待被中止时的时间戳
{
    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 (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {  //如果任务在等待多个信号量或消息队列
                 OS_PendAbort1(p_obj,                       //强制解除任务对某一对象的等待
                               p_tcb,
                               ts);
             }
#if (OS_MSG_EN > 0u)                                //如果使能了任务队列或消息队列
             p_tcb->MsgPtr     = (void      *)0;    //清除(复位)任务的消息域
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
             p_tcb->TS         = ts;                        //保存等待被中止时的时间戳到任务控制块
             if (p_obj != (OS_PEND_OBJ *)0) {               //如果等待对象非空
                 OS_PendListRemove(p_tcb);                  //将任务从所有等待列表中移除
             }
             OS_TaskRdy(p_tcb);                             //让任务进准备运行
             p_tcb->TaskState  = OS_TASK_STATE_RDY;         //修改任务状态为就绪状态
             p_tcb->PendStatus = OS_STATUS_PEND_ABORT;      //标记任务的等待被中止
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;   //标记任务目前没有等待任何对象
             break;                                         //跳出

        case OS_TASK_STATE_PEND_SUSPENDED:                  //如果任务在无期限等待中被挂起
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:          //如果任务在有期限等待中被挂起
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {  //如果任务在等待多个信号量或消息队列
                 OS_PendAbort1(p_obj,                       //强制解除任务对某一对象的等待
                               p_tcb,
                               ts);
             }
#if (OS_MSG_EN > 0u)                              //如果使能了任务队列或消息队列
             p_tcb->MsgPtr     = (void      *)0;  //清除(复位)任务的消息域
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
             p_tcb->TS         = ts;                        //保存等待被中止时的时间戳到任务控制块
             if (p_obj != (OS_PEND_OBJ *)0) {               //如果等待对象非空
                 OS_PendListRemove(p_tcb);                  //将任务从所有等待列表中移除
             }
             OS_TickListRemove(p_tcb);                      //让任务脱离节拍列表
             p_tcb->TaskState  = OS_TASK_STATE_SUSPENDED;   //修改任务状态为挂起状态
             p_tcb->PendStatus = OS_STATUS_PEND_ABORT;      //标记任务的等待被中止
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;   //标记任务目前没有等待任何对象
             break;                                         //跳出

        default:                                            //如果任务状态超出预期
             break;                                         //不需处理,直接跳出
    }
}
OS_PendAbort()

 

原文地址:https://www.cnblogs.com/tianxxl/p/10386065.html