二值信号量通过下面的宏来创建,从此宏的实现可以看出二值信号量实质上是一个长度为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;
}
文章评论(0条评论)
登录后参与讨论