2014年(16)
分类: 嵌入式
2014-11-03 11:03:56
原文地址:FreeRTOS-Queue.c 队列,信号量用于任务间通讯 作者:lwchsz
队列
下载 (29.87 KB) 2009-7-23 23:20
下载 (77.37 KB) 2009-7-23 23:20
|
浅析FreeRTOS_v4.5.0队列、信号和Mutex互斥量共用函数体Queue.c文件
typedef struct QueueDefinition
{
signed char *pcHead;
signed char *pcWriteTo;
signed char *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;
} xQUEUE;
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 )
{
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1;
pxNewQueue->pcHead = ( signed char * ) pvPortMalloc( xQueueSizeInBytes );
if( pxNewQueue->pcHead != NULL )
{
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;
vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );
vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );
traceQUEUE_CREATE( pxNewQueue );
return pxNewQueue;
}
else
{
traceQUEUE_CREATE_FAILED();
vPortFree( pxNewQueue );
}
}
}
return NULL;
}
//-----------------------------------------------------------------------------------
signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition )
{
signed portBASE_TYPE xEntryTimeSet = pdFALSE;
xTimeOutType xTimeOut;
for( ;; )
{
taskENTER_CRITICAL();
{
if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
{
traceQUEUE_SEND( pxQueue );
prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE )
{
如果唤醒的任务比当前任务优先级高,进行调度。
portYIELD_WITHIN_API();
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( portTickType ) 0 )
{
没有加等待延时,直接返回。
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
vTaskSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
}
}
taskEXIT_CRITICAL();
vTaskSuspendAll();
prvLockQueue( pxQueue );
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueFull( pxQueue ) )
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
如上边提到的,当这里正要执行的时候,ISR突然到达,那么
//ISR可能把数据读出去了,所以空出了数据,但是这里还会进行队列调整
//因为ISR中发现Q已经被锁,所以Q不会在ISR中发生调度,仅仅是把数据读出去而已
//下面耗费多长时间也无所谓了,因为现在ISR硬件中断系统并没有关闭
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
上面解释:解锁queue意味着queue操作可以影响事件列表,中断(释放信号量)可以移出等待信号量任务,但是,如果此时任务调度被关闭,任务不直接放到就绪表,而是放到pending redaylist.///
prvUnlockQueue( pxQueue );
如果上面的ISR中断发生,那么本task已经被放到了Event事件双向链表上,
//下面的Unlock将使等待在Event事件队列双向链表上的最应该执行的task
//从事件队列双向链表上摘下,因为当下内核调度器被锁,所以恢复的task将被
//暂时放到PendingReady队列上
允许任务调度,移pending 到 readylist.必须进行调度。
if( !xTaskResumeAll() )
{
portYIELD_WITHIN_API();
}
}
else
{
prvUnlockQueue( pxQueue );
解锁函数,会检查在解锁期间,由中断函数send,,receive 的信号量,重新移出等待列表的任务,如果调度被禁止,任务会放到pendinglist .
( void ) xTaskResumeAll();
xTastResumeALL函数代码是:
while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ) ) != NULL )
{
vListRemove( &( pxTCB->xEventListItem ) );
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
}
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}
//-----------------------------------------------------------------------------------
signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking )
{
signed portBASE_TYPE xEntryTimeSet = pdFALSE;
xTimeOutType xTimeOut;
signed char *pcOriginalReadPosition;
for( ;; )
{
taskENTER_CRITICAL();
{
if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )
{
pcOriginalReadPosition = pxQueue->pcReadFrom;
prvCopyDataFromQueue( pxQueue, pvBuffer );
if( xJustPeeking == pdFALSE )
{
traceQUEUE_RECEIVE( pxQueue );
--( pxQueue->uxMessagesWaiting );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle();
}
}
#endif
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )
{
portYIELD_WITHIN_API();
}
}
}
else
{
traceQUEUE_PEEK( pxQueue );
pxQueue->pcReadFrom = pcOriginalReadPosition;
if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( portTickType ) 0 )
{
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else if( xEntryTimeSet == pdFALSE )
{
vTaskSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
}
}
taskEXIT_CRITICAL();
vTaskSuspendAll();
prvLockQueue( pxQueue );
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();
//2007-09-28 gliethttp
//优先级继承
//如果将要悬挂在该Q上的本task对应的优先级高于Q队列上正在持有Q使用权的pxMutexHolder对应的task的优先级
//,那么提升正在使用Q资源的pxMutexHolder对应的task的优先级到达本task的优先级
//2007-09-29 gliethttp
//低于持有mutex互斥量的task优先级的task-C不会抢占C
//高于task-C优先级的task应该抢占C
//比悬停在Q上的最高优先级task-A的优先级低的同时比持有mutex互斥量的C优先级高的task们
//才会出现优先级翻转现象
//[注:对于优先级翻转问题,可以参看《浅析μCOS/II v2.85内核OSMboxPend()和OSMboxPost()函数工作原理》
// 和《关于uC/OS-II中优先级翻转问题(转)》.文章地址:http://gliethttp.cublog.cn
//]
{
vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
}
portEXIT_CRITICAL();
}
}
#endif
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( !xTaskResumeAll() )
{
portYIELD_WITHIN_API();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
}
}
。
//-----------------------------------------------------------------------------------
static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition )
{
if( pxQueue->uxItemSize == 0 )
{
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
//2007-09-28 gliethttp
//说是恢复Q上task的优先级,倒不如说调用了mutex_unlock()
//其中xQueueReceive等效于mutex_lock();
// xQueueSend 等效于mutex_unlock();
//所以对于mutex,就没有什么Q的概念了
//任何一个对mutex发送的数据,即:xQueueSend,都会使mutex解锁,因为
//下一次肯定是等待在Q-mutex上最高优先级的task获得执行
//2007-09-29 gliethttp
//低于持有mutex互斥量的task优先级的task-C不会抢占C
//高于task-C优先级的task应该抢占C
//比悬停在Q上的最高优先级task-A的优先级低的同时比持有mutex互斥量的C优先级高的task们
//才会出现优先级翻转现象
vTaskPriorityDisinherit( ( void * const ) pxQueue->pxMutexHolder );
}
}
#endif
}
else if( xPosition == queueSEND_TO_BACK )
{
memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize );
pxQueue->pcWriteTo += pxQueue->uxItemSize;
if( pxQueue->pcWriteTo >= pxQueue->pcTail )
{
pxQueue->pcWriteTo = pxQueue->pcHead;
}
}
else
{
memcpy( ( void * ) pxQueue->pcReadFrom, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize );
pxQueue->pcReadFrom -= pxQueue->uxItemSize;
if( pxQueue->pcReadFrom < pxQueue->pcHead )
{
pxQueue->pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize );
}
}
++( pxQueue->uxMessagesWaiting );
}