打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
FreeRTOS信号量初探

二值信号量通过下面的宏来创建,从此宏的实现可以看出二值信号量实质上是一个长度为1,每个条目的大小为0的队列
(semSEMAPHORE_QUEUE_ITEM_LENGTH=0)。信号量创建后紧接着调用xSemaphoreGive,使信号量值为1(实际上
是增加了uxMessagesWaiting的值,下面将分析其实现)。

#define
vSemaphoreCreateBinary( xSemaphore )       
{                                                                                               
\

                                                       
xSemaphore = xQueueCreate( ( unsigned portBASE_TYPE ) 1,
semSEMAPHORE_QUEUE_ITEM_LENGTH );    \

                                                       
if( xSemaphore != NULL
)                                                                    \

                                                       
{                                                                                           
\

                                                           
xSemaphoreGive( xSemaphore
);                                                            \

                                                       
}                                                                                           
\

                                                    }




计数信号量的实现与二值信号量类似,所不同的是有两个参数,uxCountValue为计数信号量的最大值,uxInitialCount为初始值,创建返回的是信号量的句柄,函数CreateCountingSemaphore的实现如下:

xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount )

    {

    xQueueHandle pxHandle;

        /*对队列进行初始化操作后,队列的长度为信号量的最大值,
uxMessagesWaiting初始化为信号量的初始值*/

        pxHandle = xQueueCreate( ( unsigned portBASE_TYPE ) uxCountValue, queueSEMAPHORE_QUEUE_ITEM_LENGTH );



        if( pxHandle != NULL )

        {

            pxHandle->uxMessagesWaiting = uxInitialCount;



            traceCREATE_COUNTING_SEMAPHORE();

        }

        else

        {

            traceCREATE_COUNTING_SEMAPHORE_FAILED();

        }



        return pxHandle;

    }


互斥锁跟上面的两种信号量不一样,它通过pxMutexHolder来指向其所有者的tcb,它的长度为1,同时使用uxRecursiveCallCount来记录其所有者获取此互斥锁的次数,互斥锁还有一个特性就是具有优先级继承机制,当前任务请求获取互斥锁时,如果互斥锁已经被另一个任务获取,当前任务会把已获取互斥锁的任务优先级提升到与自己一致,用来减少优先级翻转情况的出现。互斥锁的创建如下:

几个相关的宏定义:

#define pxMutexHolder                pcTail

#define uxQueueType                    pcHead

#define uxRecursiveCallCount        pcReadFrom

#define queueQUEUE_IS_MUTEX            NULL




xQueueHandle xQueueCreateMutex( void )

    {

    xQUEUE *pxNewQueue;



        /* Allocate the new queue structure. */

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

        if( pxNewQueue != NULL )

        {

            /* Information required for priority inheritance. */

            pxNewQueue->pxMutexHolder = NULL;

            pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;



            /* Queues used as a mutex no data is actually copied into or out

            of the queue. */

            pxNewQueue->pcWriteTo = NULL;

            pxNewQueue->pcReadFrom = NULL;



            /* Each mutex has a length of 1 (like a binary semaphore) and

            an item size of 0 as nothing is actually copied into or out

            of the mutex. */

            pxNewQueue->uxMessagesWaiting = 0;

            pxNewQueue->uxLength = 1;

            pxNewQueue->uxItemSize = 0;

            pxNewQueue->xRxLock = queueUNLOCKED;

            pxNewQueue->xTxLock = queueUNLOCKED;



            /* Ensure the event queues start with the correct state. */

            vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );

            vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );



            /* Start with the semaphore in the expected state. */

            xQueueGenericSend( pxNewQueue, NULL, 0, queueSEND_TO_BACK );



            traceCREATE_MUTEX( pxNewQueue );

        }

        else

        {

            traceCREATE_MUTEX_FAILED();

        }



        return pxNewQueue;

    }




信号量实在队列的基础上实现的,而信号量的获取与释放也是通过队列对应的接收与发送函数来实现,这三个函数适用与上面介绍的三种信号量类型。其宏定义如下:

#define
xSemaphoreTake( xSemaphore, xBlockTime )        xQueueGenericReceive( (
xQueueHandle ) xSemaphore, NULL, xBlockTime, pdFALSE )

#define
xSemaphoreGive( xSemaphore )        xQueueGenericSend( ( xQueueHandle )
xSemaphore, NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

#define
xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )       
    xQueueGenericSendFromISR( ( xQueueHandle ) xSemaphore, NULL,
pxHigherPriorityTaskWoken, queueSEND_TO_BACK )


获取信号量可以导致任务挂起,而释放信号量是无论是否成功均会立刻返回。需要注意的是对于前面两个函数,当对互斥锁进行操作时会进行优先级继承,在获取互斥锁时通过调用vTaskPriorityInherit函数来提升互斥锁拥有者的优先级到当前任务的优先级,当释放互斥锁时也会对应的检查任务是否被提升过优先级,并进行恢复,通过调用vTaskPriorityDisinherit函数,这两个函数的实现如下:


/*如果被提升优先级的任务在就绪列表中,则先从就绪列表中移出,提升优先级后再插入;如果此任务没有就绪运行,则简单的提升优先级*/

void vTaskPriorityInherit( xTaskHandle * const pxMutexHolder )

    {

    tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder;



        if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )

        {

            /* Adjust the mutex holder state to account for its new priority. */

   
        listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ),
configMAX_PRIORITIES - ( portTickType ) pxCurrentTCB->uxPriority );



            /* If the task being modified is in the ready state it will need to

            be moved in to a new list. */

   
        if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[
pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ) )

            {

                vListRemove( &( pxTCB->xGenericListItem ) );



                /* Inherit the priority before being moved into the new list. */

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;

                prvAddTaskToReadyQueue( pxTCB );

            }

            else

            {

                /* Just inherit the priority. */

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;

            }

        }

    }



/*检查当前的优先级是否跟另一个副本相等,如果不等则表示被提升过优先级,就要恢复*/

void vTaskPriorityDisinherit( xTaskHandle * const pxMutexHolder )

    {

    tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder;



        if( pxMutexHolder != NULL )

        {

            if( pxTCB->uxPriority != pxTCB->uxBasePriority )

            {

                /* We must be the running task to be able to give the mutex back.

                Remove ourselves from the ready list we currently appear in. */

                vListRemove( &( pxTCB->xGenericListItem ) );



                /* Disinherit the priority before adding ourselves into the new

                ready list. */

                pxTCB->uxPriority = pxTCB->uxBasePriority;

   
            listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ),
configMAX_PRIORITIES - ( portTickType ) pxTCB->uxPriority );

                prvAddTaskToReadyQueue( pxTCB );

            }

        }

    }


对于互斥锁还有递归的获取与发送函数,为xQueueTakeMutexRecursive与xQueueGiveMutexRecursive,分别对uxRecursiveCallCount进行加与减的操作,调用xQueueTakeMutexRecursive时如果任务还没有获取互斥锁,就会先进行获取操作,调用xQueueGiveMutexRecursive时如果uxRecursiveCallCount的值变为0,就会释放互斥锁。此两个函数的实现如下:

portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle pxMutex, portTickType xBlockTime )

    {

    portBASE_TYPE xReturn;



        /* Comments regarding mutual exclusion as per those within

        xQueueGiveMutexRecursive(). */



        traceTAKE_MUTEX_RECURSIVE( pxMutex );



        if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() )

        {

            ( pxMutex->uxRecursiveCallCount )++;

            xReturn = pdPASS;

        }

        else

        {

            xReturn = xQueueGenericReceive( pxMutex, NULL, xBlockTime, pdFALSE );



            /* pdPASS will only be returned if we successfully obtained the mutex,

            we may have blocked to reach here. */

            if( xReturn == pdPASS )

            {

                ( pxMutex->uxRecursiveCallCount )++;

            }

        }



        return xReturn;

    }


portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle pxMutex )

    {

    portBASE_TYPE xReturn;



        /* If this is the task that holds the mutex then pxMutexHolder will not

        change outside of this task.  If this task does not hold the mutex then

        pxMutexHolder can never coincidentally equal the tasks handle, and as

        this is the only condition we are interested in it does not matter if

        pxMutexHolder is accessed simultaneously by another task.  Therefore no

        mutual exclusion is required to test the pxMutexHolder variable. */

        if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() )

        {

            traceGIVE_MUTEX_RECURSIVE( pxMutex );



            /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to

            the task handle, therefore no underflow check is required.  Also,

            uxRecursiveCallCount is only modified by the mutex holder, and as

            there can only be one, no mutual exclusion is required to modify the

            uxRecursiveCallCount member. */

            ( pxMutex->uxRecursiveCallCount )--;



            /* Have we unwound the call count? */

            if( pxMutex->uxRecursiveCallCount == 0 )

            {

                /* Return the mutex.  This will automatically unblock any other

                task that might be waiting to access the mutex. */

                xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );

            }



            xReturn = pdPASS;

        }

        else

        {

            /* We cannot give the mutex because we are not the holder. */

            xReturn = pdFAIL;



            traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );

        }



        return xReturn;

    }


关于互斥锁的一点思考


果任务A在请求互斥锁时把任务B的优先级提升到a,若在任务B还拥有互斥锁的进行操作的过程中,具有更高优先级的C被唤醒,同时又请求获取同一个互斥锁,
则B的优先级又被提升到c,当B释放互斥锁时,其原来的优先级b已经丢失,变成了A的优先级a了。因为还没有做过实验,也不知道这种情况是否会发生以及发
生的概率有多大。

网址:http://www.openrtos.cn
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
FreeRTOS任务切换解析
FreeRTOS高级篇2
FreeRTOS --(11)任务管理之系统节拍
FreeRTOS学习笔记
关于嵌入式实时操作系统的实时性
干货 | FreeRTOS学习笔记——任务状态及切换
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服