信号量用于限制对共享资源的访问和多任务之间的同步。三个信号量API函数都是宏,使用现有的队列实现。
使用例子:
typedef void * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;
SemaphoreHandle_t mutex_sema;
mutex_sema = xSemaphoreCreateMutex();
xSemaphoreTake(mutex_sema, portMAX_DELAY);
/* 对共享资源的操作 */
xSemaphoreGive(mutex_sema);
代码:
/*
* xSemaphoreCreateMutex
*/
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); // 创建队列,注意参数.
prvInitialiseMutex( pxNewQueue ); // 初始化刚创建的普通队列为互斥体
return pxNewQueue;
}
static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
if( pxNewQueue != NULL )
{
/* The queue create function will set all the queue structure members
correctly for a generic queue, but this function is creating a
mutex. Overwrite those members that need to be set differently -
in particular the information required for priority inheritance. */
/* 队列创建函数将为通用队列正确设置所有队列结构成员,但此函数正在创建互斥体。
覆盖那些需要以不同方式设置的成员-特别是优先级继承所需的信息 */
// #define pxMutexHolder pcTail
// #define uxQueueType pcHead
pxNewQueue->pxMutexHolder = NULL;
pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
/* In case this is a recursive mutex. */
pxNewQueue->u.uxRecursiveCallCount = 0;
traceCREATE_MUTEX( pxNewQueue );
/* Start with the semaphore in the expected state. */
/* 从处于预期状态的信号量开始 */
( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
}
else
{
traceCREATE_MUTEX_FAILED();
}
}
/*
* xSemaphoreTake
*/
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
#if( configUSE_MUTEXES == 1 )
BaseType_t xInheritanceOccurred = pdFALSE;
#endif
/* Check the queue pointer is not NULL. */
configASSERT( ( pxQueue ) );
/* Check this really is a semaphore, in which case the item size will be
0. 检查这是否是一个信号量,在这种情况下,项目大小将为0 */
configASSERT( pxQueue->uxItemSize == 0 );
/* Cannot block if the scheduler is suspended. 如果计划程序已挂起,则无法阻止 */
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
/* This function relaxes the coding standard somewhat to allow return
statements within the function itself. This is done in the interest
of execution time efficiency. */
/* 这个函数在一定程度上放宽了编码标准,允许函数本身内部有返回语句。这是为了提高执行时间效率 */
for( ;; )
{
taskENTER_CRITICAL();
{
/* Semaphores are queues with an item size of 0, and where the
number of messages in the queue is the semaphore's count value. */
/* 信号量是项目大小为0的队列,其中队列中的消息数是信号量的计数值 */
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
/* Is there data in the queue now? To be running the calling task
must be the highest priority task wanting to access the queue. */
/* 队列中现在有数据吗?要运行调用任务,必须是要访问队列的最高优先级任务 */
if( uxSemaphoreCount > ( UBaseType_t ) 0 )
{
traceQUEUE_RECEIVE( pxQueue );
/* Semaphores are queues with a data size of zero and where the
messages waiting is the semaphore's count. Reduce the count. */
/* 信号量是数据大小为零的队列,其中等待的消息是信号量的计数。减少计数 */
pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/* Record the information required to implement
priority inheritance should it become necessary. */
/* 如果有必要,记录实现优先级继承所需的信息 */
pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
/* Check to see if other tasks are blocked waiting to give the
semaphore, and if so, unblock the highest priority such task. */
/* 检查是否有其他任务在等待发出信号量时被阻塞,如果是,请取消阻止优先级最高的任务 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/* For inheritance to have occurred there must have been an
initial timeout, and an adjusted timeout cannot become 0, as
if it were 0 the function would have exited. */
/* 要使继承发生,必须有一个初始超时,并且调整后的超时不能变为0,如果它为0,函数将退出 */
#if( configUSE_MUTEXES == 1 )
{
configASSERT( xInheritanceOccurred == pdFALSE );
}
#endif /* configUSE_MUTEXES */
/* The semaphore count was 0 and no block time is specified
(or the block time has expired) so exit now. */
/* 信号量计数为0,并且未指定块时间(或块时间已过期),因此请立即退出 */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else if( xEntryTimeSet == pdFALSE )
{
/* The semaphore count was 0 and a block time was specified
so configure the timeout structure ready to block. */
/* 信号量计数为0,并且指定了阻塞时间,因此请配置超时结构以准备阻塞 */
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
/* Entry time was already set. */
/* 已设置进入时间 */
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();
/* Interrupts and other tasks can give to and take from the semaphore
now the critical section has been exited. */
/* 中断和其他任务可以对信号量进行交换,现在关键部分已经退出 */
vTaskSuspendAll();
prvLockQueue( pxQueue );
/* Update the timeout state to see if it has expired yet. */
/* 更新超时状态以查看它是否已过期 */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
/* A block time is specified and not expired. If the semaphore
count is 0 then enter the Blocked state to wait for a semaphore to
become available. As semaphores are implemented with queues the
queue being empty is equivalent to the semaphore count being 0. */
/* 块时间已指定且未过期。如果信号量计数为0,则进入阻塞状态,等待信号量变为可用。
由于信号量是用队列实现的,因此队列为空相当于信号量计数为0 */
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER_CRITICAL();
{
xInheritanceOccurred = xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* There was no timeout and the semaphore count was not 0, so
attempt to take the semaphore again. */
/* 没有超时,信号量计数不为0,因此请尝试再次获取该信号量 */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
/* Timed out. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
/* If the semaphore count is 0 exit now as the timeout has
expired. Otherwise return to attempt to take the semaphore that is
known to be available. As semaphores are implemented by queues the
queue being empty is equivalent to the semaphore count being 0. */
/* 如果信号量计数为0,则立即退出,因为超时已过期。否则返回尝试获取已知可用的信号量。
由于信号量是由队列实现的,因此队列为空就等于信号量计数为0 */
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
#if ( configUSE_MUTEXES == 1 )
{
/* xInheritanceOccurred could only have be set if
pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to
test the mutex type again to check it is actually a mutex. */
/* 只有当pxQueue->uxQueueType==queueQUEUE_IS_MUTEX时,才能设置xinHeritanceOccurrence,
因此无需再次测试互斥体类型以检查它是否是互斥体 */
if( xInheritanceOccurred != pdFALSE )
{
taskENTER_CRITICAL();
{
UBaseType_t uxHighestWaitingPriority;
/* This task blocking on the mutex caused another
task to inherit this task's priority. Now this task
has timed out the priority should be disinherited
again, but only as low as the next highest priority
task that is waiting for the same mutex. */
/* 互斥体上的此任务阻塞导致另一个任务继承此任务的优先级。现在这个任务已经超时了,
应该再次取消优先级继承,但只能低到等待同一互斥锁的下一个最高优先级任务 */
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
vTaskPriorityDisinheritAfterTimeout( ( void * ) pxQueue->pxMutexHolder, uxHighestWaitingPriority );
}
taskEXIT_CRITICAL();
}
}
#endif /* configUSE_MUTEXES */
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
/*
* xSemaphoreGive
*/
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
/* This function relaxes the coding standard somewhat to allow return
statements within the function itself. This is done in the interest
of execution time efficiency. */
/* 这个函数在一定程度上放宽了编码标准,允许函数本身内部有返回语句。这是为了提高执行时间效率 */
for( ;; )
{
taskENTER_CRITICAL();
{
/* Is there room on the queue now? The running task must be the
highest priority task wanting to access the queue. If the head item
in the queue is to be overwritten then it does not matter if the
queue is full. */
/* 现在排队还有空吗?正在运行的任务必须是要访问队列的最高优先级任务。如果要覆盖队列中的头项,那么队列是否已满并不重要 */
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
traceQUEUE_SEND( pxQueue );
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
#if ( configUSE_QUEUE_SETS == 1 )
{
if( pxQueue->pxQueueSetContainer != NULL )
{
if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
{
/* The queue is a member of a queue set, and posting
to the queue set caused a higher priority task to
unblock. A context switch is required. */
/* 队列是队列集的成员,投递到队列集中会导致优先级较高的任务取消阻止。需要上下文切换 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* If there was a task waiting for data to arrive on the
queue then unblock it now. */
/* 如果有一个任务正在等待数据到达队列,请立即取消阻止它 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The unblocked task has a priority higher than
our own so yield immediately. Yes it is ok to
do this from within the critical section - the
kernel takes care of that. */
/* 解除封锁的任务比我们的任务有更高的优先级,所以立刻让步。是的,在临界区内这样做是可以的-内核负责处理这个问题 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
/* This path is a special case that will only get
executed if the task was holding multiple mutexes
and the mutexes were given back in an order that is
different to that in which they were taken. */
/* 此路径是一种特殊情况,只有当任务持有多个互斥锁,并且互斥锁的返回顺序与它们的获取顺序不同时,才会执行该路径 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#else /* configUSE_QUEUE_SETS */
{
/* If there was a task waiting for data to arrive on the
queue then unblock it now. */
/* 如果有一个任务正在等待数据到达队列,请立即取消阻止它 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The unblocked task has a priority higher than
our own so yield immediately. Yes it is ok to do
this from within the critical section - the kernel
takes care of that. */
/* 解除封锁的任务比我们的任务有更高的优先级,所以立刻让步。是的,在临界区内这样做是可以的-内核负责处理这个问题 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
/* This path is a special case that will only get
executed if the task was holding multiple mutexes and
the mutexes were given back in an order that is
different to that in which they were taken. */
/* 此路径是一种特殊情况,只有当任务持有多个互斥锁,并且互斥锁的返回顺序与它们的获取顺序不同时,才会执行该路径 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/* The queue was full and no block time is specified (or
the block time has expired) so leave now. */
/* 队列已满,未指定块时间(或块时间已过期),请立即离开 */
taskEXIT_CRITICAL();
/* Return to the original privilege level before exiting
the function. */
/* 在退出函数之前返回到原始权限级别 */
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
/* The queue was full and a block time was specified so
configure the timeout structure. */
/* 队列已满,并且指定了块时间,因此请配置超时结构 */
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
/* Entry time was already set. */
/* 已设置进入时间 */
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();
/* Interrupts and other tasks can send to and receive from the queue
now the critical section has been exited. */
/* 中断和其他任务可以发送到队列或从队列接收,现在关键部分已经退出 */
vTaskSuspendAll();
prvLockQueue( pxQueue );
/* Update the timeout state to see if it has expired yet. */
/* 更新超时状态以查看它是否已过期 */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
/* Unlocking the queue means queue events can effect the
event list. It is possible that interrupts occurring now
remove this task from the event list again - but as the
scheduler is suspended the task will go onto the pending
ready last instead of the actual ready list. */
/* 解锁队列意味着队列事件可以影响事件列表。现在发生的中断可能会再次将此任务从事件列表中删除-但是由于调度程序被挂起,
任务将转到挂起的就绪最后一个,而不是实际的就绪列表 */
prvUnlockQueue( pxQueue );
/* Resuming the scheduler will move tasks from the pending
ready list into the ready list - so it is feasible that this
task is already in a ready list before it yields - in which
case the yield will not cause a context switch unless there
is also a higher priority task in the pending ready list. */
/* 恢复调度程序会将任务从挂起的就绪列表移动到就绪列表中—因此,此任务在其生成之前已经在就绪列表中是可行的—在这种情况下,
除非挂起的就绪列表中还有更高优先级的任务,否则产生的结果不会导致上下文切换 */
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
else
{
/* Try again. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
/* The timeout has expired. */
/* 超时已过期 */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}