Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15308960
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类:

2007-10-07 19:02:54

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

阅读(8578) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

Annazss2015-12-17 14:28:51

__asm uint32_t ulSetInterruptMaskFromISR( void )
{
 mrs r0, PRIMASK //?á??r0????·???
 cpsid i //disable interrupt
 bx lr //??????????
}
/*-----------------------------------------------------------*/

__asm void vClearInterruptMaskFromISR( uint32_t ulMask )
{
 msr PRIMASK, r0 //??ulMask????????PRIMASK
 bx lr
}

Annazss2015-12-17 14:17:32

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();//??PRIMASK??±???????NMI??hard fault???ù???????ì??
    uxCriticalNesting++;
 __dsb( portSY_FULL_READ_WRITE );
 __isb( portSY_FULL_READ_WRITE );//????????????????
}
/*-----------------------------------------------------------*/

void vPortExitCritical( void )
{
 configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
 &nb

Annazss2015-12-17 14:17:27

请教博主一个问题,FreeRTOS里所有中断里使用的API进出critical section使用的都是portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR(),代替了taskENTER_CRITICAL()和taskEXIT_CRITICAL(),定义如下,请问它们的区别在哪呢?如果混淆使用有什么后果呢?请楼主不吝赐教,感谢感谢!