信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步, FreeRTOS中信号量又分为二值信号量、 计数型信号量、互斥信号量和递归互斥信号量。不同的信号量其应用场景不同,但有些应用场景是可以互换着使用的
信号量用于控制共享资源访问的场景相当于一个上锁机制, 代码只有获得了这个锁的钥匙才能够执行。
信号量的另一个重要的应用场合就是任务同步,用于任务与任务或中断与任务之间的同步。 在执行中断服务函数的时候可以通过向任务发送信号量来通知任务它所期待的事件发生了, 当退出中断服务函数以后在任务调度器的调度下同步的任务就会执行。在编写中断服务函数的时候我们都知道一定要快进快出,中断服务函数里面不能放太多的代码,否则的话会影响的中断的实时性。 裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记,然后在其他的地方根据标记来做具体的处理过程。在使用 RTOS 系统的时候我们就可以借助信号量完成此功能, 当中断发生的时候就释放信号量,中断服务函数不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,那么就开始完成相应的处理,这样做的好处就是中断执行时间非常短。 这个例子就是中断与任务之间使用信号量来完成同步,当然了, 任务与任务之间也可以使用信号量来完成同步。
一、二值信号量
二值信号量通常用于互斥访问或同步, 二值信号量和互斥信号量非常类似,但是互斥信号量拥有优先级继承机制, 二值信号量没有优先级继承。 因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。
使用二值信号量来完成中断与任务同步的这个机制中,任务优先级确保了外设能够得到及时的处理,这样做相当于推迟了中断处理过程。 也可以使用队列来替代二值信号量,在外设事件的中断服务函数中获取相关数据,并将相关的数据通过队列发送给任务。如果队列无效的话任务就进入阻塞态,直至队列中有数据,任务接收到数据以后就开始相关的处理过程。
二值信号量的使命就是同步,完成任务与任务或中断与任务之间的同步。大多数情况下都是中断与任务之间的同步
1.1、创建二值信号量
1、函数 xSemaphoreCreateBinary()
使用此函数创建二值信号量的话信号量所需要的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。函数原型如下:
SemaphoreHandle_t xSemaphoreCreateBinary( void )
返回值:NULL: 二值信号量创建失败。其他值: 创建成功的二值信号量的句柄。
2、函数 xSemaphoreCreateBinaryStatic()
此函数也是创建二值信号量的,只不过使用此函数创建二值信号量的话信号量所需要的RAM 需要由用户来分配,函数原型如下:
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
参数:pxSemaphoreBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 二值信号量创建失败。其他值: 创建成功的二值信号量句柄。
1.2、释放信号量
释放信号量分为任务级和中断级。不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数释放信号量。
1、 函数 xSemaphoreGive()
此函数用于释放二值信号量、计数型信号量或互斥信号量, 函数原型如下:
BaseType_t xSemaphoreGive( xSemaphore )
参数:xSemaphore: 要释放的信号量句柄。
返回值:pdPASS: 释放信号量成功。errQUEUE_FULL: 释放信号量失败。
2、函数 xSemaphoreGiveFromISR()
此函数用于在中断中释放信号量, 此函数只能用来释放二值信号量和计数型信号量,绝对不能用来在中断服务函数中释放互斥信号量。此函数原型如下:
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
参数:xSemaphore: 要释放的信号量句柄。pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS: 释放信号量成功。errQUEUE_FULL: 释放信号量失败。
1.3、获取信号量
不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数获取信号量。
1、函数 xSemaphoreTake()
此函数用于获取二值信号量、计数型信号量或互斥信号量,函数原型如下:
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
参数:xSemaphore: 要获取的信号量句柄。xBlockTime: 阻塞时间。
返回值:pdTRUE: 获取信号量成功。pdFALSE: 超时,获取信号量失败。
2、函数 xSemaphoreTakeFromISR ()
此函数用于在中断服务函数中获取信号量, 此函数用于获取二值信号量和计数型信号量,绝 对 不 能 使 用 此 函 数 来 获 取 互 斥 信 号 量 。此函数原型如下:
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
参数:xSemaphore: 要获取的信号量句柄。pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:pdPASS: 获取信号量成功。pdFALSE: 获取信号量失败。
二、计数型信号量
计数型信号量叫做数值信号量
2.1、创建计数型信号量
1、函数 xSemaphoreCreateCounting()
此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount )
参数:uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。uxInitialCount: 计数信号量初始值。
返回值:NULL: 计数型信号量创建失败。其他值: 计数型信号量创建成功,返回计数型信号量句柄。
2、函数 xSemaphoreCreateCountingStatic()
此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存需要由用户分配。函数原型如下:
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t * pxSemaphoreBuffer )
参数:uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。uxInitialCount: 计数信号量初始值。pxSemaphoreBuffer: 指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 计数型信号量创建失败。其他值: 计数型号量创建成功,返回计数型信号量句柄。
2.2、释放和获取计数信号量
计数型信号量的释放和获取与二值信号量相同。
三、互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量, 在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。 互斥信号量适合用于那些需要互斥访问的应用中。
互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。 当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级, 这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。
优先级继承并不能完全的消除优先级翻转, 它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:
1、互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
2、中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
3.1、创建互斥信号量
1、函数 xSemaphoreCreateMutex()
此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数原型如下:
SemaphoreHandle_t xSemaphoreCreateMutex( void )
返回值:NULL: 互斥信号量创建失败。其他值: 创建成功的互斥信号量的句柄。
2、函数 xSemaphoreCreateMutexStatic()
此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的RAM 需要由用户来分配,函数原型如下:
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
参数:pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:NULL: 互斥信号量创建失败。其他值: 创建成功的互斥信号量的句柄。
3.2、释放互斥信号量
释放互斥信号量的时候和二值信号量 、计数型信号量一样 、都是用的函数xSemaphoreGive()
3.3、获取互斥信号量
获取互斥信号量的函数同获取二值信号量和计数型信号量的函数 相同 ,都是xSemaphoreTake()