原创 FreeRTOS队列管理初探

2009-5-21 18:39 3001 2 2 分类: MCU/ 嵌入式
前段时间拜读了bluehacker前辈的两篇关于FreeRTOS任务管理与时间管理的文章,收益颇多。今天我也把自己在阅读FreeRTOS源码过程中的一些笔记放出来,如果有高手路过恳请指点一二,不胜感激。


队列结构与创建




队列结构的定义如下:


typedef struct QueueDefinition

{

    signed portCHAR *pcHead;                /*队列 头部指针*/

    signed portCHAR *pcTail;                /*尾部指针,比长度多一个字节*/



    signed portCHAR *pcWriteTo;                /*指向下一个可以写入的空闲空间*/

    signed portCHAR *pcReadFrom;            /*指向队列中最后一个已读的数据 */



    xList xTasksWaitingToSend;                /*等待发送的任务队列,按优先级排序 */

    xList xTasksWaitingToReceive;            /*等待读取的任务队列,按优先级排序*/



    volatile unsigned portBASE_TYPE uxMessagesWaiting;   /*队列中当前的条目数*/

    unsigned portBASE_TYPE uxLength;        /*队列总的条目个数 */

    unsigned portBASE_TYPE uxItemSize;        /*每个条目的大小*/



    signed portBASE_TYPE xRxLock;            /*队列接收锁标志 */

    signed portBASE_TYPE xTxLock;            /*队列发送锁标志 */

     /*这两个标志的值可以为queueUNLOCKED (-1)、queueLOCKED_UNMODIFIED(0)、或正值,当为正直时表示锁的次数*/

} xQUEUE;


队列的创建


队列通过xQueueCreate函数来创建,此函数主要负责申请队列空间,初始化队列的各项的值。


xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize )

{

xQUEUE *pxNewQueue;

size_t xQueueSizeInBytes;



    /* 为队列分配空间*/

    if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 )

    {

        pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) );

        if( pxNewQueue != NULL )

        {

            /* Create the list of pointers to queue items.  The queue is one byte

            longer than asked for to make wrap checking easier/faster. */

            xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1;    /*分配的空间比队列长度多一个字节,方便检测*/



            pxNewQueue->pcHead = ( signed portCHAR * ) pvPortMalloc( xQueueSizeInBytes );

            if( pxNewQueue->pcHead != NULL )

            {

                /* Initialise the queue members as described above where the

                queue type is defined. */

                pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize );

                pxNewQueue->uxMessagesWaiting = 0;

                pxNewQueue->pcWriteTo = pxNewQueue->pcHead;

                pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - 1 ) * uxItemSize );    /*指向的是最后一个已读的条目,故初始化为最后一项*/

                pxNewQueue->uxLength = uxQueueLength;

                pxNewQueue->uxItemSize = uxItemSize;

                pxNewQueue->xRxLock = queueUNLOCKED;

                pxNewQueue->xTxLock = queueUNLOCKED;



                /* Likewise ensure the event queues start with the correct state. */

                vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );      /*初始化两个等待队列*/

                vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );



                traceQUEUE_CREATE( pxNewQueue );    /*用于调试跟踪*/



                return  pxNewQueue;

            }

            else

            {

                traceQUEUE_CREATE_FAILED();

                vPortFree( pxNewQueue );

            }

        }

    }



    /* Will only reach here if we could not allocate enough memory or no memory

    was required. */

    return NULL;

}


队列发送

FreeRTOS有三个一般的队列发送函数,xQueueSend(),
xQueueSendToFront()  和  xQueueSendToBack()
,此三个函数不可以在中断中使用,它们都是通过宏的方式实现,其中xQueueSend()与xQueueSendToBack()的定义是一样的,只是
为与早期的版本兼容而保留下来。


此三个宏定义如下:

#define
xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait,
queueSEND_TO_FRONT )

#define xQueueSendToBack( xQueue,
pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue,
xTicksToWait, queueSEND_TO_BACK )

#define xQueueSend( xQueue,
pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue,
xTicksToWait, queueSEND_TO_BACK )

三个宏在底层均是通过xQueueGenericSend函数来实现,通过传递给此函数的最后一个参数来区别三个宏的实现,下面分析xQueueGenericSend函数的实现方式


signed
portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void *
const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE
xCopyPosition )

{

signed portBASE_TYPE xEntryTimeSet = pdFALSE;
xTimeOutType xTimeOut;

    /* This function relaxes the coding standard somewhat to allow return
    statements within the function itself.  This is done in the interest
    of execution time efficiency. */

    for( ;; )
    {
        taskENTER_CRITICAL();
        {
              /* Is there room on the queue now?  To be running we must be
              the highest priority task wanting to access the queue. */
            if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )    /*判断队列是否已满,如果还有空的空间则插入条目*/
            {
               traceQUEUE_SEND( pxQueue );

                /*

                prvCopyDataToQueue函数用于把条目数据复制到队列中

                xCopyPosition为queueSEND_TO_FRONT时把条目数据复制到pcReadFrom指向的位置,即放到第一项

                为queueSEND_TO_BACK时则复制到pcWriteTo指向的位置,即为最后一项

                */
                prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
                /* If there was a task waiting for data to arrive on the
                queue then unblock it now. */

               /*如果有任务在等待则把任务从等待队列移出然后进行调度*/

                if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )  
                {
                    if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE )
                    {
                        /* The unblocked task has a priority higher than
                        our own so yield immediately.  Yes it is ok to do
                       this from within the critical section - the kernel
                        takes care of that. */

                        taskYIELD();
                    }
                }
                taskEXIT_CRITICAL();
                return pdPASS;
            }

            else    /*队列已满,如果xTicksToWait>0则把任务插入到等待队列,否则直接返回队列满信息*/
            {
                if( xTicksToWait == ( portTickType ) 0 )    /*不等待返回*/
                {
                    /* The queue was full and no block time is specified (or
                    the block time has expired) so leave now. */
                    taskEXIT_CRITICAL();
                    traceQUEUE_SEND_FAILED( pxQueue );
                    return errQUEUE_FULL;
                }
                else if( xEntryTimeSet == pdFALSE )  
                {
                    /* The queue was full and a block time was specified so
                   configure the timeout structure. */
                    vTaskSetTimeOutState( &xTimeOut );    /*把xTimeOut初始化为当前的系统时间计数*/
                    xEntryTimeSet = pdTRUE;
                }
            }
        }
        taskEXIT_CRITICAL();   
        /* Interrupts and other tasks can send to and receive from the queue
        now the critical section has been exited. */
        vTaskSuspendAll();
        prvLockQueue( pxQueue );
        /* Update the timeout state to see if it has expired yet. */

        /*检查超时是否已经到达,并且通过当前系统时间来校正xTicksToWait的值*/

        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) 
        {
            if( prvIsQueueFull( pxQueue ) )    /*如果队列已满,则把当前任务插入到队列的等待列表中*/
            {       
                traceBLOCKING_ON_QUEUE_SEND( pxQueue );
                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
                /* Unlocking the queue means queue events can effect the
                event list.  It is possible    that interrupts occurring now
                remove this task from the event    list again - but as the
                scheduler is suspended the task will go onto the pending
                ready last instead of the actual ready list. */
                prvUnlockQueue( pxQueue );

                /* Resuming the scheduler will move tasks from the pending
                ready list into the ready list - so it is feasible that this
                task is already in a ready list before it yields - in which
                case the yield will not cause a context switch unless there
                is also a higher priority task in the pending ready list. */

                if( !xTaskResumeAll() )
                {
                    taskYIELD();
                }
            }
            else    /*队列未满,重试插入*/
            {
                /* Try again. */

                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();           
            }
        }
        else    /*已经超时,不等待返回*/
        {
            /* The timeout has expired. */
            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();
            traceQUEUE_SEND_FAILED( pxQueue );
            return errQUEUE_FULL;
        }
    }
}


队列接收




与队列的发送对应,一般的队列接收函数也是由宏来实现,底层用一个函数来实现,队列接收函数有下面两个:


#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdTRUE )


#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdFALSE )


xQueuePeek与xQueueReceive的区别在于前者只接收而不删除条目,后者在成功接收后会删除原来的条目,两者均通过xQueueGenericReceive函数来实现,不能在中断中调用。


下面分析xQueueGenericReceive的实现方式:


signed portBASE_TYPE
xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer,
portTickType xTicksToWait, portBASE_TYPE xJustPeeking )

{

signed portBASE_TYPE xEntryTimeSet = pdFALSE;

xTimeOutType xTimeOut;

signed portCHAR *pcOriginalReadPosition;



    /* This function relaxes the coding standard somewhat to allow return

    statements within the function itself.  This is done in the interest

    of execution time efficiency. */



    for( ;; )

    {

        taskENTER_CRITICAL();

        {

              /* Is there space on the queue now?  To be running we must be

              the highest priority task wanting to access the queue. */       

            if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )   /*队列非空*/

            {

                /* Remember our read position in case we are just peeking. */

                pcOriginalReadPosition = pxQueue->pcReadFrom;



                prvCopyDataFromQueue( pxQueue, pvBuffer );



                if( xJustPeeking == pdFALSE )    /*复制完成后删除条目*/

                {

                    traceQUEUE_RECEIVE( pxQueue );



                    /* We are actually removing data. */

                    --( pxQueue->uxMessagesWaiting );



                    #if ( configUSE_MUTEXES == 1 )

                    {

                        if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )    /*与互斥锁相关的操作,改变互斥锁的拥有者为当前任务*/

                        {

                            /* Record the information required to implement

                            priority inheritance should it become necessary. */

                            pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle();

                        }

                    }

                    #endif



                    if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )   /*如果过有条目在等待发送,则移出等待队列并进行切换*/

                    {

                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )

                        {

                            taskYIELD();

                        }

                    }

                }

                else    /*不删除条目*/

                {

                    traceQUEUE_PEEK( pxQueue );



                    /* We are not removing the data, so reset our read

                    pointer. */

                    pxQueue->pcReadFrom = pcOriginalReadPosition;



                    /* The data is being left in the queue, so see if there are

                    any other tasks waiting for the data. */

                    if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )

                    {

                        /* Tasks that are removed from the event list will get added to

                        the pending ready list as the scheduler is still suspended. */

                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )

                        {

                            /* The task waiting has a higher priority than this task. */

                            taskYIELD();

                        }

                    }



                }



                taskEXIT_CRITICAL();

                return pdPASS;

            }

            else 
/*条目为空,如果xTicksToWait&amp;gt;0则插入到等待队列,否则直接返回,下面的操作跟队列发送时类似*/

            {

                if( xTicksToWait == ( portTickType ) 0 )

                {

                    /* The queue was empty and no block time is specified (or

                    the block time has expired) so leave now. */               

                    taskEXIT_CRITICAL();

                    traceQUEUE_RECEIVE_FAILED( pxQueue );

                    return errQUEUE_EMPTY;

                }

                else if( xEntryTimeSet == pdFALSE )

                {

                    /* The queue was empty and a block time was specified so

                    configure the timeout structure. */               

                    vTaskSetTimeOutState( &xTimeOut );

                    xEntryTimeSet = pdTRUE;

                }

            }

        }

        taskEXIT_CRITICAL();



        /* Interrupts and other tasks can send to and receive from the queue

        now the critical section has been exited. */



        vTaskSuspendAll();

        prvLockQueue( pxQueue );



        /* Update the timeout state to see if it has expired yet. */

        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )

        {

            if( prvIsQueueEmpty( pxQueue ) )

            {

                traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );



                #if ( configUSE_MUTEXES == 1 )

                {

                    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )

                    {

                        portENTER_CRITICAL();

                        {

                            vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );

                        }

                        portEXIT_CRITICAL();

                    }

                }

                #endif



                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );

                prvUnlockQueue( pxQueue );

                if( !xTaskResumeAll() )

                {

                    taskYIELD();

                }

            }

            else

            {

                /* Try again. */

                prvUnlockQueue( pxQueue );

                ( void ) xTaskResumeAll();

            }

        }

        else

        {

            prvUnlockQueue( pxQueue );

            ( void ) xTaskResumeAll();

            traceQUEUE_RECEIVE_FAILED( pxQueue );

            return errQUEUE_EMPTY;

        }

    }

}


参考地址:http://www.openrtos.cn/
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
2
关闭 站长推荐上一条 /3 下一条