首页 > 代码库 > RAW-OS:消息队列

RAW-OS:消息队列

平台:VS2010 版本:1.04

   我们先看看消息队列的数据结构:

typedef struct RAW_QUEUE
{
    RAW_COMMON_BLOCK_OBJECT       common_block_obj;
    RAW_MSG_Q                     msg_q;
    RAW_VOID                      (*queue_send_notify)(struct RAW_QUEUE *queue_ptr);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
} RAW_QUEUE;

   RAW_MSG_Q:

typedef struct RAW_MSG_Q {   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    RAW_VOID         **queue_start;            /* Pointer to start of queue data                              */
    RAW_VOID         **queue_end;              /* Pointer to end   of queue data                              */
    RAW_VOID         **write;               /* Pointer to where next message will be inserted  in   the Q  */
    RAW_VOID         **read;              /* Pointer to where next message will be extracted from the Q  */
    MSG_SIZE_TYPE    size;             /* Size of queue (maximum number of entries)                   */
    MSG_SIZE_TYPE    current_numbers;          /* Current number of entries in the queue                      */
    MSG_SIZE_TYPE    peak_numbers;          /* Peak number of entries in the queue                      */
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
} RAW_MSG_Q;

  Common_block_obj:

typedef struct RAW_COMMON_BLOCK_OBJECT {   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    LIST                      block_list;
    RAW_U8                    *name;
    RAW_U8                    block_way;
    RAW_U8                    object_type;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
} RAW_COMMON_BLOCK_OBJECT;

   消息队列是用来在两个任务之间传递数据的,它相当于一个有存储功能的数据通道。例如:Task1把MSG1放入queue,则Task2可从queue中MSG1。需要注意的是消息传递是地址而不是数值。其实消息的通信机制和信号量是类似,只是一个需要传递参数一个不需要。下面我们就来看看源代码:

   Raw_queue_creat:

RAW_U16 raw_queue_create(RAW_QUEUE *p_q, RAW_U8 *p_name, RAW_VOID **msg_start, MSG_SIZE_TYPE number)
{
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    #if (RAW_QUEUE_FUNCTION_CHECK > 0)
    if (p_q == 0) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        return RAW_NULL_OBJECT;
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    if (msg_start == 0) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        return RAW_NULL_POINTER;
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    if (number == 0u) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        return RAW_ZERO_NUMBER;
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    #endif
    /*init the queue blocked list*/
    list_init(&p_q->common_block_obj.block_list);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    p_q->common_block_obj.name = p_name;  
    p_q->common_block_obj.block_way = RAW_BLOCKED_WAY_PRIO;
    p_q->msg_q.queue_start       = msg_start;               /*      Initialize the queue                 */
    p_q->msg_q.queue_end        = &msg_start[number];        //warning:queue not include the queue_end
    p_q->msg_q.write            = msg_start;
    p_q->msg_q.read             = msg_start;
    p_q->msg_q.size              = number;
    p_q->msg_q.current_numbers  = 0u;
    p_q->msg_q.peak_numbers      = 0u;
    p_q->queue_send_notify      = 0;
    p_q->common_block_obj.object_type = RAW_QUEUE_OBJ_TYPE;
    TRACE_QUEUE_CREATE(raw_task_active, p_q);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    return RAW_SUCCESS;
}

   create函数最主要是对MSG结构进行赋值,都很好理解的。这里看三点:
       1 block_way:PRIO or FIFO,消息支持的任务阻塞策略:任务优先级或者是先来先出和信号量相同

       2 queue_end:注意queue队列不包含queue_end指向的地址,看到&msg_start[number]中number就应该明白了

       3 peak_numbers:这个是queue队列中最多消息的数量



   Msg_post:

RAW_U16 msg_post(RAW_QUEUE *p_q, RAW_VOID *p_void, RAW_U8 opt_send_method, RAW_U8 opt_wake_all)            
{
    LIST *block_list_head;
    RAW_SR_ALLOC();
    RAW_CRITICAL_ENTER();
    if (p_q->common_block_obj.object_type != RAW_QUEUE_OBJ_TYPE) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        RAW_CRITICAL_EXIT();
        return RAW_ERROR_OBJECT_TYPE;
    }
    block_list_head = &p_q->common_block_obj.block_list;
    //消息队列满了
    if (p_q->msg_q.current_numbers >= p_q->msg_q.size) { 
        RAW_CRITICAL_EXIT();
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        TRACE_QUEUE_MSG_MAX(raw_task_active, p_q, p_void, opt_send_method);
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        return RAW_MSG_MAX;
    }
    /*Queue is not full here, if there is no blocked receive task*/
    if (is_list_empty(block_list_head)) {       
        p_q->msg_q.current_numbers++;                               
        /*update peak_numbers for debug*/
        if (p_q->msg_q.current_numbers > p_q->msg_q.peak_numbers) {
            p_q->msg_q.peak_numbers = p_q->msg_q.current_numbers;
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        if (opt_send_method == SEND_TO_END)  {
            *p_q->msg_q.write++ = p_void;                             
            if (p_q->msg_q.write == p_q->msg_q.queue_end) {  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                p_q->msg_q.write = p_q->msg_q.queue_start;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
            }  
        }
        else {
             /* Wrap read pointer to end if we are at the 1st queue entry */
            if (p_q->msg_q.read == p_q->msg_q.queue_start) {               
                p_q->msg_q.read = p_q->msg_q.queue_end;
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
            p_q->msg_q.read--;
            *p_q->msg_q.read = p_void;                               /* Insert message into queue                     */
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        RAW_CRITICAL_EXIT();
        /*if queue is registered with notify function just call it*/       
        if (p_q->queue_send_notify) {
            p_q->queue_send_notify(p_q);
        }
        TRACE_QUEUE_MSG_POST(raw_task_active, p_q, p_void, opt_send_method);
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        return RAW_SUCCESS;
    }
    /*wake all the task blocked on this queue*/
    if (opt_wake_all) {
        while (!is_list_empty(block_list_head)) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
            wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list),  p_void); 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
            TRACE_QUEUE_WAKE_TASK(raw_task_active, list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void, opt_wake_all);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    /*wake hignhest priority task blocked on this queue and send msg to it*/
    else {
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list),  p_void);
                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        TRACE_QUEUE_WAKE_TASK(raw_task_active, list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void, opt_wake_all);
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    RAW_CRITICAL_EXIT();
    do_possible_sche();   
    return RAW_SUCCESS;
}

   消息传递函数,我们来看下函数的执行过程:
       1 判断消息队列的类型

       2 判断消息队列是否满了

       3 若消息队列中没有被阻塞的任务:
           3.1 更新peak_number的值

           3.2 根据opt_send_mathod参数将msg放置在消息队列中

               3.2.1 消息添加到消息的最后,体会write指针的改变

               3.2.2 消息添加到消息的最前,体会read指针的改变----为什么是改变read指针呢?看下满的获取msg函数

       4 根据opt_wake_all参数选择唤醒最前面的task还是所有task

   wake_send_msg函数和信号量的wake函数一样,只是在这里多一步给task的msg变量赋值的操作,这里就不在分析了。

   在这看一点,当消息队列满时,怎么操作呢?raw宏定义了一个TRACE_QUEUE_MSG_POST(NOTE1)函数让你自己去实现。


   Raw_queue_receive:

RAW_U16 raw_queue_receive(RAW_QUEUE *p_q, RAW_TICK_TYPE wait_option, RAW_VOID **msg)
{
    RAW_VOID *pmsg;
    RAW_U16 result;
                                                                                                                                                                                                                                                              
    RAW_SR_ALLOC();
    #if (RAW_QUEUE_FUNCTION_CHECK > 0)
    if (raw_int_nesting) {
                                                                                                                                                                                                                                                                  
        return RAW_NOT_CALLED_BY_ISR;
                                                                                                                                                                                                                                                                  
    }
    if (p_q == 0) {
                                                                                                                                                                                                                                                                  
        return RAW_NULL_OBJECT;
    }
                                                                                                                                                                                                                                                              
    if (msg == 0) {
                                                                                                                                                                                                                                                                  
        return RAW_NULL_POINTER;
    }
                                                                                                                                                                                                                                                              
    #endif
                                                                                                                                                                                                                                                              
    RAW_CRITICAL_ENTER();
    if (p_q->common_block_obj.object_type != RAW_QUEUE_OBJ_TYPE) {
                                                                                                                                                                                                                                                                  
        RAW_CRITICAL_EXIT();
        return RAW_ERROR_OBJECT_TYPE;
    }
    /*if queue has msgs, just receive it*/
    if (p_q->msg_q.current_numbers) {
                                                                                                                                                                                                                                                                  
        pmsg = *p_q->msg_q.read++;                   
                                                                                                                                                                                                                                                                  
        if (p_q->msg_q.read == p_q->msg_q.queue_end) {
            /*wrap around to start*/
            p_q->msg_q.read = p_q->msg_q.queue_start;
        }
        *msg = pmsg;
        p_q->msg_q.current_numbers--; 
                                                                                                                                                                                                                                                                  
        RAW_CRITICAL_EXIT();
        TRACE_QUEUE_GET_MSG(raw_task_active, p_q, wait_option, *msg);
                                                                                                                                                                                                                                                                  
        return RAW_SUCCESS;                        
    }
    if (wait_option == RAW_NO_WAIT) {  
        *msg = (RAW_VOID *)0;
        RAW_CRITICAL_EXIT();
        return RAW_NO_PEND_WAIT;
    }
    /*if system is locked, block operation is not allowed*/
    SYSTEM_LOCK_PROCESS_QUEUE();
    raw_pend_object((RAW_COMMON_BLOCK_OBJECT  *)p_q, raw_task_active, wait_option);
                                                                                                                                                                                                                                                              
    RAW_CRITICAL_EXIT();
    TRACE_QUEUE_GET_BLOCK(raw_task_active, p_q, wait_option);
                                                                                                                                                                                                                                                              
    raw_sched();                                            
    *msg      = (RAW_VOID      *)0;
    result = block_state_post_process(raw_task_active, msg);
                                                                                                                                                                                                                                                              
    return result;
                                                                                                                                                                                                                                                              
}

   获取msg函数分析如下:
       1 判断消息队列类型

       2 消息队列中有消息则立即获取后返回。看这里获取是操作read指针来进行着就是为什么消息插在最前面时修改read指针。在消息的传递和获取时都需要操作current_number成员,它用来判断消息队列的使用情况,空、满载和负载。

       3 若消息队列中没有消息且wait_option参数为RAW_NO_WAIT则立即返回不等待消息

       4 若消息队列中没有消息且wait_option参数不为RAW_NO_WAIT,则会raw_pend_object函数即task进入阻塞状态,具体不分析了因为信号量的阻塞是一样的

       5 函数返回的值是msg,在block_state_post_process函数中直接将task的msg赋值给函数返回值



NOTE1:在函数前面加TRACE都是raw的宏定义函数让你自己去实现,看TRACE这个单词就明白了。

本文出自 “Runner” 博客,请务必保留此出处http://pregnant.blog.51cto.com/8349579/1411255