实现_信号量 - 图1

信号量概念

是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。
信号量是用作计数的,标记,不需要关心队列中的消息。

临界资源

多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。
一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机磁带机等。


二值信号量运作机制

创建信号量时,系统会为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1

计数信号量运作机制

计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目。

访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个任务访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个任务访问,那么第 4 个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务 1)释放掉该资源的时候,第 4 个任务才能获取到信号量从而进行资源的访问,
image.png

信号量控制块

  1. typedef struct QueueDefinition {
  2. int8_t *pcHead;
  3. int8_t *pcTail;
  4. int8_t *pcWriteTo;
  5. union {
  6. int8_t *pcReadFrom;
  7. UBaseType_t uxRecursiveCallCount;
  8. } u;
  9. List_t xTasksWaitingToSend;
  10. List_t xTasksWaitingToReceive;
  11. volatile UBaseType_t uxMessagesWaiting; //(1) 用来记录当前消息队列的消息个数;
  12. UBaseType_t uxLength; (2)
  13. UBaseType_t uxItemSize; (3)
  14. volatile int8_t cRxLock;
  15. volatile int8_t cTxLock;
  16. #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
  17. uint8_t ucStaticallyAllocated;
  18. #endif
  19. #if ( configUSE_QUEUE_SETS == 1 )
  20. struct QueueDefinition *pxQueueSetContainer;
  21. #endif
  22. #if ( configUSE_TRACE_FACILITY == 1 )
  23. UBaseType_t uxQueueNumber;
  24. uint8_t ucQueueType;
  25. #endif
  26. } xQUEUE;
  27. typedef xQUEUE Queue_t;

(2):如果控制块结构体是用于消息队列:uxLength 表示队列的长度, 也就是能存放多少消息;
如果控制块结构体被用于信号量的时候,uxLength 表示最大的信号量可用个数,会有以下两种情况:

  • 如果信号量是二值信号量、互斥信号量,uxLength 最大为 1,因为信号量要么是有效的,要么是无效的。
  • 如果是计数信号量,这个值表示最大的信号量个数,在创建计数信号量的时候将由用户指定这个值 uxMaxCount。

(3):如果控制块结构体是用于消息队列:uxItemSize 表示单个消息的大小;
如果控制块结构体被用于信号量的时候,则无需存储空间,为 0 即可。

常用信号量函数接口讲解

实现_信号量 - 图3

xSemaphoreCreateBinary()

  1. #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
  2. #define xSemaphoreCreateBinary() \ 4 xQueueGenericCreate( \
  3. ( UBaseType_t ) 1, \ (1)
  4. semSEMAPHORE_QUEUE_ITEM_LENGTH, \ (2) 7 queueQUEUE_TYPE_BINARY_SEMAPHORE ) (3)
  5. #endif
  1. QueueHandle_t xQueueGenericCreate(const UBaseType_t uxQueueLength,
  2. const UBaseType_t uxItemSize,
  3. const uint8_t ucQueueType )

xSemaphoreCreateCounting()

image.png

  1. #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
  2. #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
  3. xQueueCreateCountingSemaphore((uxMaxCount),(uxInitialCount))
  4. #endif
  5. //下面是函数源码
  6. #if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
  7. QueueHandle_t xQueueCreateCountingSemaphore(
  8. const UBaseType_t uxMaxCount,
  9. const UBaseType_t uxInitialCount )
  10. {
  11. QueueHandle_t xHandle;
  12. configASSERT( uxMaxCount != 0 );
  13. configASSERT( uxInitialCount <= uxMaxCount );
  14. xHandle = xQueueGenericCreate( uxMaxCount,
  15. queueSEMAPHORE_QUEUE_ITEM_LENGTH,
  16. queueQUEUE_TYPE_COUNTING_SEMAPHORE );
  17. if ( xHandle != NULL )
  18. {
  19. ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
  20. traceCREATE_COUNTING_SEMAPHORE();
  21. }
  22. else
  23. {
  24. traceCREATE_COUNTING_SEMAPHORE_FAILED();
  25. }
  26. return xHandle;
  27. }
  28. #endif
  29. /*-----------------------------------------------------------*/

实例:创建二值信号量

  1. SemaphoreHandle_t xSemaphore = NULL;
  2. 2
  3. 3
  4. 4 void vATask( void * pvParameters )
  5. 5 {
  6. 6 /* 尝试创建一个信号量 */
  7. 7 xSemaphore = xSemaphoreCreateBinary();
  8. 8
  9. 9 if ( xSemaphore == NULL ) {
  10. 10 /* 内存不足,创建失败 */
  11. 11 } else {
  12. 12 /* 信号量现在可以使用,句柄存在变量 xSemaphore 中
  13. 13 这个时候还不能调用函数 xSemaphoreTake()来获取信号量
  14. 14 因为使用 xSemaphoreCreateBinary()函数创建的信号量是空的
  15. 15 在第一次获取之前必须先调用函数 xSemaphoreGive()先提交*/
  16. 16 } 17}

实例:创建计数信号量

  1. 1 void vATask( void * pvParameters )
  2. 2 {
  3. 3 SemaphoreHandle_t xSemaphore;
  4. 4 /* 创建一个计数信号量, 用于事件计数 */
  5. 5 xSemaphore = xSemaphoreCreateCounting( 5, 5 );
  6. 6
  7. 7 if ( xSemaphore != NULL ) {
  8. 8 /* 计数信号量创建成功 */
  9. 9 }

vSemaphoreDelete()

image.png
image.png

  1. 1 #define vSemaphoreDelete( xSemaphore ) \
  2. 2 vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )

xSemaphoreGive()

  1. #define xSemaphoreGive( xSemaphore ) \
  2. 2 xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \
  3. 3 NULL, \
  4. 4 semGIVE_BLOCK_TIME, \
  5. 5 queueSEND_TO_BACK )

image.png

xSemaphoreGiveFromISR()(中断)

image.png
image.png

xSemaphoreTake()

image.png
image.png
实际上是一次消息出队操作,阻塞时间由用户指定xBlockTime,
当有任务试图获取信号量的时候,当且仅当信号量有效的时候,任务才能读获取到信号量。
如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量有效。
image.png

xSemaphoreTakeFromISR()(中断)

image.png