uCOS任务消息队列相关函数理解

任务消息队列:同任务信号量一样,任务消息队列存在于每个任务的TCB中,元素定义为OS_MSG_Q  MsgQ,无须单独创建,也就没有相关的创建函数。任务消息队列为每个任务独有,只有任务本身可以通过自己的任务消息队列获取消息,其他任务只能发送消息到该消息队列。其他任务不能获取,那就无须等待,不需要等待列表存储其他任务,所以没有等待列表,省去了进出等待列表的操作。

TCB中与任务消息队列相关的元素

{

OS_MSG_Q     MsgQ;  //消息列表

CPU_TS  MsgQPendTime; 任务等待自身任务消息列表的等待时间。

CPU_TS  MsgQPendTimeMax; 任务多次等待任务消息列表的最久一次的等待时间。

}

消息结构体

struct  os_msg {                                           

    OS_MSG    *NextPtr;  //指向下一消息的指针                         

    void  *MsgPtr;    //指向消息内容的指针           

    OS_MSG_SIZE   MsgSize;   //一个表示消息大小的变量           

    CPU_TS   MsgTS;   //一个信息发送的时间戳    

};

值得注意的是:消息队列发送消息的时间戳是保存在消息结构体中的,并不像信号量一样,依赖TCB中的元素CPU_TS  TS来保存信号量释放时的时间戳

OS_MSG_Q  MsgQ:其实OS_MSG_Q结构体是消息列表结构体,并不是消息队列结构体。因为反观内核消息队列结构体,其中除了消息列表和等待列表元素,其他都是调试相关的元素和消息队列的名字之类的。由于任务消息队列是每个任务独有的,所以并不需要指定消息队列名字,直接找相应的任务就可以找到其消息队列;也不需要等待列表和调试列表。所以任务消息队列只是在TCB中添加了一个消息列表的元素OS_MSG_Q  MsgQ

struct  os_msg_q {                                     

    OS_MSG    *InPtr;  //指向要插入队列的下一个消息的指针                    

    OS_MSG   *OutPtr;  //指向要从队列中提取的下一个消息的指针;                

    OS_MSG_QTY    NbrEntriesSize;  //队列中允许的最大消息个数       

    OS_MSG_QTY   NbrEntries;   //队列中当前的消息个数        

    OS_MSG_QTY    NbrEntriesMax;  //队列中的消息个数峰值                  

    CPU_INT32U   MsgQID;    //第三方调试器和跟踪器的唯一ID                   

};

消息队列发送和接收消息机制:① 消息队列发送消息是从消息池中取消息,成功获取一个消息,消息池可用消息个数就减少1。② 消息池中的消息在初始化时并没有指定消息指向,使用指针不仅是为了指向不定长的消息,也是为了在运行过程中,灵活指向用户要发送的消息。③ 消息队列发送函数在发送消息时才给指向消息结构体赋值,但是在任务获取到消息时,是直接把消息返还到消息池,并没有清空消息的指向。因为每次消息发送时都会被赋值,所以无须清空

OSTaskQPost():任务消息队列发送函数

{

5个入口参数:指向目标TCB的指针p_tcb;指向消息内容的指针p_void;消息的大小msg_size;选项opt;返回的错误类型p_err。

函数过程:① 首先进行安全检查,参数选项检查。选项主要包括OS_OPT_POST_FIFO和OS_OPT_POST_LIFO以及他们与OS_OPT_POST_NO_SCHED的组合。② 获取时间戳作为发送消息队列的时间点。③ 如果使能了中断延迟发布,并且该函数在中断中被调用,那就调用OS_IntQPost(),将消息先发送到中断消息队列,并返回。④ 否则,调用OS_TaskQPost()将消息直接发送。

--调用OS_TaskQPost()

6个入口参数:指向目标TCB的指针p_tcb;指向消息内容的指针p_void;消息大小变量msg_size;选项opt;时间戳ts;返回的错误类型p_err。

函数过程:① uCOS允许指定一个空的TCB指针,空TCB指针的话就是发送消息给任务自身,而非其他任务。② 因为发送消息给任务有两种情况:一是有任务阻塞等待该消息,二是没有任务在等待该消息。因为任务消息队列为任务独有,没有等待列表,所以就根据目标任务的状态判断该任务是否在等待该消息。如果目标TCB的状态没有等待态,说明该任务并没有在等待该消息队列,那么就调用OS_MsgQPut()把消息插入消息列表。③ 如果目标TCB状态由等待态,要看它是否是在等待任务消息队列:如果是在等待消息队列,那么调用OS_Post()将消息发送给目标TCB(其实就是对TCB中消息相关的元素赋值);紧接着判断是否需要调用OSSched()进行任务调度。如果不是在等待任务消息队列,那么发送的消息也应该插入消息列表,执行步骤②的操作,调用OS_MsgQPut()把消息插入消息列表。

OS_MsgQPut()都是对消息列表进行操作。所以内核消息队列和任务消息队列的操作都一样。只不过其中参数,内核消息队列是操作内核消息队列结构体中的消息列表;任务消息队列是操作TCB结构体中消息列表元素。

}  

OSTaskQPend():任务消息队列获取函数

{

5个入口参数:超时时间timeout;选项opt;指向消息大小的指针p_msg_size;返回消息发布时的时间戳p_ts;返回的错误类型p_err。

返回:该函数是指针函数,本质上是函数,返回是void *类型。返回的指针就是指向消息内容的指针。

函数过程:① 进行安全,参数,选项,中断中非法调用检查。② 消息队列获取函数,首先检查任务消息列表中有无等待的消息。通过OS_MsgQGet()获得消息列表中的消息。如果返回的错误类型为OS_ERR_NONE,说明消息列表中有消息,并且得到指向消息的指针p_void;然后获取当前时间戳与该消息发布时的时间戳作差得到此次等待消息的时间;如果有必要则更新MsgQPendTimeMax;最后返回指向消息的指针p_void。③ OS_MsgQGet()只会返回两个错误:一是OS_ERR_NONE,表明消息列表中有消息;二是OS_ERR_Q_EMPTY表明消息列表中午消息。如果无消息,就要根据opt判断用户是否指定任务进入阻塞,以等待消息,如果用户选择不阻塞,则返回OS_ERR_PEND_WOULD_BLOCK错误类型,并返回空指针。(这里其实可以故意选择不阻塞的选项,然后通过错误类型判断任务是否获取到消息。因为如果获取到消息则已经返回指向消息的指针,不会进行到判断是否阻塞这一步;否则就返回空指针和需要阻塞的错误类型)。④ 如果消息列表无可用消息,且用户选择了阻塞等待,那么就要把任务移出就绪列表,此时需要判断调度器是否被锁。如果没被锁,就调用OS_Pend()阻塞该任务,即把该任务移出就绪列表,加入时基列表(如果超时时间不为0)。最后调用OSSched()进行任务切换。⑤ 程序执行到这里,必定是任务加入到就绪列表,要么是得到消息,要么是被中止、超时为得到消息等。所以需要根据任务状态进行相应的处理。如果是等到消息OS_STATUS_PEND_OK,那么就获取指向消息的指针和消息的大小,并返回指向消息的指针。其他情况指向消息内容的指针为空,消息大小为0。

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值