uC/OS任务信号量

在实际任务间的通信中,一个或多个任务发送一个信号量或者消息给另一个任务是比常见的,而一个任务给多个任务发送信号量和消息相对比较少。前面所讲的信号量和消息队列均是单独的内核对象,是独立于任务存在的。这两章要讲述的任务信号量和任务消息队列是
任务特有的属性,紧紧依赖于一个特定任务。

  任务信号量和任务消息队列分别与多值信号量和消息队列非常相似,不同之处是,前者仅发布给一个特定任务,而后者可以发布给多个任务。因此,前者的操作相对比较简单,而且省时。如果任务信号量和任务消息队列可以满足设计需求,那么尽量不要使用普通多值信号量和消息队列

  任务信号量伴随任务存在,只要创建了任务,其任务信号量就是该任务的一个数据成员,任务信号量的数据成员被包含在任务控制块里。
OSTaskSemPost ()
  OSTaskSemPost () 函数用于给一个任务发布任务信号量。OSTaskSemPost () 函数的信息如下表所示。

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

S_SEM_CTR  OSTaskSemPost (OS_TCB  *p_tcb,   //目标任务
                           OS_OPT   opt,     //选项
                           OS_ERR  *p_err)   //返回错误类型
{
    OS_SEM_CTR  ctr;
    CPU_TS      ts;



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

#if OS_CFG_ARG_CHK_EN > 0u                  //如果使能(默认使能)了参数检测功能
    switch (opt) {                          //根据选项分类处理
        case OS_OPT_POST_NONE:              //如果选项在预期之内
        case OS_OPT_POST_NO_SCHED:
             break;                         //跳出

        default:                            //如果选项超出预期
            *p_err =  OS_ERR_OPT_INVALID;   //错误类型为“选项非法”
             return ((OS_SEM_CTR)0u);       //返回0(有错误),停止执行
    }
#endif

    ts = OS_TS_GET();                                      //获取时间戳

#if OS_CFG_ISR_POST_DEFERRED_EN > 0u                       //如果使能了中断延迟发布
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {             //如果该函数是在中断中被调用
        OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_SIGNAL,  //将该信号量发布到中断消息队列
                    (void      *)p_tcb,
                    (void      *)0,
                    (OS_MSG_SIZE)0,
                    (OS_FLAGS   )0,
                    (OS_OPT     )0,
                    (CPU_TS     )ts,
                    (OS_ERR    *)p_err);
        return ((OS_SEM_CTR)0);                           //返回0(尚未发布)   
    }
#endif

    ctr = OS_TaskSemPost(p_tcb,                          //将信号量按照普通方式处理
                         opt,
                         ts,
                         p_err);

    return (ctr);                                       //返回信号的当前计数值
}

OSTaskSemPost()

  其实,不管是否使能了中断延迟发布,最终都是调用 OS_TaskSemPost() 函数进行发布信号量。只是使能了中断延迟发布的发布过程会比较曲折,中间会有许多插曲,这是中断管理范畴的内容。

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

OS_SEM_CTR  OS_TaskSemPost (OS_TCB  *p_tcb,   //目标任务
                            OS_OPT   opt,     //选项
                            CPU_TS   ts,      //时间戳
                            OS_ERR  *p_err)   //返回错误类型
{
    OS_SEM_CTR  ctr;
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

    OS_CRITICAL_ENTER();                               //进入临界段
    if (p_tcb == (OS_TCB *)0) {                        //如果 p_tcb 为空
        p_tcb = OSTCBCurPtr;                           //将任务信号量发给自己(任务)
    }
    p_tcb->TS = ts;                                    //记录信号量被发布的时间戳
   *p_err     = OS_ERR_NONE;                           //错误类型为“无错误”
    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:
             switch (sizeof(OS_SEM_CTR)) {                        //判断是否将导致该信
                 case 1u:                                         //号量计数值溢出,如
                      if (p_tcb->SemCtr == DEF_INT_08U_MAX_VAL) { //果溢出,则开中断,
                          OS_CRITICAL_EXIT();                     //返回错误类型为“计
                         *p_err = OS_ERR_SEM_OVF;                 //数值溢出”,返回0
                          return ((OS_SEM_CTR)0);                 //(有错误),不继续
                      }                                           //执行。
                      break;                                      

                 case 2u:
                      if (p_tcb->SemCtr == DEF_INT_16U_MAX_VAL) {
                          OS_CRITICAL_EXIT();
                         *p_err = OS_ERR_SEM_OVF;
                          return ((OS_SEM_CTR)0);
                      }
                      break;

                 case 4u:
                      if (p_tcb->SemCtr == DEF_INT_32U_MAX_VAL) {
                          OS_CRITICAL_EXIT();
                         *p_err = OS_ERR_SEM_OVF;
                          return ((OS_SEM_CTR)0);
                      }
                      break;

                 default:
                      break;
             }
             p_tcb->SemCtr++;                              //信号量计数值不溢出则加1
             ctr = p_tcb->SemCtr;                          //获取信号量的当前计数值
             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_SEM) { //如果正等待任务信号量
                 OS_Post((OS_PEND_OBJ *)0,                    //发布信号量给目标任务
                         (OS_TCB      *)p_tcb,
                         (void        *)0,
                         (OS_MSG_SIZE  )0u,
                         (CPU_TS       )ts);
                 ctr = p_tcb->SemCtr;                         //获取信号量的当前计数值
                 OS_CRITICAL_EXIT_NO_SCHED();                 //退出临界段(无调度)
                 if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) { //如果选择了调度任务
                     OSSched();                               //调度任务
                 }
             } else {                                         //如果没等待任务信号量
                 switch (sizeof(OS_SEM_CTR)) {                         //判断是否将导致
                     case 1u:                                          //该信号量计数值
                          if (p_tcb->SemCtr == DEF_INT_08U_MAX_VAL) {  //溢出,如果溢出,
                              OS_CRITICAL_EXIT();                      //则开中断,返回
                             *p_err = OS_ERR_SEM_OVF;                  //错误类型为“计
                              return ((OS_SEM_CTR)0);                  //数值溢出”,返
                          }                                            //回0(有错误),
                          break;                                       //不继续执行。

                     case 2u:
                          if (p_tcb->SemCtr == DEF_INT_16U_MAX_VAL) {
                              OS_CRITICAL_EXIT();
                             *p_err = OS_ERR_SEM_OVF;
                              return ((OS_SEM_CTR)0);
                          }
                          break;

                     case 4u:
                          if (p_tcb->SemCtr == DEF_INT_32U_MAX_VAL) {
                              OS_CRITICAL_EXIT();
                             *p_err = OS_ERR_SEM_OVF;
                              return ((OS_SEM_CTR)0);
                          }
                          break;

                     default:
                          break;
                 }
                 p_tcb->SemCtr++;                            //信号量计数值不溢出则加1
                 ctr = p_tcb->SemCtr;                        //获取信号量的当前计数值
                 OS_CRITICAL_EXIT();                         //退出临界段
             }
             break;                                          //跳出

        default:                                             //如果任务状态超出预期
             OS_CRITICAL_EXIT();                             //退出临界段
            *p_err = OS_ERR_STATE_INVALID;                   //错误类型为“状态非法”
             ctr   = (OS_SEM_CTR)0;                          //清零 ctr
             break;                                          //跳出
    }
    return (ctr);                                            //返回信号量的当前计数值
}

OS_TaskSemPost()

  在 OS_SemPost() 函数中,又会调用 OS_Post() 函数发布内核对象。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()

OSTaskSemPend () 

  与 OSSemPost () 多值信号量发布函数相对应,OSTaskSemPend () 函数用于等待任务信号量。

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

OS_SEM_CTR  OSTaskSemPend (OS_TICK   timeout,  //等待超时时间
                           OS_OPT    opt,      //选项
                           CPU_TS   *p_ts,     //返回时间戳
                           OS_ERR   *p_err)    //返回错误类型
{
    OS_SEM_CTR    ctr;
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

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

#if OS_CFG_ARG_CHK_EN > 0u                  //如果使能了参数检测
    switch (opt) {                          //根据选项分类处理
        case OS_OPT_PEND_BLOCKING:          //如果选项在预期内
        case OS_OPT_PEND_NON_BLOCKING:
             break;                         //直接跳出

        default:                            //如果选项超出预期
            *p_err = OS_ERR_OPT_INVALID;    //错误类型为“选项非法”
             return ((OS_SEM_CTR)0);        //返回0(有错误),停止执行
    }
#endif

    if (p_ts != (CPU_TS *)0) {      //如果 p_ts 非空
       *p_ts  = (CPU_TS  )0;        //清零(初始化)p_ts
    }

    CPU_CRITICAL_ENTER();                        //关中断  
    if (OSTCBCurPtr->SemCtr > (OS_SEM_CTR)0) {   //如果任务信号量当前可用
        OSTCBCurPtr->SemCtr--;                   //信号量计数器减1
        ctr    = OSTCBCurPtr->SemCtr;            //获取信号量的当前计数值
        if (p_ts != (CPU_TS *)0) {               //如果 p_ts 非空
           *p_ts  = OSTCBCurPtr->TS;             //返回信号量被发布的时间戳
        }
#if OS_CFG_TASK_PROFILE_EN > 0u                  //如果使能了任务控制块的简况变量
        OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS;     //更新任务等待
        if (OSTCBCurPtr->SemPendTimeMax < OSTCBCurPtr->SemPendTime) { //任务信号量的
            OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime;   //最长时间记录。
        }
#endif
        CPU_CRITICAL_EXIT();                     //开中断            
       *p_err = OS_ERR_NONE;                     //错误类型为“无错误”
        return (ctr);                            //返回信号量的当前计数值
    }
    /* 如果任务信号量当前不可用 */
    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {  //如果选择了不阻塞任务
        CPU_CRITICAL_EXIT();                              //开中断
       *p_err = OS_ERR_PEND_WOULD_BLOCK;                  //错误类型为“缺乏阻塞”
        return ((OS_SEM_CTR)0);                           //返回0(有错误),停止执行
    } else {                                              //如果选择了阻塞任务
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {  //如果调度器被锁
            CPU_CRITICAL_EXIT();                          //开中断
           *p_err = OS_ERR_SCHED_LOCKED;                  //错误类型为“调度器被锁”
            return ((OS_SEM_CTR)0);                       //返回0(有错误),停止执行
        }
    }
    /* 如果调度器未被锁 */
    OS_CRITICAL_ENTER_CPU_EXIT();                         //锁调度器,重开中断                      
    OS_Pend((OS_PEND_DATA *)0,                            //阻塞任务,等待信号量。
            (OS_PEND_OBJ  *)0,                            //不需插入等待列表。
            (OS_STATE      )OS_TASK_PEND_ON_TASK_SEM,
            (OS_TICK       )timeout);
    OS_CRITICAL_EXIT_NO_SCHED();                          //开调度器(无调度)

    OSSched();                                            //调度任务
    /* 任务获得信号量后得以继续运行 */
    CPU_CRITICAL_ENTER();                                 //关中断
    switch (OSTCBCurPtr->PendStatus) {                    //根据任务的等待状态分类处理
        case OS_STATUS_PEND_OK:                           //如果任务成功获得信号量
             if (p_ts != (CPU_TS *)0) {                   //返回信号量被发布的时间戳
                *p_ts                    =  OSTCBCurPtr->TS;
#if OS_CFG_TASK_PROFILE_EN > 0u                           //更新最长等待时间记录
                OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS;
                if (OSTCBCurPtr->SemPendTimeMax < OSTCBCurPtr->SemPendTime) {
                    OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime;
                }
#endif
             }
            *p_err = OS_ERR_NONE;                         //错误类型为“无错误”
             break;                                       //跳出

        case OS_STATUS_PEND_ABORT:                        //如果等待被中止
             if (p_ts != (CPU_TS *)0) {                   //返回被终止时的时间戳
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_PEND_ABORT;                   //错误类型为“等待被中止”
             break;                                       //跳出

        case OS_STATUS_PEND_TIMEOUT:                      //如果等待超时
             if (p_ts != (CPU_TS *)0) {                   //返回时间戳为0
                *p_ts  = (CPU_TS  )0;
             }
            *p_err = OS_ERR_TIMEOUT;                      //错误类型为“等待超时”
             break;                                       //跳出

        default:                                          //如果等待状态超出预期
            *p_err = OS_ERR_STATUS_INVALID;               //错误类型为“状态非法”
             break;                                       //跳出
    }                                                     
    ctr = OSTCBCurPtr->SemCtr;                            //获取信号量的当前计数值
    CPU_CRITICAL_EXIT();                                  //开中断
    return (ctr);                                         //返回信号量的当前计数值
}

OSTaskSemPend()

  当需要阻塞任务,等待任务信号量时,OSTaskSemPend () 函数会调用一个更加底层的等待函数来执行当前任务对多值信号量的等待,该函数就是 OS_Pend()。与 OS_Post() 函数一样,OS_Pend() 函数不仅仅用来等待任务信号量,还可以等待多值信号量、互斥信号量、消息队列、事件标志组或任务消息队列。注意,在这里,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()

OSTaskSemPendAbort () 

  OSTaskSemPendAbort() 函数用于中止一个任务对其任务信号量的等待。要使用OSTaskSemPendAbort() 函数,还得事先使能 OS_CFG_TASK_SEM_PEND_ABORT_EN(位于“os_cfg.h”)

#define OS_CFG_TASK_SEM_PEND_ABORT_EN   1u   //使能/禁用函数 OSTaskSemPendAbort()

  OSTaskSemPendAbort() 函数的信息如下表所示。

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

#if OS_CFG_TASK_SEM_PEND_ABORT_EN > 0u  //如果使能了 OSTaskSemPendAbort()
CPU_BOOLEAN  OSTaskSemPendAbort (OS_TCB  *p_tcb, //目标任务
                                 OS_OPT   opt,   //选项
                                 OS_ERR  *p_err) //返回错误类型
{
    CPU_TS         ts;
    CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和
                    //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器
                    // SR(临界段关中断只需保存SR),开中断时将该值还原。

#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) {   //如果该函数是在中断中被调用
       *p_err = OS_ERR_PEND_ABORT_ISR;           //错误类型为“在中断中创建对象”
        return (DEF_FALSE);                      //返回(失败),停止执行
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                  //如果使能了参数检测
    switch (opt) {                          //根据选项匪类处理
        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 ((p_tcb == (OS_TCB *)0) ||         //如果 p_tcb 为空,或者
        (p_tcb == OSTCBCurPtr)) {         //p_tcb 指向当前运行任务。
        CPU_CRITICAL_EXIT();              //开中断
       *p_err = OS_ERR_PEND_ABORT_SELF;   //错误类型为“中止自身”
        return (DEF_FALSE);               //返回(失败),停止执行
    }
    /* 如果 p_tcb (目标任务) 不是当前运行任务(自身) */
    if (p_tcb->PendOn != OS_TASK_PEND_ON_TASK_SEM) { //如果目标任务没在等待任务信号量
        CPU_CRITICAL_EXIT();                         //开中断
       *p_err = OS_ERR_PEND_ABORT_NONE;              //错误类型为“没在等待任务信号量”
        return (DEF_FALSE);                          //返回(失败),停止执行
    }
    CPU_CRITICAL_EXIT();                             //开中断

    OS_CRITICAL_ENTER();                             //进入临界段
    ts = OS_TS_GET();                                //获取时间戳
    OS_PendAbort((OS_PEND_OBJ *)0,                   //中止目标任务对信号量的等待
                 p_tcb, 
                 ts);
    OS_CRITICAL_EXIT_NO_SCHED();                     //退出临界段(无调度)
    if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) { //如果选择了任务调度
        OSSched();                                   //调度任务
    }
   *p_err = OS_ERR_NONE;                             //错误类型为“无错误”
    return (DEF_TRUE);                               //返回(中止成功)
}
#endif

OSTaskSemPendAbort()

  OSTaskSemPendAbort() 函数会调用一个更加底层的中止等待函数来执行当前任务对多值信号量的等待,该函数就是 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/10385933.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值