浅析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 ); }
|