简介
信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为:
- 二值信号量
- 计数型信号量
- 互斥信号量
- 递归互斥信号量
信号量常常用于控制对共享资源的访问和任务同步。信号量的实现是基于队列的,换句话说就是信号量其实就是队列的一种封装。
二值信号量
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。
和队列一样,信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一一个信号量上的话那么优先级最高的哪个任务优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空。
创建二值信号量简介
函数 | 描述 |
---|---|
vSemaphoreCreateBinary () | 动态创建二值信号量 , 这个是老版本FreeRTOS 中使用的创建二值信号量的 API 函数 |
xSemaphoreCreateBinary() | 动态创建二值信号量,新版 FreeRTOS 使用此函数创建二值信号量 |
xSemaphoreCreateBinaryStatic() | 静态创建二值信号量 |
函数 vSemaphoreCreateBinary ()
此函数是老版本 FreeRTOS 中的创建二值信号量函数,创建完成后会立刻释放一次信号量。新版本已经不再使用了,新版本的FreeRTOS 使用 xSemaphoreCreateBinary()来替代此函数,此函数是个宏, 具体创建过程是由函数xQueueGenericCreate()来完成的,在文件 semphr.h 中有如下定义。
void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
参数
- xSemaphore:保存创建成功的二值信号量句柄。
返回值
- NULL: 二值信号量创建失败。
- 其他值: 二值信号量创建成功。
函数 xSemaphoreCreateBinary()
此函数是 vSemaphoreCreateBinary()的新版本,新版本的 FreeRTOS 中统一用此函数来创建二值信号量。使用此函数创建二值信号量的话信号量所需要的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。此函数创建好的二值信号量默认是空的,也就是说刚创建好的二值信号量使用函数 xSemaphoreTake()是获取不到的,此函数也是个宏,具体创建过程是由函数xQueueGenericCreate()来完成的。
SemaphoreHandle_t xSemaphoreCreateBinary( void )
参数
- 无
返回值
- NULL: 二值信号量创建失败。
- 其他值: 创建成功的二值信号量的句柄。
函数 xSemaphoreCreateBinaryStatic()
此函数也是创建二值信号量的,只不过使用此函数创建二值信号量的话信号量所需要的RAM 需要由用户来分配,此函数是个宏,具体创建过程是通过函数 xQueueGenericCreateStatic()来完成的。
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
参数
- pxSemaphoreBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值
- NULL: 二值信号量创建失败。
- 其他值: 创建成功的二值信号量句柄。
释放信号量简介
函数 | 描述 |
---|---|
xSemaphoreGive() | 任务级信号量释放函数 |
xSemaphoreGiveFromISR() | 中断级信号量释放函数 |
同队列一样,释放信号量也分为任务级和中断级。不管是二值信号量、计数型信号量还是互斥信号量都是用上述释放信号量函数,只有递归互斥信号量采用专用的释放信号量函数。
函数 xSemaphoreGive()
此函数用于释放二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正释放信号量的过程是由函数 xQueueGenericSend()来完成的。
BaseType_t xSemaphoreGive( xSemaphore )
参数
- xSemaphore:要释放的信号量句柄。
返回值
- pdPASS: 释放信号量成功。
- errQUEUE_FULL: 释放信号量失败。
函数 xSemaphoreGiveFromISR()
此函数用于在中断中释放信号量,此函数只能用来释放二值信号量和计数型信号量,绝对不能用来在中断服务函数中释放互斥信号量!此函数是一个宏,真正执行的是函数xQueueGiveFromISR()。
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t * pxHigherPriorityTaskWoken)
参数
- xSemaphore: 要释放的信号量句柄。
- pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值
- pdPASS: 释放信号量成功。
- errQUEUE_FULL: 释放信号量失败。
获取信号量简介
函数 | 描述 |
---|---|
xSemaphoreTake() | 任务级获取信号量函数 |
xSemaphoreTakeFromISR() | 中断级获取信号量函数 |
函数 xSemaphoreTake()
此函数用于获取二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正获取信号量的过程是由函数 xQueueGenericReceive ()来完成的。
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
参数
- xSemaphore:要获取的信号量句柄。
- xBlockTime: 阻塞时间。
返回值
- pdTRUE: 获取信号量成功。
- pdFALSE: 超时,获取信号量失败。
函数 xSemaphoreTakeFromISR ()
此函数用于在中断服务函数中获取信号量,此函数用于获取二值信号量和计数型信号量,绝 对 不 能 使 用 此 函 数 来 获 取 互 斥 信 号 量 ! 此 函 数 是 一 个 宏 , 真 正 执 行 的 是 函 数xQueueReceiveFromISR ()。
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
参数
- xSemaphore: 要获取的信号量句柄。
- pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值
- pdPASS: 获取信号量成功。
- pdFALSE: 获取信号量失败。
计数型信号量
计数型信号量简介
计数型信号量通常用于如下两个场合:
- 事件计数:每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减一,信号量值就是队列结构体成员变量uxMessagesWaiting)来处理事件。在这种场合中创建的计数型信号量初始计数值为 0。
- 资源管理:信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。
计数型信号量创建简介
| 函数 | 描述 | | —- | —- | | xSemaphoreCreateCounting() | 使用动态方法创建计数型信号量 | | xSemaphoreCreateCountingStatic() | 使用静态方法创建计数型信号量 |
函数 xSemaphoreCreateCounting()
此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateCountingSemaphore()。
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
参数
- uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
- uxInitialCount: 计数信号量初始值。
返回值
- NULL: 计数型信号量创建失败。
- 其他值: 计数型信号量创建成功,返回计数型信号量句柄。
函数 xSemaphoreCreateCountingStatic()
此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存需要由用户分配。此函数也是一个宏,真正执行的是函数xQueueCreateCountingSemaphoreStatic()
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t * pxSemaphoreBuffer )
参数
- uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
- uxInitialCount: 计数信号量初始值。
- pxSemaphoreBuffer:指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值
- NULL: 计数型信号量创建失败。
- 其他值: 计数型号量创建成功,返回计数型信号量句柄。
计数型信号量的释放
函数 | 描述 |
---|---|
xSemaphoreGive() | 任务级信号量释放函数 |
xSemaphoreGiveFromISR() | 中断级信号量释放函数 |
计数型信号量的获取
函数 | 描述 |
---|---|
xSemaphoreTake() | 任务级获取信号量函数 |
xSemaphoreTakeFromISR() | 中断级获取信号量函数 |
互斥信号量
互斥信号量简介
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。
互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的优先级翻转
的影响降到最低。优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,
原因如下:
- 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
- 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
互斥信号量创建了之后,会立刻释放该信号
互斥信号量的创建简介
函数 | 描述 |
---|---|
xSemaphoreCreateMutex() | 使用动态方法创建互斥信号量 |
xSemaphoreCreateMutexStatic() | 使用静态方法创建互斥信号量 |
函数 xSemaphoreCreateMutex()
此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex()。
SemaphoreHandle_t xSemaphoreCreateMutex( void )
参数
- 无。
返回值
- NULL: 互斥信号量创建失败。
- 其他值: 创建成功的互斥信号量的句柄。
函数 xSemaphoreCreateMutexStatic()
此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的RAM 需要由用户来分配,此函数是个宏,具体创建过程是通过函数 xQueueCreateMutexStatic ()来完成的。
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
参数
- pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值
- NULL: 互斥信号量创建失败。
- 其他值: 创建成功的互斥信号量的句柄。
释放互斥信号量
函数 | 描述 |
---|---|
xSemaphoreGive() | 任务级信号量释放函数 |
获取互斥信号量
函数 | 描述 |
---|---|
xSemaphoreTake() | 任务级获取信号量函数 |
递归互斥信号量
递归互斥信号量简介
递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,而且次数不限!一个任务使用函数xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。递归互斥信号量也有优先级继承的机制,所以当任务使用完递归互斥信号量以后一定要记得释放。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中。
要使用递归互斥信号量的话宏 configUSE_RECURSIVE_MUTEXES 必须为 1!
递归互斥信号量的创建简介
函数 | 描述 |
---|---|
xSemaphoreCreateRecursiveMutex() | 使用动态方法创建递归互斥信号量 |
xSemaphoreCreateRecursiveMutexStatic() | 使用静态方法创建递归互斥信号量 |
函数 xSemaphoreCreateRecursiveMutex()
此函数用于创建一个递归互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex ()。
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
参数
- 无。
返回值
- NULL: 互斥信号量创建失败。
- 其他值: 创建成功的互斥信号量的句柄。
函数 xSemaphoreCreateRecursiveMutexStatic()
此函数也是创建递归互斥信号量的,只不过使用此函数创建递归互斥信号量的话信号量所需要的 RAM 需 要 由 用 户 来 分 配 , 此 函 数 是 个 宏 , 具 体 创 建 过 程 是 通 过 函 数xQueueCreateMutexStatic ()来完成的。
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
参数
- pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值
- NULL: 互斥信号量创建失败。
- 其他值: 创建成功的互斥信号量的句柄。
释放递归互斥信号量
递归互斥信号量有专用的释放函数:xSemaphoreGiveRecursive()。
#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
参数
- xMutex :要释放的递归互斥信号量句柄。
返回值
- pdPASS: 释放信号量成功。
- errQUEUE_FULL: 释放信号量失败。
获取递归互斥信号量
递归互斥信号量的获取使用函数 xSemaphoreTakeRecursive()。
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
参数
- xMutex:要获取的递归互斥信号量句柄。
- xTicksToWait :阻塞时间
返回值
- pdPASS: 释放信号量成功。
- errQUEUE_FULL: 释放信号量失败。