浅析FreeRTOS_v4.5.0队列、信号和Mutex互斥量共用函数体Queue.c文件
文章来源:http://gliethttp.cublog.cn[转载请声明出处]
//-----------------------------------------------------------------------------------
signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition )
{
signed portBASE_TYPE xReturn = pdPASS;
xTimeOutType xTimeOut;
//2007-09-30 gliethttp
//锁住调度器,不发生线程切换
vTaskSuspendAll();
vTaskSetTimeOutState( &xTimeOut );
//2007-09-30 gliethttp
//锁住队列,不让ISR对睡眠在list队列上的task进行唤醒操作,但是可以对队列数据进行操作
//要保证对这些全局量的修改地方具有唯一性,否则两个地方都去修改同一个变量,
//那么会出现数据混乱
prvLockQueue( pxQueue );
do
{
if( prvIsQueueFull( pxQueue ) )
{
if( xTicksToWait > ( portTickType ) 0 )
{
//2007-09-30 gliethttp
//正如上边提到的,当这里正要执行的时候,ISR突然到达,那么
//ISR可能把数据读出去了,所以空出了数据,但是这里还会进行队列调整
//因为ISR中发现Q已经被锁,所以Q不会在ISR中发生调度,仅仅是把数据读出去而已
//下面耗费多长时间也无所谓了,因为现在ISR硬件中断系统并没有关闭
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
taskENTER_CRITICAL();
{
//2007-09-30 gliethttp
//如果上面的ISR中断发生,那么本task已经被放到了Event事件双向链表上,
//下面的Unlock将使等待在Event事件队列双向链表上的最应该执行的task
//从事件队列双向链表上摘下,因为当下内核调度器被锁,所以恢复的task将被
//暂时放到PendingReady队列上
prvUnlockQueue( pxQueue );
if( !xTaskResumeAll() )
{
taskYIELD();
}
//2007-09-30 gliethttp
//可能确实task已经被Event事件唤醒,并且被置入了就绪运行队列,但是因为
//和他同优先级的其他task的原因,自己还没有获得cpu,而就在快要轮转到自己持有
//cpu的时候,一个高优先级的task可能又把Q填满了,
//之后本task被轮转到,获得了cpu,进而执行下面的语句
if( pxQueue->uxMessagesWaiting == pxQueue->uxLength )
{
xReturn = errQUEUE_FULL;
}
vTaskSuspendAll();
//因为加入了lock,所以ISR中的程序不会进行Q队列操作
prvLockQueue( pxQueue );
}
taskEXIT_CRITICAL();
}
}
// 1.因为Q已经被锁住,所以在ISR中不会处理等待在Q队列上的task,但是会对Q上的数据进行操作
// 2.调度器已经上锁,不会发生调度,本task肯定会继续执行下面的代码
if( xReturn != errQUEUE_FULL )
{
taskENTER_CRITICAL();
{
//可能发生的ISR,又把Q填满了
if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
{
prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
xReturn = pdPASS;
++( pxQueue->xTxLock );
}
else
{
xReturn = errQUEUE_FULL;
}
}
taskEXIT_CRITICAL();
}
if( xReturn == errQUEUE_FULL )
{
if( xTicksToWait > 0 )
{
//2007-09-30 gliethttp
// 1.确实因为Recve事件唤醒了本task,但是在执行上面的非原子操作的过程中,
// if( xReturn != errQUEUE_FULL )的汇编代码之间,可能ISR又将Q填满了
// 如果是这样,那么TimeOut一定还没有到达,所以需要继续在时间链表上挂起自己,继续等待
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
xReturn = queueERRONEOUS_UNBLOCK;
}
}
}
}
while( xReturn == queueERRONEOUS_UNBLOCK );
prvUnlockQueue( pxQueue );
xTaskResumeAll();
return xReturn;
}
//-----------------------------------------------------------------------------------
signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking )
{
signed portBASE_TYPE xReturn = pdTRUE;
xTimeOutType xTimeOut;
signed portCHAR *pcOriginalReadPosition;
vTaskSuspendAll();
vTaskSetTimeOutState( &xTimeOut );
prvLockQueue( pxQueue );
do
{
if( prvIsQueueEmpty( pxQueue ) )
{
if( xTicksToWait > ( portTickType ) 0 )
{
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
//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
//]
portENTER_CRITICAL();
vTaskPriorityInherit( ( void * const ) pxQueue->pxMutexHolder );
portEXIT_CRITICAL();
}
}
#endif
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
taskENTER_CRITICAL();
{
prvUnlockQueue( pxQueue );
if( !xTaskResumeAll() )
{
taskYIELD();
}
//2007-09-29 gliethttp
//可能确实task已经被Event事件唤醒,并且被置入了就绪运行队列,但是因为
//和他同优先级的其他task的原因,自己还没有获得cpu,而就在快要轮转到自己持有
//cpu的时候,一个高优先级的task可能把Q中数据给读走了,所以Q又空了,
//之后本task被轮转到,获得了cpu,进而执行下面的语句
if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )
{
xReturn = errQUEUE_EMPTY;
}
vTaskSuspendAll();
prvLockQueue( pxQueue );
}
taskEXIT_CRITICAL();
}
}
if( xReturn != errQUEUE_EMPTY )
{
taskENTER_CRITICAL();
{
if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )
{
pcOriginalReadPosition = pxQueue->pcReadFrom;
prvCopyDataFromQueue( pxQueue, pvBuffer );
//2007-09-29
//xJustPeeking=true表示仅仅偷偷的数据读出来,就像小偷一样,它还会花费
//下面的功夫来恢复现场
if( xJustPeeking == pdFALSE )
{
--( pxQueue->uxMessagesWaiting );
++( pxQueue->xRxLock );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
//2007-09-29 gliethttp
//mutex_lock();mutex_unlock()会比较清晰一些,当Q用作mutex功能时,就不是Q了,就像
//------------------------------------------------------
//vSemaphoreCreateBinary
//xSemaphoreTake
//xSemaphoreGive
//xSemaphoreGiveFromISR
//xSemaphoreCreateMutex
//就像Semaphore函数一样,可以给他们取个名字为
//xMutexCreatexxxxxxxxx
//xMutexTake
//xMutexGive
//xMutexGiveFromISR
//xMutexCreate
//------------------------------------------------------
//但是实际上Mutex功能是为Semaphore信号量提供的防止信号量保护的内容出现优先级翻转
//的基于优先级继承的互斥机制
//其中xQueueReceive等效于mutex_lock();
// xQueueSend 等效于mutex_unlock();
//pxMutexHolder对应的是正在使用本Q队列的task上下文句柄
//如果某一个时刻因为,优先级高于本task的另一个task被阻塞在这个Q上的时候
//那么当前拥有该Q运行权利的本task的优先级将会被提升到被阻塞的task对应的优先级
//2007-09-29 gliethttp
//低于持有mutex互斥量的task优先级的task-C不会抢占C
//高于task-C优先级的task应该抢占C
//比悬停在Q上的最高优先级task-A的优先级低的同时比持有mutex互斥量的C优先级高的task们
//才会出现优先级翻转现象
//本task持有互斥锁
pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle();
}
}
#endif
}
else
{
//2007-09-29 gliethttp
//如果不是从Q上读取数据,那么这次唤醒的等待在Q上的task等于没有唤醒,
//所以下面++( pxQueue->xTxLock ),再去唤醒一个等待在Q上的task
pxQueue->pcReadFrom = pcOriginalReadPosition;
++( pxQueue->xTxLock );
}
xReturn = pdPASS;
}
else
{
xReturn = errQUEUE_EMPTY;
}
}
taskEXIT_CRITICAL();
}
if( xReturn == errQUEUE_EMPTY )
{
if( xTicksToWait > 0 )
{
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
xReturn = queueERRONEOUS_UNBLOCK;
}
}
}
} while( xReturn == queueERRONEOUS_UNBLOCK );
prvUnlockQueue( pxQueue );
xTaskResumeAll();
return xReturn;
}
//-----------------------------------------------------------------------------------
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 );
}